Wednesday, October 28, 2009

What did I learn today - Invoking help from Welcome Screen in Eclipse RCP

I wanted to know how to launch help window from the welcome screen. From the welcome screen, either we could link to other html pages or we could link to help content. Since the help content topics are already in help, it makes more sense not to duplicate it, but to link to it. Basically, when I click on "How to do feature X?" link in welcome screen, I want my Help window to open up and go to that topic directly. Very similar to context sensitive help, but from welcome screen.

After some fight, I got it working. If you want to link from the intro content xml file, you can add this within the page.

<page id="root" style="content/root.css" style-id="page">
<group id="Introduction">
<link label="Getting started" url="http://org.eclipse.ui.intro/showHelpTopic?id=/my.test.plugin.help/html/intro1.html" id="intro1">
</link>
</group>
</page>

Or, if you need to launch help topic from within the html or xhtml welcome screen pages, you could use this:

<body>
<div id="header">
<h1>My Application</h1>
</div>
<div id="content">
<li>
<a href="http://org.eclipse.ui.intro/showHelpTopic?id=/my.test.plugin.help/html/intro1.html" id="intro1">
Getting started with my application.
</a>
</li>
</div>
</body>

Here my.test.plugin.help is the id of my help plugin.


Monday, October 19, 2009

What did I learn today - Cross plug-in help in Eclipse plugins

Each plugin in Eclipse typically has it's own corresponding help plugin. Sometimes, it is necessary to link help topic in one plugin from a help topic in another plugin. I was not sure how to do that. Found that we can do it by having the link in the following form:

<a href="PLUGINS_ROOT/com.mycompany.mypluginid/html/mytopic.html">My Topic</a>

Here, com.mycompany.mypluginid is the id of the target plugin where mytopic.html lives.

Note: Before Eclipse 3.2, the cross plugin help link used to be referenced like "../com.mycompany.mypluginid/html/mytopic.html". It is recommended not to use it anymore.

Sunday, October 18, 2009

What did I learn today - Eclipse IDE Java Editor Save Actions (Automatic code formatting/clean-up)

Formatter/Code Clean-up:

In our organization, we have our builds set up so that they fail even if the source has one PMD/Checkstyle error. Until recently, I'm used to running formatter, clean-up and removing unused import options (available via right click context menu) before committing my source code, so that I don't break the build. You could also have clean-up do formatting and remove unused imports as part of clean-up. If you don't know what I'm talking about, look at formatter and clean-up options under Java->Code Style preferences.

How to automate?

While this is good, I recently found that I can automate this. Go to Window->Preferences->Java->Editor->Save Actions preferences page. Here there are options so that formatter, clean up and removing unused imports all run automatically whenever the source code is saved.

Available Options:

In save actions preferences page, Check "Perform the selected actions on save" to enable the other options below. Check "Format source code" and make sure to select "Format edited lines" option so that only the lines you edit are formatted. This would avoid formatter changing every line in the file. Check on "Organize imports" and also check on "Additional Actions". The actions here are (almost) same as the clean-up options available under Java->Code Style->Clean Up preferences. Click on configure button in "Additional Actions" and make sure that they both have the same configuration. Only thing missing here (which would have been really nice!) is that the additional actions does not automatically point to the clean up preferences defined already and so you have to define it again here. Also, you cannot use a clean-up profile here either. But, still the automatic save actions really save me time and makes sure that my code is always clean. Finally, the main thing is, I break our builds much fewer now!


Saturday, October 17, 2009

What did I learn today - Eclipse PluginSpy

Eclipse PluginSpy has been there for sometime now. Select any view or editor and do ALT+SHIFT+F1. This will open the plugin-spy popup window and shows information about the view/editor selected. It shows information about Active Shell, Active Part, Active Selection and Active Help. Try it if you did not know about this feature.

What's new in Eclipse 3.5 is, PluginSpy now works for toolbar and menu items also. Do ALT+SHIFT+F2 and move the mouse over toolbar or menu item. You will see the cursor change to a book like image. Now, click the toolbar or menu item and you will see that plugin-spy popup window come up for that also. This shows Active Selection and Contributing Plugin Information for the selected toolbar or menu item.

I find it very useful. I'm sure every Eclipse Plugin Developer will appreciate this great feature.

Wish everyone a very Happy Diwali!

Saturday, October 10, 2009

Eclipse RCP - Saving Editor State and Layout

Introduction:

In the previous article, we saw how to save view layout and state. In this article, we will see how to save editor layout and state.

Saving Editor State and Layout:

Saving editor state is slightly more complicated than saving the view state. Below are the steps which needs to be done to save the editor state.

1) First of all, make sure that initialize method in ApplicationWorkbenchAdvisor has save and restore enabled. Add the following line to the initialize method for this: configurer.setSaveAndRestore(true);

2) The editor input needs to implement getPersistable() method and should return an IPersistableElement. Mostly, my editor input itself implements IPersistableElement and so getPersistable() would return this. IPersistableElement is responsible for saving the current state of the element and also tells the id of the factory which is responsible for reconstructing the editor from current state of the application. The methods (listed in points 4 and 5 below) need to be implemented in IPersistableElement.

3) The editor input needs to implement exists() method and make sure that it returns true.

public boolean exists() {
return true;
}

4) Implement saveState method in IPersistableElement - public void saveState(final IMemento memento) {}

This method is responsible to save the current editor state into the memento. See the previous article on how saveState method works for views and it behaves exactly the same for editors too. Make sure to save all the information in memento, which are needed to reconstruct the editor later.

5) Implement getFactoryId method in IPersistableElement - public String getFactoryId() {}

This method should return the id of your IElementFactory which is responsible for re-creating the editor from the saved memento. We will see more about IElementFactory below in 6 and 7.

