Eclipse RCP menus & actions: Configure or code? - eclipse-rcp

This is a general question but my current problem revolves around menu handling.
In a normal plugin with contributes menu actions you would configure ActionSets etc in the plugin.xml configuration. This is obviously sensible.
I am working on a RCP application (actually RAP) and I'm wondering if it's worth the effort to configure everything via plugin.xml. My plugin does not have to interact with an other unknown plugins so, theoretically, I have control. I can add menus and actions programmatically.
I have been trying to configure a menu which contains a submenu. I have tried defining ActionSets and linking one inside the other but without success. Some items need to be disabled depending on the user role.
I figure I could have coded the whole lot in a few minutes but I'm not sure if that fits with the eclipse 'ethos'.
What opinions are out there? The application will get fairly big so I'd like to get the approach right from the start. Perhaps someone can point me at an example for configuring a nested menu :-)

My opinion is that the plugin.xml implementation is the way to go.
My main two reasons for using this method:
It's really easy to reconfigure and reorganize the menus and buttons without writing java code.
Very clear hierarchical visualization of the menu trees.
Here is a code snippet that implements menus and submenus. In this example, they are added to the main menu.
You can paste this into your plugin.xml:
<extension
name="Main Menu Contributions"
point="org.eclipse.ui.menus">
<menuContribution
allPopups="false"
locationURI="menu:org.eclipse.ui.main.menu">
<menu
id="fileMenu"
label="File">
<command
commandId="org.eclipse.ui.file.exit"
label="Exit"
style="push">
</command>
</menu>
<menu
label="Edit">
<command
commandId="org.eclipse.ui.edit.selectAll"
label="Select All"
style="push">
</command>
<menu
label="Submenu">
<command
commandId="org.eclipse.ui.edit.selectAll"
label="Select All Submenu"
style="push">
</command>
<command
commandId="org.eclipse.ui.edit.delete"
label="Delete submenu"
style="push">
</command>
</menu>
</menu>
</menuContribution>
</extension>
For activating/deactivating a menu, you have to use Core Expressions to enable/disable command handlers. If a command doesn't have any active handlers attached, it will be disabled. So, the menu item that calls that command will also be disabled.
The following code snippets show how to create a button on the toolbar of a view and have it be enabled/disabled depending of a variable's value. Bare in mind that you will have to change some things in this code to make it work. Most of the changes are for reference names and class implementation.
Create the button in the toolbar (plugin.xml):
<extension
name="View Toolbar Contributions"
point="org.eclipse.ui.menus">
<menuContribution
allPopups="false"
locationURI="toolbar:myapp.views.MyView">
<command
commandId="myapp.commands.PauseSound"
icon=""
label="Pause Playback Sound"
style="push"
tooltip="Pause">
</command>
</menuContribution>
</extension>
Create the command (plugin.xml):
<extension
id="myapp.commands.PauseSound"
name="Pause sound command"
point="org.eclipse.ui.commands">
<command
id="myapp.commands.PauseSound"
name="Pause Sound">
</command>
</extension>
Create the command handler (plugin.xml):
<extension
point="org.eclipse.ui.handlers">
<handler
commandId="myapp.commands.PauseSound">
<activeWhen>
<with
variable="myapp.commands.sourceprovider.active">
<or>
<equals
value="PLAYING">
</equals>
<equals
value="PAUSED">
</equals>
</or>
</with>
</activeWhen>
<class
class="myapp.rcp.commands.toolbar.PausePlayback">
</class>
</handler>
</extension>
Create the state variable for the command (plugin.xml):
<extension
point="org.eclipse.ui.services">
<sourceProvider
provider="myapp.commands.sourceprovider.CommandState">
<variable
name="myapp.commands.sourceprovider.active"
priorityLevel="workbench">
</variable>
</sourceProvider>
</extension>
Implement the class that changes the variable's state:
public class CommandState extends AbstractSourceProvider {
public final static String STATE = "myapp.commands.sourceprovider.active";
public final static String STOPPED = "STOPPED";
public final static String PLAYING = "PLAYING";
public final static String PAUSED = "PAUSED";
public final static String NOT_LOADED = "NOT_LOADED";
enum State {
NOT_LOADED, PLAYING, PAUSED, STOPPED
};
private State curState = State.NOT_LOADED;
#Override
public void dispose() {
}
#Override
public String[] getProvidedSourceNames() {
return new String[] { STATE };
}
// You cannot return NULL
#SuppressWarnings("unchecked")
#Override
public Map getCurrentState() {
Map map = new HashMap(1);
if (curState == State.PLAYING)
map.put(STATE, PLAYING);
else if (curState == State.STOPPED)
map.put(STATE, STOPPED);
else if (curState == State.PAUSED)
map.put(STATE, PAUSED);
return map;
}
public void setPlaying() {
fireSourceChanged(ISources.WORKBENCH, STATE, PLAYING);
}
public void setPaused() {
fireSourceChanged(ISources.WORKBENCH, STATE, PAUSED);
}
public void setStopped() {
fireSourceChanged(ISources.WORKBENCH, STATE, STOPPED);
}
public void setNotLoaded() {
fireSourceChanged(ISources.WORKBENCH, STATE, NOT_LOADED);
}
}
More details on how to implement these features can be found at these locations:
Eclipse Commands Tutorial
Limiting Visibility of Commands

