Creating a "live" view in the background - eclipse

I'd like to define an Eclipse view that is self-contained, but able to respond to selection events as soon as the Workbench app is launched, even if the view is not visible, as long as it appears as one of the "background views" in a folder. In other words, I need a "hook" which is invoked when placeholder for my view is created.
The problem is that when a Workbench app launches, the background views are normally not instantiated. You can of course implement an IPerspectiveFactory to do an initial perspective layout in Eclipse, but the factory is only used when the perspective is initially chosen; after that, on subsequent launches, Eclipse persists mementos that don't involve creating the view.
Is there any way to accomplish this?

I think it would be a good idea to separate the data model that's driving your view from the view UI. This will allow the data model to be continuously updated and the view to be added or removed without having to maintain any kind of state.
You can add the data model as a SelectionService listener in your plugin Activator:
private ISelectionListener listener;
public void start(BundleContext context) throws Exception {
super.start(context);
listener = new ISelectionListener() {
#Override
public void selectionChanged(IWorkbenchPart part,
ISelection selection) {
// Update model
}
};
PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getSelectionService()
.addSelectionListener(listener);
}
public void stop(BundleContext context) throws Exception {
PlatformUI.getWorkbench().getActiveWorkbenchWindow()
.getSelectionService().removeSelectionListener(listener);
super.stop(context);
}
When the view is created it can use the data model to populate the UI components and listen to the data model for any live updates.
You'll need to make sure that your plugin is activated when the application is launched. The following question can help with that.
auto-start OSGi service
Edit
The following code can be used to activate your view (calling createPartControl) even if the view is currently hidden. By placing this code in your activator it can start listening to model events as soon as your plugin is activated.
PlatformUI.getWorkbench().addWindowListener(new IWindowListener() {
...
#Override
public void windowActivated(IWorkbenchWindow window) {
IViewReference view = window.getActivePage().findViewReference(
VIEW_ID);
if (view != null) {
view.getPart(true);
}
}
});

Related

Eclipse RAP multi-window/tab

I would like to have a multi-tab/windowed Eclipse RAP application.
I am able to open a second window using
UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
launcher.openURL("/gasf?foo=other_perspective");
Where I use the foo paramter to select the perspetive I want. However using this method will create a speparate http session, thus the various listeners and so on won't communicate with my first window.
I also tried opening a second window/page using
PlatformUI.getWorkbench().getActiveWorkbenchWindow().openPage("other_perspective" , null);
But this merely changes the current window perspective but does not open a second window or tab in my browser.
Has anyone achieved a multi-tab RAP application with working selectionlisteners between the tabs?
Thanks for any help you can provide
EDIT:
THANKS a lot ralfstx, as you pointed out, I can share the listeners or anything using the shared HTTP session, so far so good. Now the next step is to be able to update a tab based on an external event.
To try my idea of refresh from another tab, I did a dummy timer that does something 2 seconds later (i.e. simulate something triggered from another tab) with:
final ServerPushSession pushSession = new ServerPushSession();
pushSession.start();
Display display = Display.getDefault();
NavigationView navigationView = ((NavigationView) window.getActivePage().findView(NavigationView.ID));
timer.schedule(new TimerTask() {
#Override
public void run() {
display.asyncExec(new Runnable() {
public void run() {
navigationView.doSomething();
}
});
}
}, 2000);
This works! The pushSession.start() forces the UI to refresh without any user interaction. So now the action doSomething() is executed on the navigationView as soon as the 2 seconds are reached.
My only remaining concern is how much load this puts on the server, but its a reasonable solution at least. I validated your answer.
EDIT2:
Just to be complete, to make sure not bump in an invalid Thread access error since we are updating a display from another display, in the doSomething() method we must execute actions using display.asyncExec:
Display display = Display.getCurrent();
public void doSomething() {
display.asyncExec(new Runnable() {
public void run() {
treeViewer.refresh();
}
});
}
With the current architecture of RAP, you can't spread workbench windows over different browser tabs. Every new browser starts a new UISession which implies another Display (see Scopes in RAP).
However, the HttpSession should be the same (unless you have cookies turned off), so you could use this as a means of communicating between different browser tabs.