6) Open plugin.xml and go to extensions tab. Add a new extension point org.eclipse.ui.elementFactories and add a new factory for your editor. Give it an Id (this is the ID which needs to be returned in getFactoryId method in IPersistable element - see point 5 above) and class (the class name which implements IElementFactory).

7) Implement your IElementFactory class. It needs to implement public IAdaptable createElement(final IMemento memento) {} method. This method is passed the IMemento which is saved when application was closed last time. createElement method need to re-create the editor and need to return IAdaptable. IEditorInput is an instanceof IAdaptable. So, for editors, you would typically get info from IMemento, create your IEditorInput (using info from IMemento) and return that.

This is all you need to do to save your editor state and layout. Eclipse takes care of remembering the layout of the editors. Main thing here is, we need to make sure that we persist all state information (in saveState method - point 4) and be able to recreate the editor in exactly the same state (in createElement method - point 7).

How this works?

Behind the scene, whenever the application is closed, getPersistable method for all the open editors is called. If it is not null, then the IPersistableElement's saveState method is called and all the state information is saved into the memento. Also, getFactoryId is called so that eclipse can know which class is responsible for recreating this editor later. This information is saved in <youreclipseworkspacedir>\.metadata\.plugins\org.eclipse.ui.workbench\workbench.xml file. Take a look at this file and you will see that the information saved in memento and also factory information is persisted there.

Note that if getPersistable() returns null, then the state of the editor will not be saved. You can use this in cases where you don't want to save the editor state. For example, if user switches to a different workspace or to a completely different environment, you don't want to save the editor state because they are no longer applicable in the new workspace/environment. In these cases, you could just return null in getPeristable() method.

When application is re-openened, platform reads the workbench.xml file, creates IElementFactory instance and passes it the proper memento. Then, the IElementFactory knows how to recreate the editor from the saved information.

Conclusion:

In this article, we saw how to save editor state and layout so that the application can re-open in exactly the same state as it was closed last time.

Sunday, October 4, 2009

Eclipse RCP - How to save view layouts and state?

Introduction:

Saving the state of the application and reopening the application in the same state as previously opened vastly improves user experience. This article explains how to save the layout and state of the views in your application.

Saving Views and Layout:

First of all, you need to enable saving and restoring application state at workbench level. Make sure you override initialize method in your ApplicationWorkbenchAdvisor and add configurer.setSaveAndRestore(true);

@Override
public void initialize(final IWorkbenchConfigurer configurer) {
super.initialize(configurer);
configurer.setSaveAndRestore(true);
}

Adding this automatically saves opens views along with their layouts and makes sure they are reopened appropriately in the same layout. Isn't that amazing? Almost half of our work is done just by adding the above code block to ApplicationWorkbenchAdvisor. The remaining thing is saving the internal view state (i.e saving details of inside the view) .

Saving View State:

Let's say you have a navigator view in your perspective which displays a tree. By adding the above code block, the navigator view would be reopened in the same spot when application reopens again. But, the internals of navigator view would be lost. For example, if the tree had few selections and few nodes opened, they would be lost. Below, I'll explain how to preserve tree selections in your view.

1) Override saveState method (from ViewPart) in your view.

@Override
public void saveState(final IMemento memento) {
IStructuredSelection sel = (IStructuredSelection)this.treeViewer.getSelection();
if (sel.isEmpty()) {
return;
}
memento = memento.createChild("tree-selections");
Iterator iter = sel.iterator();
while (iter.hasNext()) {
String nodeName = iter.next();
memento.createChild("selected-nodes", nodeName);
}
}

IMemento provides way for user to store the application state in XML. User does not have to deal with XML directly and IMemento API takes care of it. When application is closed, if a view is open, it's saveState method is called before it is closed. This way, internal state of the view can be persisted before it is closed.

Restoring View State:

If you want to restore your view state (from the saved memento), you need to override the init method with memento i.e override this method

public void init(IViewSite site, IMemento memento) throws PartInitException {}

instead of

public void init(IViewSite site) throws PartInitException {}

Then, you can read the contents of the memento and restore view's internal state.

// Stores the memento so that it can be used after view is created

@Override
public void init(final IViewSite site, final IMemento memento) throws PartInitException {
init(site);
this.memento = memento;
}

@Override
public void createPartControl(Composite parent) {
// Create view here - For example, create tree viewer here

restoreState();
}

// Method which reads memento and sets selections on the tree
private void restoreState() {
IMemento selectionsMomento = this.memento.getChild("tree-selections");
if (selectionsMomento != null) {
IMemento selectedNodes[] = selectionsMomento.getChildren("selected-nodes");
if (selectedNodes.length > 0) {
ArrayList selections = new ArrayList(selectedNodes.length);
for (int i = 0; i<selectedNodes.length; i++) {
String id = selectedNodes[i].getID();
if (id != null) {
selections.add(id);
}
}

this.treeViewer.setSelection(new StructuredSelection(selections));
}
}
}

The contents of the view is created in createPartControl. Once the contents are created, it's state is set by using the restoreState method, which uses memento to get the internal state of the view last time. Above, we get selected node names from the memento and set tree selection appropriately.

Conclusion:

In this article, we saw how to preserve view layout and it's internal state so that views are reopened in exactly the same way as it was closed last time. In the next one, we'll see how to preserve editor layout and it's state.


Saturday, October 3, 2009

What did I learn today - Eclipse Search

We all learn something new everyday (hopefully!). Starting today, I plan to start writing short tips or articles about what did I learn today. It helps me in remembering them and hope it helps everyone else too...

I came across this eclipse search site - http://www.cypal.in/Eclipse_Search.html (Powered by Google, ofcourse).

This site is very focused and does not clutter my search results with non-eclipse related hits as in regular google search.