For Eclipse there are two different ways to contributing to the Workbench: Actions and Commands.
I definitely recommend the Commands as the newer and more advanced than Actions.
The drawbacks of Actions as specified here (1):
The UI and handling are always tied. There is no way you can separate each other
While Actions can be contributed to different parts of the workbench (popup menu/tool bar), all of them were different extension points and so you end up duplicating the XML in multiple places. The worst of it is that not all the extension points expect the same configuration.
Specifying Actions in multiple places is a maintenance nightmare. If you have to change the icon of an action, you need to change in all the places.
Another issue with duplicating Actions in plugin.xml is that multiple instance of the same Actions will be created in the memory
(1) Actions vs Commands

If you are writing the RCP application, the good practice is to create the place holders in your ActionBarAdvisor. While that would define the basic structure of your menus and toolbars, you can extend menuContributions and using commands to contribute the actual menu/tool items.

For adding actions in RCP you can also use ApplicationActinBarAcvisor.It is more easy than the above mentioned solution
In this u just have to first declare the action as an object of IWorkbenchAction, then in method "protected void makeActions(IWorkbenchWindow window)" u can register it.
And the last step is to add it into menu.
Following code will help u out.
1.First declare the action :-
private IWorkbenchAction newAction
2.Registering action :-
protected void makeActions(IWorkbenchWindow window) {
newAction = ActionFactory.NEW_WIZARD_DROP_DOWN.create(window);
register(newAction);
newAction.setText("New");
3.Last step is to add the action in menu:-
MenuManager filemenu = new MenuManager("&File", "file");
filemenu.add(newAction);
You can also add the action in toolbar as follows:-
protected void fillCoolBar(ICoolBarManager coolBar) {
IToolBarManager toolbar = new ToolBarManager(coolBar.getStyle());
coolBar.add(toolbar);
toolbar.add(newAction);

Related

Eclipse RCP dynamic MenuContribution hide and unhide with CoreExpression

I'm trying currently to add a visibleWhen condition for a MenuContribution in my Eclipse RCP application. We've different versions of our app and we've differentiate them with a IClientConfiguration interface/service which works more or less as feature toggles.
Depending on witch app-$version.product is called different implementations of the configuration will part of the app.
However.. get to the current situation: We've a menu which is defined in a common fragment.e4xmi and I would like to hide one menu depending on the configuration. For that I've added the toggle isAdminMenuActive(). I've hoped that I can create a CoreExpression and using (somehow) the return value of this service. But unfortunately I can't get this to work by now.
What I tried was the following:
fragment.e4xmi:
<elements xsi:type="menu:MenuContribution" xmi:id="_ZuL6oA4IEeqbk5lJVdc6WQ" elementId="com.example.client.menucontribution.administration" parentId="com.example.product.main.menu">
<children xsi:type="menu:Menu" xmi:id="_ngOY4LOMEemRpJf6BiOdqQ" elementId="com.example.client.menu.administration" label="%UI.general.administration">
<visibleWhen xsi:type="ui:CoreExpression" xmi:id="_rrmcQA4lEeqbk5lJVdc6WQ" coreExpressionId="com.example.client.configuration.adminMenuActiveExpression"/>
<children xsi:type="menu:HandledMenuItem" xmi:id="_B4RqsLONEemRpJf6BiOdqQ" elementId="com.example.client.menuitem.action.administration" label="%UI.action" tooltip="%UI.action" command="_XnSp8LOPEemRpJf6BiOdqQ"/>
</children>
</elements>
plugin.xml
<extension
point="org.eclipse.core.expressions.definitions">
<definition
id="com.example.client.configuration.adminMenuActiveExpression">
<with variable="com.example.client.configuration.adminMenuActive">
<equals
value="true">
</equals>
</with>
</definition>
</extension>
Startapp.java
#PostConstruct
public void init(IEclipseContext context) {
context.set(com.example.client.configuration.adminMenuActive,
configuration.isAdminMenuActive().toString());
}
The current behaviour is that the menu is never shown no matter which configuration is loaded. I've tried if the variable is added to the IEclipseContext and this happens very early in the start up phase of the application. Currently I've no idea what's wrong.
Rather than using a core expression in the fragment.e4xmi you can use an 'Imperative Expression'. You do this by changing the 'Visible-When Expression' in the fragment editor.
An imperative expression specifies a Java class which is called directly making it easier to access your code. The method tagged with #Evaluate is called to evaluate the expression. Maybe something like:
#Evaluate
public boolean evaluate(#Named("com.example.client.configuration.adminMenuActive") String active)
{
return Boolean.valueOf(active);
}
Using this makes it much easier to see what is happening.

How to make Eclipse enabledWhen expression work for selection in non-focused view?

I have a handler which is connected to a menu contribution and a command. The menu contribution adds a button to a view and I want to have the button enabled depending on a selection in the Debug view.
So here's the expression:
<handler
class="com.example.myhandler"
commandId=" com.example.mycommand">
<enabledWhen>
<with
variable="selection">
<iterate
ifEmpty="false">
<instanceof
value="org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext">
</instanceof>
</iterate>
</with>
</enabledWhen>
</handler>
This works absolutely fine to the point where the Debug view has focus, meaning that if I select the element in the Debug view, the added button in a separate view is also enabled (as desired). As soon as I click on the view where I added the button via the menu contribution, then it's suddenly disabled (I guess because the selection is empty even though it's still selected; but the Debug view has no focus). How can I make this work so that the selection is still considered independently of the Debug view's focus state?
(You appear to be asking a DSF specific question, which has a different answer to the "general" case your title refers to. As such this answer probably solves your problem, but may not solve the general case.)
A complete example of extending DSF-GDB is provided in the CDT source repo in the org.eclipse.cdt.examples.dsf.gdb bundle.
That example defines a new command org.eclipse.cdt.examples.dsf.gdb.command.showVersion:
<!-- Example showing how to add a custom command with toolbar/menu contributions with DSF.
The example command id is org.eclipse.cdt.examples.dsf.gdb.command.showVersion.
In this example, when run it will display the version of GDB that is connected. -->
<extension point="org.eclipse.ui.commands">
<command
categoryId="org.eclipse.cdt.debug.ui.category.debugViewLayout"
description="Show the GDB Version in a pop-up"
id="org.eclipse.cdt.examples.dsf.gdb.command.showVersion"
name="Show GDB Version">
</command>
</extension>
It goes on to show how to contribute the command to the menus with the org.eclipse.ui.menus extension point. Then binds the command to a command handler with the org.eclipse.ui.handlers extension point.
Up until this point, DSF behaves the same as "normal" commands. But in DSF (using the retargettable command infrastructure provided by platform debug), the handler is not directly the command you are trying to run, but is a subclass of DebugCommandHandler.
DSF then can bind that command, using adapters to the concrete command implementation, depending on what the selected debug session in the Debug view is. In the show version case, this is GdbShowVersionHandler (implementation of IDebugCommandHandler). The handler has a canExecute which can connect to the back end if needed (gdb) to see if the current selection is applicable. The canExecute receives something that can be converted into a DSF context object like this:
private Optional<ICommandControlDMContext> getContext(final IDebugCommandRequest request) {
if (request.getElements().length != 1 || !(request.getElements()[0] instanceof IDMVMContext)) {
return Optional.empty();
}
final IDMVMContext context = (IDMVMContext) request.getElements()[0];
ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(context.getDMContext(),
ICommandControlDMContext.class);
if (controlDmc != null)
return Optional.of(controlDmc);
return Optional.empty();
}
PS I added this example to CDT to help another extender a while back. The conversation on cdt-dev may be useful too? This was all added initially for this bug, with its associated gerrit which pulls all the changes for adding a new command into one place.

How do I hide Menu whose children are hidden?

I have some menus on Main Menu in Eclipse 4 Application Project whose children are contributed with the help of fragments from different plugins. I have applied permissions on sub-menus according to logged in user.
My problem is even after no sub-menu of a menu has the permission and none of them is visible but still menu is visible. I want to hide that menu. Any Suggestions.
Update: My class for model Processor and plugin.xml
public class MenuProcessor {
public MenuProcessor(){}
#Execute
public void execute(#Named("application_luna.menu.contract(fo)") MMenu menu)
{
if(menu.getChildren().isEmpty())
menu.setVisible(false);
}
}
<extension
id="com.swte.approval.ui.fragment"
point="org.eclipse.e4.workbench.model">
<fragment
uri="fragment.e4xmi">
</fragment>
<processor
apply="always"
beforefragment="false"
class="com.swte.approval.ui.MenuProcessor1">
<element
id="application_luna.menu.contract(fo)">
</element>
</processor>
`
You could do this with a processor on the menu.
You declare the processor in the plugin.xml as part of the 'org.eclipse.e4.workbench.model' extension point.
<extension
id="playerFragment"
point="org.eclipse.e4.workbench.model">
... other elmeents
<processor
beforefragment="false"
class="package.MenuProcessor">
<element
id="menu.id">
</element>
</processor>
</extension>
The declaration names the class to run and the model element that it applies to.
The processor code could then inject the named menu and make it invisible if there are no children. Something like (not tested):
public class MenuProcessor
{
#Inject #Named("menu.id")
private MMenu menu;
#Execute
public void execute()
{
if (menu.getChildren().isEmpty())
menu.setVisible(false);
}
Note: you must inject the menu element as a field in the class, specifying it as a parameter on the execute method will not work. This is because the element is only available for injection when the class is constructed.

Single Line Menu Action in Eclipse RCP Workbench Coolbar

I added a JFace action to the workbench coolbar of an RCP application using a subclass of the ActionBarAdvisor. For some reason the corresponding menu is drawn with what I guess is an extra line. It is causing the whole coolbar including the list of open perspectives to be way too tall:
Anyone know how to make it single-lined? I tried adding SWT.SINGLE style to the Action constructor, but that didn't help. Any help would be appreciated. Thanks.
ActionBarAdvisor.fillCoolBar() implementation:
protected void fillCoolBar(final ICoolBarManager coolBar) {
final IToolBarManager toolbar = new ToolBarManager(coolBar.getStyle());
coolBar.add(new ToolBarContributionItem(toolbar, "main"));
toolbar.add(new Pulldown());
}
Action implementation:
public class Pulldown extends Action {
public Pulldown() {
super("Saved Layouts");
setMenuCreator(menuCreator);
}
...
}
It's not direct the answer of your problem, but I would declare the button as menu extension with dropdown/radio style. Thus you don't have to change the code of the coolbar.Here is a tutorial about toolbar menu contributions.
<extension
point="org.eclipse.ui.menus">
<menuContribution
allPopups="false"
locationURI="toolbar:de.example.com">
<command
commandId="de.abas.erp.wb.base.marker.commands.PullDownCommand"
icon="icons/16x16/layouts.png"
label="Saved Layouts"
style="pulldown">
</command>
</menuContribution>
</extension>

How to integrate a custom ISelectionProvider with Eclipse enabledWhen expressions?

I have a custom graphical EditorPart in a Eclipse RCP application.
It sets a custom ISelectionProvider in the createPartControl(..) method:
#Override
public void createPartControl(Composite parent) {
// [snip]
getSite().setSelectionProvider((ISelectionProvider)model.getSelection());
}
I have two ViewParts listening to selection changes via the ISelectionService. They get notified when the selection in the EditorPart changes. So far so good.
Now I've added a handler using the following XML:
<handlerclass="my.custom.Handler" commandId="org.eclipse.ui.edit.delete">
<enabledWhen>
<count value="+"/>
</enabledWhen>
</handler>
If the selection in the EditPart contains > 0 elements, this should enable the handler my.custom.Handler, but it doesn't.
It is, however enabled if the selection provided by built-in selection providers changes to > 0 element (i.e. those provided by JFace viewers).
Any hint how to integrate a custom implementation of ISelectionProvider with the expression framework used in the handler declaration?
enabledWhen can contain any expression. You need to specify that you want the selection variable there. See here for an example: http://eclipse-tips.com/tutorials/1-actions-vs-commands?start=1