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

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.

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 customize the context menu in the solution explorer for a specific project type?

Description
I have developed a Visual Studio extension (VSPackage) which adds a new Project Type to Visual Studio (using CPS Project System). I also have added some Commands to the VSPackage.
When right clicking on my Project Node in the Solution Explorer, I want to have a customized context menu to appear.
Example
For example: in the screen shot below, I want to get rid of the Build command and add a custom command (e.x. mycommand).
I tried..
Setting the Parent of my custom command to IDM_VS_CTXT_PROJNODE.
Question
When I create a new Custom Project Type, How to create a new Context Menu for my Project Nodes in the Solution Explorer?
How to remove/add Commands to the Context Menu only for the Custom Projects:
If I have a C# project, the context menu should be the default one, if I add a MyProjectType project, I want to see a different context menu when right clicking on the Project Node in the Solution Explorer.
You were close with the IDM_VS_CTXT_PROJNODE parent.
Here is how I achieved it in my FluentMigratorRunner extension, which only shows its context menu item for a project if it has a reference to the FluentMigrator NuGet package.
Step 1: Add a submenu to the context menu
<Menus>
<Menu guid="guidCmdSet" id="packageMenu" priority="0x0300" type="Menu">
<Parent guid="guidSHLMainMenu" id="IDG_VS_CTXT_PROJECT_BUILD" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>CPSProject</ButtonText>
<CommandName>CPSProject</CommandName>
</Strings>
</Menu>
Note the added special CommandFlag elements.
Step 2: Add a group to the menu
<Groups>
<Group guid="guidCmdSet" id="packageMenuGroup" priority="0x0600">
<Parent guid="guidCmdSet" id="packageMenu" />
</Group>
</Groups>
Step 3: Add a button
<Button guid="guidCmdSet" id="specialBuildActionId" priority="0x0100" type="Button">
<Parent guid="guidCmdSet" id="packageMenuGroup" />
<CommandFlag>DynamicVisibility</CommandFlag>
<Strings>
<ButtonText>Special build</ButtonText>
</Strings>
Step 4: Add the menu in your *Package.cs
protected override async System.Threading.Tasks.Task InitializeAsync(System.Threading.CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
// Initialize the Fluent Migrator Menu, should only be visible for projects with FluentMigrator reference
var mcs = await GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
var menuCommandId = new CommandID(packageCmdSetGuidString, 0x1010);
var menuItem = new OleMenuCommand(null, menuCommandId);
menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus;
mcs.AddCommand(menuItem);
}
private void MenuItem_BeforeQueryStatus(object sender, EventArgs e) =>
((OleMenuCommand)sender).Visible = ???;
Note the added BeforeQueryStatus eventhandler.
In that eventhandler you can check the type of the project and return a boolean controlling if the extra context menu should be shown yes or no
I'm not sure about removing the existing Build menu item... but as for having context menu items appear only when right-clicking on your custom project type, <VisibilityConstraints> might help:
(Excerpt from an example usage):
Often a package is being loaded to run a BeforeQueryStatus method for a button to determine its visibility. With <VisibilityConstraints> we can determine the visibility of a button without running BeforeQueryStatus and therefore don't need to load the package before the user clicks the button.
I've only ever used this strategy to show menu items for specific file types, but there are also some term types related to projects that might be more useful for this scenario.

Eclipse plugin: overriding standard command handler

I'm trying to override an existing command handler in Eclipse. Specifically, I want to override the Run command (org.eclipse.debug.ui.commands.RunLast) so that it first terminates the process from the previous launch before starts the new one. I read this article, wrote a handler and associated it with the Run command:
<extension point="org.eclipse.ui.handlers">
<handler commandId="org.eclipse.debug.ui.commands.RunLast"
class="net.anggo.tnr.TnRHandler">
<activeWhen>
<with variable="activeContexts">
<iterate operator="or">
<equals value="net.anggo.tnr.TnREnabled"></equals>
</iterate>
</with>
</activeWhen>
</handler>
</extension>
I added activeWhen clause so that the new handler has a higher priority than the default handler. I activated TnREnabled context in an IStartup. But still, the default handler is run when I hit the Run menu item. I assume this is because the priority of the default handler is still higher than that of the new one. So my question is..
How can I calculate the priority of the default handler so that I can compare it with that of the new handler? Does my assumption seem correct that it is a priority issue? Is there a diagnostic way I can see what the problem is e.g. plugin spy? I'd appreciate any of help.

how to debug JSF/EL

