Sunday, November 22, 2009

What did I learn today - Using ArrayContentProvider (Eclipse RCP - JFace)

If your table view displays a collection of objects for example, collection of Person objects, Collection of Employee info etc., instead of writing your own content provider, consider using ArrayContentProvider. This takes the viewer input (collection) and converts it to an Array.

For example, say your code does something like this:

this.viewer.setContentProvider(new EmployeeInfoContentProvider());
this.viewer.setLabelProvider(new EmployeeInfoLabelProvider());
// Assume employeeInfoSet is a Set of EmployeeInfo objects
this.viewer.setInput(this.employeeInfoSet);

EmployeeInfoContentProvider code:

public class EmployeeInfoContentProvider implements IStructuredContentProvider {

@Override
public Object[] getElements(final Object inputElement) {
if (inputElement instanceof Set) {
return ((Set) inputElement).toArray();
}

return new Object[0];
}

@Override
public void dispose() {
//Nothing to do
}

@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
//Nothing to do
}

}

In the above code, EmployeeContentProvider does nothing but translating the collection to array. In this case, you can remove EmployeeContentProvider and use ArrayContentProvider, which does the conversion for you. So, your view code would look like this:

// Replacing EmployeeInfoContentProvider with ArrayContentProvider
this.viewer.setContentProvider(new ArrayContentProvider());
this.viewer.setLabelProvider(new EmployeeInfoLabelProvider());
// Assume employeeInfoSet is a Set of EmployeeInfo objects
this.viewer.setInput(this.employeeInfoSet);

Less code is better right!

Sunday, November 8, 2009

What did I learn today - Adding key listeners to trees and tables (SWT/JFace)

I wanted my trees and tables to listen to DEL key so that when user presses it, the selected nodes in the tree or the selected cells in the table would get deleted. I was looking for the method to add key listener to the table/tree viewer, but found that I need to add it to the viewer control.

Below is the code to add listener to the delete key on your tree viewer.

this.treeViewer.getControl().addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
if (e.keyCode == SWT.DEL) {
IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection();
if (selection.isEmpty()) {
return;
}

// Business logic to handle deletion - remove from model, view etc.,
handleDelete(selections);
}
}
});

You can do it similarly for other viewers (table viewer etc.,) or to listen to other keys also.

If you are working directly with SWT, you will add listener directly to the tree/table (i.e the control itself) like tree.addKeyListener(), table.addKeyListener() etc., You can add this to any control in SWT.

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.