Dimitry Karpenko
Java/Eclipse developer in MyEclipse and Webclipse teams.
Java/Eclipse developer in MyEclipse and Webclipse teams.
In previous articles, I’ve discussed how to create custom XML bindings and custom editing UI for an editor based on the great Eclipse Sapphire framework. Now let’s take a look at another aspect of editor UI creation, customizing standard actions like Add or Browse, or creating completely custom actions for your Sapphire-based editor.
As in previous articles, it is good to know basic Eclipse plug-in development and Sapphire framework principles.
In this example, Sapphire 9 is used. To download it, go to https://eclipse.org/sapphire/releases/9/.
For Sapphire documentation, refer to https://eclipse.org/sapphire/releases/9/documentation/index.html.
Also, this article doesn’t completely describe action or browsing customization API supported by Sapphire, for more information refer to the following:
https://eclipse.org/sapphire/releases/9.0.5/documentation/actions/index.html
https://eclipse.org/sapphire/releases/9.0.5/documentation/focus/browsing/index.html
Let’s create an editor for plugin_info.xml—syntethic sample file, which contains the project id from the workspace and the list of plugins on which this project depends. You need to create an Eclipse plugin project making UI contributions and put standard Sapphire editor infrastructure inside it. Please refer to the sample below or Sapphire help for details.
There are only 2 model classes in our sample project—IPluginsInfo
is a root model class containing ProjectName
string value property for the project name and Plugins
list property containing the plugins list. Each element of that list is an instance of IPluginDescr
model class, which only contains a single property, Name.
So, let’s start and create an editor for the project name property. For user convenience, let’s add the ability to select a project from the workspace. To do this, we’ll need to specify a custom Browse action handler in the sdef file.
<property-editor> <property>ProjectName</property> <action-handler> <action>Sapphire.Browse</action> <impl>SelectProjectActionHandler</impl> </action-handler> </property-editor>
Handler implementation is extending SapphireActionHandler
and has only one method, run
, which is pretty straightforward.
protected Object run(Presentation context) { try { SwtPresentation presentation = (SwtPresentation) context; IJavaModel model = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()); //Create JDT model for the workspace Set<IJavaProject> projects = Arrays.stream(model.getJavaProjects()).collect(Collectors.toSet()); //Get list of available projects ProjectSelectionDialog dialog = new ProjectSelectionDialog(presentation.shell(), projects); //Create project selection dialog dialog.setTitle("Select project"); dialog.setTitle("Select project from the workspace"); if (dialog.open() == Window.OK) { Property property = context.part().getModelElement().property("ProjectName"); //Get Property to store selected value String result = ((IJavaElement) dialog.getFirstResult()).getElementName(); //Get selected project name ((Value<String>) property).write(result,true); //Write value to property } } catch (JavaModelException e) { CustomActionsActivator.log(e); } return null; }
Now let’s say we want to create an action which isn’t overriding any standard action, but is completely custom, launching some code on a button click. Custom actions can be added to several different areas of the UI, including the editor page itself, section, etc. Let’s add one more action button to the right of our editing field, which would allow us to launch the import wizard and import a project into the workspace.
Now we’ll need to add two elements into our <property-editor>
tag.
<action> <id>ImportProject</id> <label>Import Project</label> <image>icons/import_wiz.gif</image> <key-binding-behavior>local</key-binding-behavior> </action> <action-handler> <action>ImportProject</action> <impl>ImportProjectActionHandler</impl> </action-handler>
In <action>
tag, action’s id, textual label and image are specified. In <action-handler>
tag, we specify a particular action handler for this action. Handler class is like the previous one, containing overridden run
method and one helper method responsible for showing a wizard using its id.
protected Object run(Presentation context) { SwtPresentation presentation = (SwtPresentation) context; String name = openWizard("org.eclipse.ui.wizards.import.ExternalProject", presentation.shell()); if (name != null) { //If user successfully imported some project - set it's name to ProjectName field Property property = context.part().getModelElement().property("ProjectName"); //Get property to put value to ((Value<String>) property).write(name,true); //Write property value } return null; } private String openWizard(String id, Shell parentShell) { IWizardDescriptor descriptor = PlatformUI.getWorkbench().getImportWizardRegistry() .findWizard(id); try { if (descriptor != null) { // Then if we have a wizard, open it. IWizard wizard = descriptor.createWizard(); WizardDialog wd = new WizardDialog(parentShell, wizard); //Create wizard dialog for wizard if (wd.open() != Window.CANCEL) { WizardProjectsImportPage page = (WizardProjectsImportPage) wizard.getPage("wizardExternalProjectsPage"); //Get wizard page ref ProjectRecord[] projectRecords = page.getProjectRecords(); //Get selected projects if (projectRecords.length > 0) { //If there are some projects se;ected, return a name of the first one return projectRecords[0].getProjectName(); } } } } catch (CoreException e) { e.printStackTrace(); } return null; }
Let’s see how our editor looks after all our customizations:
We can use the same action handler in several places across the editor with little customizations, like different title, initial selections, etc. In these cases, handler can easily be parametrized. Let’s customize dialog title for our project selection dialog—add a param
to our sdef file.
<action-handler> <action>Sapphire.Browse</action> <impl>SelectProjectActionHandler</impl> <param> <name>dialog-title</name> <value>Choose project</value> </param> </action-handler>
To load given param, add init
method to your handler.
@Override public void init(SapphireAction action, ActionHandlerDef def) { super.init(action, def); dialogTitle = def.getParam("dialog-title"); }
dialogTitle
here is a field, which is set to dialog when creating it. See attached source for more details.
Another customization option is obtaining value from some annotation used with model property. For this, we’ll need to have a simple annotation containing only a string value—DialogTitle
, and add it to the model’s ProjectName
property:
// *** Project name *** @XmlBinding(path = "@projectName") @Label(standard = "Project name") @Required @DialogTitle("Choose project") ValueProperty PROP_PROJECT_NAME = new ValueProperty(TYPE, "ProjectName"); //$NON-NLS-1$
Also, let’s slightly modify init
method to obtain given annotation from the model.
PropertyEditorPart propDef = getPart().nearest(PropertyEditorPart.class); //Get property editor part DialogTitle annotation = propDef.property().definition().getAnnotation(DialogTitle.class); //Get property definition for it and obtain necessary annotation if (annotation != null) { dialogTitle = annotation.value(); }
As you can see, using nearest(), property(), definition(), etc. you can walk through Sapphire data and UI models to obtain different settings specified in model or sdef.
Let’s see, how our dialog would look after one of these customizations:
But what if we want to customize a bit more complex stuff, like adding a new item to the list of items? This is also possible, however it would require a bit more effort. For the list property editor, we’ll need to specify Action Handler Factory, which would be responsible for creating a custom action handler for the action we want to override. So, our plugin list property editor in sdef file should look like following:
<property-editor> <property>Plugins</property> <child-property>Name</child-property> <show-label>false</show-label> <action-handler-factory> <context>Sapphire.ListPropertyEditor</context> <action>Sapphire.Add</action> <impl>com.genuitec.sapphire.customactions.actions.CustomAddActionHandlerFactory</impl> </action-handler-factory> </property-editor>
CustomAddActionHandlerFactory
is very simple, and just returns a list containing custom handlers—only one handler in our case:
public List<SapphireActionHandler> create() { return Collections.singletonList(new SelectPluginActionHandler()); }
Handler itself is opening a plugin selection dialog with the ability to select a single plugin, and, if selected, add a new item to the list property containing the selected plugin’s id:
@Override protected Object run(Presentation context) { SwtPresentation presentation = (SwtPresentation) context; PluginSelectionDialog dialog = new PluginSelectionDialog(presentation.shell(), false,false); //Create PDE's plugin selection dialog if (dialog.open() == Window.OK) { //If user selected smth... Property property = context.part().getModelElement().property("Plugins"); ElementList<IPluginDescr> listProp = (ElementList<IPluginDescr>) property; //Get list property, to which we want to add IPluginDescr descr = listProp.insert(); //Create new element for it Property nameProp = descr.property("Name"); //Get Name prop for this element... ((Value<String>) nameProp).write(dialog.getFirstResult().toString(),true); //...and specify it's value } return null; } @Override public void init(SapphireAction action, ActionHandlerDef def) { setId("AddSelectPlugin"); //Set action id super.init(action, def); setLabel("plugin"); //Set label, for hint "Add plugin" to be displayed }
Let’s launch our editor and see, whether it works:
Hmm… Why after clicking “+” do we see two items instead of our custom dialog? That’s because we haven’t actually overridden standard add action handler, and a shortcut for it is also being added to “+” action. To get rid of it, we’ll need to add Action Handler Filter to our list property editor.
To do this, let’s add the following under </action-handler-factory>
tag in sdef:
<action-handler-filter> <impl>com.genuitec.sapphire.customactions.actions.CustomAddActionHandlerFilter</impl> </action-handler-filter>
Filter class extends SapphireActionHandlerFilter
and contains one small method checking handler’s id to cut off unnecessary handlers:
@Override public boolean check(SapphireActionHandler handler) { return !handler.getId().startsWith("Sapphire.Add."); }
Now, let’s try it…works like a charm!
Can we add a fully custom action to the list property editor? Sure, the way of doing it is quite the same as for the value property editor. Let’s make an action, adding several plugins at once to our list. Add the following inside the <property-editor>
tag for Plugins property:
<action> <id>AddMultiple</id> <label>Add plugins</label> <tooltip>Add several plugins</tooltip> <image>icons/multi_plugins.gif</image> </action> <action-handler> <action>AddMultiple</action> <id>AddMultiple.Handler</id> <impl>com.genuitec.sapphire.customactions.actions.SelectMultiplePluginsActionHandler</impl> </action-handler>
The handler class for it is pretty much like the handler for adding a single plugin name, except configuring the dialog for selecting multiple plugins, and adding selected plugin id’s to a list inside a loop.
Let’s try it:
Works perfectly. So, finally our editor would look like the following:
sapphire_customactions_tutorial—Sample project
Coding Tip: Using Custom Bindings for Property Editing in Sapphire
Creating Custom Editors in Sapphire
Creating a Custom Eclipse Sapphire Editor to Edit Two XML Files Simultaneously
If you have any comments or questions, we would love to hear from you @MyEclipseIDE on twitter or via the MyEclipse forum. Happy coding!
If you’re not already subscribing to our blogs, why not do it today? Subscribed