How to debug EL in the JSF page? I'd like to watch variable values, function calls an so on. The best solution would be an eclipse plugin, but any other possibility is better than guessing "Why this expression failed to render correctly?".
Closest what you can get in JSF/Facelets is placing an <ui:debug /> somewhere in the view:
<ui:debug />
Pressing CtrlShiftD should then show a popup window with debug information about the component tree and all available request parameters and request/view/flash/session/application scoped variables. It's basically a representation of the content of all those maps.
The hotkey is by the way configureable by hotkey attribute so that you can choose another whenever it clashes with browser default hotkeys, as it would do in Firefox; CtrlShiftD would by default show the Add bookmarks dialogue. Here's how you could make it to listen on CtrlShiftX instead:
<ui:debug hotkey="x" />
You'd usually also like to hide it in non-development stage, so add a rendered condition like that:
<ui:debug hotkey="x" rendered="#{facesContext.application.projectStage == 'Development'}" />
In the shown debug information, the information provided about scoped variables isn't that great as you would expect. It only shows the Object#toString() outcome of all scoped variables which defaults to com.example.Bean#hashcode. You can't explore their properties and the values of their properties directly like as you could do in debug view of Eclipse's debugger. You'd need to implement toString() on the class accordingly so that as much as possible relevant information is returned (if necessary, you can even let Eclipse autogenerate it by rightclick source code > Source > Generate toString()):
#Override
public String toString() {
return String.format("Bean[prop1=%s,prop2=%s,prop3=%s]", prop1, prop2, prop3);
}
As to method calls, just put a breakpoint on the Java source code the usual way. Eclipse will kick in there as well when EL calls the method. If it's a managed bean, you'll also just see its properties in the Eclipse debugger.
If you are really having problems then if you can get the source for the EL implementation (easy enough for the RI) then you can use Eclipse to set breakpoints in the EL implementation methods. You need to get an understanding of how the EL code works, but it isn't that complicated. Not for the very faint hearted though.
Another possibility would be to create and evaluate the EL programatically. There are examples of how to do this around. You can then use the debugger to fiddle around with what the expression is and what the result is until you've worked out where your problem lies.

Eclipse RCP: how to observe the states of the cut/copy/paste commands?

I'm currently struggling with the following Eclipse RCP commands:
org.eclipse.ui.edit.cut
org.eclipse.ui.edit.copy
org.eclipse.ui.edit.paste
I'm using them as command contributions in the toolbar, but the UIElements (toolbar items) are not updated when the 'handled' state of those commands changes.
For testing I used a polling mechanism to verify that the state of those commands really changes depending on the currently focussed element, and I found out that the handler remains the same but the handler's 'handled' state changes properly, causing the commands 'handled' state to also change properly.
The only problem is, that neither one of those state changes causes a notification (neither on the command's ICommandListener, nor on the handler's IHandlerListener), so the UIElements won't get updated.
Here's some testing code to observe the states of a Command:
ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
final String commandId="org.eclipse.ui.edit.copy";
Command command = commandService.getCommand(commandId);
command.addCommandListener(new ICommandListener() {
public void commandChanged (CommandEvent commandEvent) {
System.out.println(">> Command changed: " + commandId);
}
});
Am I missing something, or is this an bug in the cut/copy/paste handler implementations?
Any insights?
EDIT:
The commands are enabled all the time, and the handler is never exchanged, only the handler's 'handled' state (and thus also the commmand's 'handled' state) changes depending on which ui element has the focus. There is however no notification when this state changes.
This results in the toolbar buttons always being enabled, and pressing them will cause a org.eclipse.core.commands.NotHandledException: There is no handler to execute for command.
The handler which is registered for the cut/copy/paste commands is org.eclipse.ui.internal.handlers.WidgetMethodHandler. This handler checks if a given method is declared on the current display's focus control. When executed, that handler will invoke the method using reflection.
Snippet from WidgetMethodHandler:
public final boolean isHandled() {
return getMethodToExecute() != null;
}
The getMethodToExecute() will locate the current focus control using Display.getCurrent().getFocusControl(), and then check if the given trigger method is declared on it.
Widgets such as org.eclipse.swt.widgets.Text have cut(), copy() and paste() methods, so when the focus is on such a widget, the handler will return 'true' for isHandled().
This handler is however not aware when the current focus control changes (I think there isn't even a way to observe this on the Display), and thus can't notify about changes on its dynamic 'isHandled' state.
This results in the cut/copy/paste commands being fine for popup menus, but they're quite problematic when used in toolbars, as their UI elements can't be updated properly when the handler does no notifications.
This leaves me with either not using those commands in the toolbar, or having a polling mechansim to update the ui elements (which is also bad and error prone). :-(
Your problem is that you need to register a handler for anything that is not a text because Eclipse needs to know how to copy the currently selected "something" to the clipboard. That's what a handler does. This article in the Eclipse wiki will get you started how to create and register a handler.
I could be wrong, but the source of the problem is that the handler is always enabled.
See Platform Plug-in Developer Guide > Programmer's Guide > Plugging into the workbench > Basic workbench extension points using commands > Handlers.
The <activeWhen/> expressions in the
plugin.xml and programmatic core
expressions are used to help determine
the scope of a handlers activation.
For example, a specific window, a
specific Shell, an active part type or
active part.
<extension
point="org.eclipse.ui.handlers">
...
<handler
class="org.eclipse.ui.examples.contributions.view.SwapInfoHandler"
commandId="org.eclipse.ui.examples.contributions.view.swap">
<activeWhen>
<reference
definitionId="org.eclipse.ui.examples.contributions.view.inView">
</reference>
</activeWhen>
<enabledWhen>
<count
value="2">
</count>
</enabledWhen>
</handler>
...