This Bugzilla instance is deprecated, and most Eclipse projects now use GitHub or Eclipse GitLab. Please see the deprecation plan for details.
Bug 427465 - [Commands] @canExecute is not executed cyclic anymore
Summary: [Commands] @canExecute is not executed cyclic anymore
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.4   Edit
Hardware: PC Windows 7
: P3 normal (vote)
Target Milestone: 4.4 M6   Edit
Assignee: Paul Webster CLA
QA Contact: Paul Elder CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-02-05 08:34 EST by Alexander Fichtinger CLA
Modified: 2014-10-30 08:57 EDT (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Fichtinger CLA 2014-02-05 08:34:41 EST
Bug occured in Eclipse-Version: Luna M5

SCENARIO

The canExecute method in our E4Part is not executed cyclic anymore. The problem is that we want to use it to check if a toolbar item in the view is active or not.

QUESTION

Is there a workaround for this in M5 or does it work completely different than in former versions/milestones?


Regards,
Alexander Fichtinger
Comment 1 Thomas Schindl CLA 2014-02-05 08:39:08 EST
I think we could introduce an event users can fire themselves to request a state check for:
* all items
* a specific item
Comment 2 Paul Webster CLA 2014-02-05 08:42:36 EST
You might be seeing the changes for Bug 385394  Constantly polling everbody's @CanExecute was a performance hit.

The MToolItem will ask its Command @CanExecute (which is passed to the handler) when appropriate variables in the context change (which can include selection, active part, etc).

If you have an external data source, it won't participate in the @CanExecute lifecycle.  It has to be hooked into an IEclipseContext somehow.  You can hook it up yourself.

What were you trying to do?  @CanExecute usually defines *if* a tool item will be enabled or not based on *some condition*.  How can the @CanExecute check for a toolbar item is enabled (or what does active mean)?

PW
Comment 3 Thomas Schindl CLA 2014-02-05 08:45:23 EST
I generally agree - but sometime it is feasable to simply request a revalidation - couldn't we introduce an event which we honor?
Comment 4 Alexander Fichtinger CLA 2014-02-05 08:48:40 EST
Sorry, my comment was misleading... :)

I meant exactly what you wrote:

"@CanExecute usually defines *if* a tool item will be enabled or not based on *some condition*."

We have a condition, and based on this condition the canExecute will return true/false.

In order to that, the state of the toolbar item will change to active/inactive.

I think now it is better to understand. :)

Regards,
Alex
Comment 5 Paul Webster CLA 2014-02-05 08:53:10 EST
(In reply to Alexander Fichtinger from comment #4)
> Sorry, my comment was misleading... :)
> 
> I meant exactly what you wrote:
> 
> "@CanExecute usually defines *if* a tool item will be enabled or not based
> on *some condition*."
> 
> We have a condition, and based on this condition the canExecute will return
> true/false.

But your condition is not based on something in IEclipseContext?  External sources?