How to get/inject EPartService outside part or in LifeCycle Manager or How to control life cycle of e4 RCP app in true sense?

My application consists of many parts and they are defined in application's e4xmi file. I want to hide and show them dynamically. I am using EpartService to do so in handlers, where I can inject it.
But I also want to control the show/hide of parts with something like life cycle manager, where I can not inject EPartService. Is there any way to achieve and fully control RCP application's life cycle?
There seems the exact same question here and void of solution:
https://www.eclipse.org/forums/index.php/t/595958/
I want to implement 'remember me like feature' where part having sign in screen is shown instead of other parts. Also after log out same sign-in part is to be shown. So I need to control life cycle of RCP app. But I cant inject EPartService before anything in Application's e4xmi is initiated.
If you are creating a class from something which is injected (such as the LifeCycle class) you can create your class with injection using ContextInjectionFactory:
#Inject
IEclipseContext context;
MyClass myClass = ContextInjectionFactory.make(MyClass.class, context);
Or if you just pass an IEclipseContext to the class you can get the part service using:
EPartService partService = context.get(EPartService.class);
Note: There is a separate instance of the part service for each part. Depending on what you are doing you may need to make sure you have the service for the active part.
If you are not locked in to using SWT, you could use the e(fx)clipse e4 renderer for JavaFX instead.
e(fx)clipse has more possibilities to control the lifecycle of the application. For example you can return a Boolean from #PostContextCreate to signal whether you want to continue the startup or not. You will not be able to use EPartService here though, but you can roll your own login dialog using dependency injection as greg-449 has described it in his answer.
public class StartupHook {
#PostContextCreate
public Boolean startUp(IEclipseContext context) {
// show your login dialog
LoginManager loginManager = ContextInjectionFactory.make(LoginManager.class, context);
if(!loginManager.askUserToLogin()) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}
(You can also restart the application. Form
more details see http://tomsondev.bestsolution.at/2014/11/03/efxclipse-1-1-new-features-api-to-restart-your-e4-app-on-startup/).

Is this an acceptable GWT solution

I want to create a GWT UI where I basically will have a single HTML page that loads a PanelA object. The user will then do their thing and eventually perform an action that will move them onto another view/screen.
I have simplified my existing views to contain just a single button which moves the user onto the next page etc. for simplicity I only have 2 views to start.
Here is my start up entry point.
public class StockWatcher implements EntryPoint
{
public void onModuleLoad()
{
final RootPanel rootPanel = RootPanel.get();
rootPanel.add( PanelA.getInstance() );
}
}
Here is the PanelA class
public class PanelA extends HTMLPanel
{
private static PanelA panel;
private PanelA()
{
super("Panel A");
final RootPanel rootPanel = RootPanel.get();
Button btnNewButton = new Button("Go to panel B");
btnNewButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event)
{
rootPanel.clear();
rootPanel.add( PanelB.getInstance() );
}
});
add(btnNewButton);
}
public static PanelA getInstance()
{
if (panel == null)
{
panel = new PanelA();
}
return panel;
}
}
My other PanelB class is pretty much the same as PanelA , ie button that brings me back to PanelA
My UI works as desired.
My Question is, Is this singleton type pattern a correct or proper way to do this? ie Have a stack of Singleton UI views that get popped on/off the main panel?
Also, what is the best way to handle hitory/breadcrumb trace through a GWT app, ie allowing a user to go back to the previous screen bearing in mind that they may navigate to PanelX from either of PanelA, PanelB or PanelC
I use "Activities and Places" to manage all of this, and it's been working quite well in production for a year or so.
https://developers.google.com/web-toolkit/doc/latest/DevGuideMvpActivitiesAndPlaces
I think it's fine to use a Singleton mechanism for your views, but you have to make sure to completely reset any state you store. For me, it was easier to just create new views every time the user navigates to a new spot, and then if I detected a problem with load times or something to retroactively cause the view to re-use its components. I'd advise you to get the navigation working first, and then worry about the singleton (or not) optimizations.
I recommend Activities and Places design pattern.
It covers all the issues you raise in your question, plus many more that you have not thought of (or did not ask about) yet, like native browser history management and ability to bookmark different places within the app, handling of page reloads, efficient memory management vs optimized DOM operations, scalability (building an app with dozens/hundreds of views with minimal code duplication), testability, and code-splitting (where to split large apps).
I suggest you refer "GWTP" framework to make a GWT Project, some of the features currently supported by GWTP:
Dependency injection through GIN and Guice;
Simple but powerful history management mechanism;
Support for nested presenters;
Lazy instantiation for presenter and view;
Effortless and efficient code splitting;
Integrated command pattern supporting undo/redo;
So your questions and query like Singleton UI views, best way to handle hitory/breadcrumb trace and singleton type pattern , will cover and one good framework will make easy project management.