PW
Comment 6 Alexander Fichtinger CLA 2014-02-05 08:58:47 EST
(In reply to Paul Webster from comment #5)

> But your condition is not based on something in IEclipseContext?  External
> sources?
> 
> PW

No, unfortunately not. We indeed have an object in the context (it's a session object which contains various informations) The canExecute method looks as follows:

=======================================================
  public boolean canExecute(final TargetSession session)
  {
    return session != null && session.getResource() != null && session.getResource().getConfiguration() != null
      && session.isConnected();
  }

=======================================================

The problem is that the session should be set anew in the context, isn't it?
Because in our case just the attributes of the object (like isConnected) are changing.


Thanks for your help,
Alex
Comment 7 Paul Webster CLA 2014-02-05 08:59:14 EST
(In reply to Thomas Schindl from comment #3)
> I generally agree - but sometime it is feasable to simply request a
> revalidation - couldn't we introduce an event which we honor?

The low level access to update tool items is org.eclipse.e4.ui.workbench.renderers.swt.ToolBarManagerRenderer.updateEnablement()

We call it in a couple of places in Eclipse4 and in the 3.x Workbench (like updateActionBars()).

In 3.x, another appropriate place we could add it would be org.eclipse.ui.services.IEvaluationService.requestEvaluation(String)

In Eclipse4 only, it's a little harder.  We don't have API for it currently, but a short-term workaround would be:

toolBarManagerRenderer = context.get(ToolBarManagerRenderer.class);
if (toolBarManagerRenderer != null) {
	toolBarManagerRenderer.updateEnablement();
}

Of course, this is not API and could change.

PW
Comment 8 Thomas Schindl CLA 2014-02-05 09:03:20 EST
I dream about:

IEventBroker b;

// for a specific item
b.post(UIEvents.ToolItem.checkEnablement,"my.item.id");
b.post(UIEvents.ToolItem.checkEnablement,UIEvents.ToolItem.ALL);
Comment 9 Alexander Fichtinger CLA 2014-02-05 09:11:45 EST
(In reply to Thomas Schindl from comment #8)
> I dream about:
> 
> IEventBroker b;
> 
> // for a specific item
> b.post(UIEvents.ToolItem.checkEnablement,"my.item.id");
> b.post(UIEvents.ToolItem.checkEnablement,UIEvents.ToolItem.ALL);

Yeah, this would be really cool. :)
Comment 10 Thomas Schindl CLA 2014-02-05 15:53:32 EST
https://git.eclipse.org/r/21586
Comment 12 Paul Webster CLA 2014-02-13 14:02:29 EST
Released http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?id=d94d17ab4b8851faec75ab3e6cba3e9d34e407d2 to update since tags.

But I only just realized Selector was in UIEvents.  Tom, could you please prepare a patch and move that somewhere else.  If you don't want it in org.eclipse.e4.ui.workbench.renderers.swt then I guess as a separate class in org.eclipse.e4.ui.workbench

PW
Comment 13 Thomas Schindl CLA 2014-02-13 17:02:45 EST
Uploaded a new gerrit changeset - https://git.eclipse.org/r/#/c/21975/
Comment 15 Alexander Fichtinger CLA 2014-02-14 02:08:21 EST
(In reply to Thomas Schindl from comment #13)
> Uploaded a new gerrit changeset - https://git.eclipse.org/r/#/c/21975/

Hi Tom,

what is the Selector class?

Can one implement the "select" method, and then determine which elements to observe in @canExecute?

It would be great if you could give me a short explanation on how it works now =).
Because when M6 is released, we have to rework our implementation of course. =)

Regards,
Alex
Comment 16 Paul Webster CLA 2014-02-14 09:20:15 EST
I guess the 2 ways to use it now are:

eventBroker.send(UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC, UIEvents.ALL_ELEMENT_ID);

or 

Selector s = (a selector that an MApplicationElement or ID);
eventBroker.send(UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC, s);

PW
Comment 17 Paul Webster CLA 2014-03-04 12:29:19 EST
In 4.4.0.I20140303-2000

PW
Comment 18 Gernot Krause CLA 2014-10-30 05:53:45 EDT
I am not sure if this behavior is by intention:

SCENARIO

If I request an enablement update for my tool items via
eventBroker.send(UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC, UIEvents.ALL_ELEMENT_ID);
this works fine as long as I use UIEvents.ALL_ELEMENT_ID.

If I for example use "my.toolitem.id" the update also happens correctly for this special tool item. However, subsequent events for another tool item like "my.toolitem.id.2" are not executed anymore.

I went done to org.eclipse.e4.ui.workbench.renderers.swt.ToolItemUpdater and found this method:

public void updateContributionItems(Selector selector) {
    for (final HandledContributionItem hci : itemsToCheck) {
        if (hci.model != null && hci.model.getParent() != null
                              && selector.select(hci.model)) {
            hci.updateItemEnablement();
        } else {
            orphanedToolItems.add(hci);
        }
    }
    if (!orphanedToolItems.isEmpty()) {
        itemsToCheck.removeAll(orphanedToolItems);
        orphanedToolItems.clear();
    }
}

QUESTION

The first if clause checks for hci.model = null and its parent = null AND if it passes the selector. If this clause results in false the item is added to the orphanedToolItems list and those items are removed from the itemsToCheck list afterwards.

However, in the cases that only the selector check fails (meaning the tool item does not need an update) it is still added to the orpahned items list and also will be removed from the itemsToCheck list afterwards.

That's why subsequent enablement update reuqests for other tool items will fail since they might be removed.

SOLUTION

Put the selector check to another if clause:

public void updateContributionItems(Selector selector) {
    for (final HandledContributionItem hci : itemsToCheck) {
        if (hci.model != null && hci.model.getParent() != null) {
            if (selector.select(hci.model)) {
                hci.updateItemEnablement();
            }
        } else {
            orphanedToolItems.add(hci);
        }
    }
    if (!orphanedToolItems.isEmpty()) {
        itemsToCheck.removeAll(orphanedToolItems);
        orphanedToolItems.clear();
    }
}

What do you think?

Regards,
Gernot Krause.
Comment 19 Paul Webster CLA 2014-10-30 08:57:44 EDT
Great catch.  Please open another bug with your description and we'll get it into 4.4.2

PW