Eclipse: Converting actions in commands for a specific view

My original RCP was started in 3.x and currently I am running it on Juno with the compatibility layer. I was looking into doing a soft migration so I have started to slowly change my practices. One of the things I am doing is to change my actions into commands.
I have a view (which is like a directory explorer) currently that adds actions to the toolbar and popup menu of the view. These actions call specific methods in the view, for example to go up one directory.
It was easy to do this by action because I just create my action in the View class itself and programmatically add them to the toolbar
IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager();
mgr.add(upDirectory);
mgr.add(refresh);
mgr.add(changeRoot);
and the creation of the actions are called from the createPartControl()
upDirectory = new Action("Go up one directory") {
public void run() {
goUpOneDirectory();
}
};
where goUpOneDirectory() is a method in the view
If I want to convert this to a command, I want to be able to access this method of the view in my handler. So I tried the following,
private void createHandlers()
{
IHandlerService handlerService = (IHandlerService) getSite().getService(IHandlerService.class);
IHandler upDirHandler = new AbstractHandler() {
public Object execute(ExecutionEvent event)
throws ExecutionException {
goUpOneDirectory();
return null;
}
};
handlerService.activateHandler("updir.id", upDirHandler);
}
And createHandlers is called in the createPartControl, and the command is added via the plugin.xml to the toolbar of the view. The problem is that the moment my view is out of focus it disables the buttons in the toolbar for these commands.
I want them to remain enabled at all times. How can I do that?
I know that the isEnabled() returns true all the time so I am not sure why it happens. The activateHanlder is called once in createPartControl so I feel that it should remain active all the time.
Edit: Ok I just saw this ,
IHandlerService from the workbench part site is the part handler
service. Any handlers activated through the part handlers service will
only be active when that part is active. Any listeners added to the
part handler service will be removed when the part is disposed, and
any active handlers will be deactivated (but not disposed).
So how can I get this,
IHandlerService from the workbench is the global handler service. it
provides no special activation scoping or lifecycle.
Sorry, I should have waited a bit longer before asking, I figured it out!
I changed the,
IHandlerService handlerService = (IHandlerService) getSite().getService(IHandlerService.class);
to
IHandlerService handlerService = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
and it worked.
I will leave the question in case it helps other people.

Key and Mouse Listener for CEditor in Eclipse

I need to add a listener to one of my plugin views. I need the view to update when a mouse or key event occurs in the CEditor in Eclipse. I have done it by:
((StyledText)((AbstractTextEditor)activeEditor).getAdapter(Control.class)).addKeyListener(l);
But when I change to a different file in the editor the listeners do not work. Any ideas?
Solved: I scrapped the idea of key/mouse listeners and just added a SelectionListener to my Eclipse page. Then created a custom selectionListener that only responded when the selection was in the CEditor.
Made my view class implement ISelectionListener
getViewSite().getPage().addSelectionListener(this);
#Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if(part.getClass() == CEditor.class){
//Do what I needed
}
}
This made it so it only responded to selection changes in my CEditor.