Home Tutorials Training Consulting Books Company Contact us


Get more...

This article describes how you can react to changes in the application model.

1. Listening to Eclipse framework events

If the application model changes, the Eclipse framework sends out notifications. The Eclipse 4 framework allows event listeners to be notified if this happens.

For example, the UIEvents.UILifeCycle.APP_STARTUP_COMPLETE event is triggered once the application has started. Application components can register for this event and are called by the framework once it occurs.

If your application model changes—for example, if windows, perspectives, parts, or their containers (such as the application, perspective stacks, part sash containers, or part stacks) are modified—the framework detects these changes. In this case, the IEventBroker propagates an event.

The central class that defines the Eclipse framework events is the UIEvents class from the org.eclipse.e4.ui.workbench package. It contains the events or topics and helper methods for working with the events. This class contains information in its Javadoc about the purpose of events. The Eclipse framework sends out objects of the type org.osgi.service.event.Event.

1.1. Example: Part activation event

A topic that is fired by Eclipse is UIEvents.UILifeCycle.ACTIVATE, which is fired every time a part is activated.

@Inject
@Optional
public void subscribeTopicPartActivation(@UIEventTopic(UIEvents.UILifeCycle.ACTIVATE) Event event) {
    
    Object element = event.getProperty(EventTags.ELEMENT);
    if (!(element instanceof MPart)) {
        return;
    }
    
    MPart part = (MPart) element;
    
    System.out.println("Part activated: " + part.getLabel());
}

The Event class contains properties, such as EventTags.ELEMENT, from which you can receive the element associated with this event.

Review the UIEvents class and its internal interfaces, such as UIEvents.EventTags, to see the other events fired by the Eclipse framework.

1.2. Extracting event information

The UIEvents.EventTags interface defines the possible event tags.

EventTags interface with properties of an Event

These event tags are explained in the following sections.

1.2.1. ATTNAME event tag

The ATTNAME property of the event is the attribute of the model that has been changed.

For instance, if you listen for changes to a model element’s label, the ATTNAME of the event will be label.

@Inject
@Optional
public void subscribeTopicLabelChange(@EventTopic(UIEvents.UILabel.TOPIC_LABEL) Event event) {
    Object element = event.getProperty(EventTags.ATTNAME);
    
    // will print "label" to the system output
    System.out.println(element);
}
Label ATTNAME
The label is an XML attribute and can be found in the source of your Application.e4xmi file.
<children xsi:type="basic:Part" xmi:id="_9CJOwNgYEeKX2Jv3m0M6IA"
                        elementId="com.sample.application.part.attname"
                        contributionURI="bundleclass://com.sample.application/yourpackage.attname"
                        label="ATTNAME sample label"/>

1.2.2. ELEMENT event tag

The ELEMENT event tag generally refers to the model element that is associated with the event.

1.2.3. NEW_VALUE and OLD_VALUE event tags

The NEW_VALUE and OLD_VALUE properties contain the new and old values of the element’s attribute. These tags represent the values before and after the attribute change. For example, use them to detect which value was updated when listening for selection changes in perspectives.

In Eclipse 3.x, you would have registered an IPerspectiveListener for the WorkbenchWindow and implemented the perspectiveChanged(…​) method. In Eclipse 4, you think in a DOM (Document Object Model) manner, where <ELEMENT ATTNAME="NEW_VALUE"> is manipulated and fired within events.

An example of using the values of an attribute in the model is a listener for a perspective change.

@Inject
@Optional
public void subscribeTopicSelectedElement(@EventTopic
        (UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT) Event event) {
    Object element = event.getProperty(EventTags.ELEMENT);
    Object newValue = event.getProperty(EventTags.NEW_VALUE);
    // ensure that the selected element of a perspective stack is changed and that this is a perspective
    if (!(element instanceof MPerspectiveStack) || !(newValue instanceof MPerspective)) {
        return;
    }

    MPerspectiveStack perspectiveStack = (MPerspectiveStack) element;
    MPerspective perspective = (MPerspective) newValue;

    System.out.println("Selected perspective of " + perspectiveStack + " is " + perspective.getLabel());
}

If you want to listen for an event when the perspective changes, you need to listen for the change of the selectedElement attribute of the MPerspectiveStack. You can then get the new and old MPerspective from the EventTags.NEW_VALUE and EventTags.OLD_VALUE properties.

You listen for the changed selectedElement attribute value of the MPerspectiveStack, rather than looking for a "perspective change" event.

It is good to keep the DOM structure of the application model in mind and think about the changes that can occur within the application model.

1.2.4. TYPE event tag

With the TYPE event tag, you can determine what action has been performed on the application model.

You can find the various types in the UIEvents.EventTypes interface.

With these event types, you can determine whether a model element was added, created, moved, removed, or an attribute was set.

The UIEvents class offers convenience methods to check for some types.

Convenience methods for event types

The isADD(Event) and isREMOVE(Event) methods apply to both single and multiple items (e.g., ADD and ADD_MANY).

1.2.5. POSITION event tag

The POSITION event tag contains the new position of model elements. This property is only set if a collection of elements is concerned.

If you move parts in an MPartStack or between different MPartStack containers, you can listen for changes to the children of an element container like this:

@Inject
@Optional
public void subscribeTopicElementContainerChildren(@EventTopic
        (UIEvents.ElementContainer.TOPIC_CHILDREN) Event event) {
    Object element = event.getProperty(EventTags.ELEMENT);
    if (!(element instanceof MPartStack)) {
        return;
    }

    if (UIEvents.isADD(event)) {
        // check new value, because we check for addition and old value will be null
        Object newValue = event.getProperty(EventTags.NEW_VALUE);
        if (newValue instanceof MPart) {
            MPart part = (MPart) newValue;
            System.out.println("Added " + part.getLabel() + " at position: "
                    + event.getProperty(EventTags.POSITION));
        }
    } else if (UIEvents.isREMOVE(event)) {
        // check old value, because we check for remove and new value will be null
        Object oldValue = event.getProperty(EventTags.OLD_VALUE);
        if (oldValue instanceof MPart) {
            MPart part = (MPart) oldValue;
            System.out.println("Removed " + part.getLabel() + " from position: "
                    + event.getProperty(EventTags.POSITION));
        }
    }
}

If you drag and drop an MPart within an MPartStack, the MPart is removed and then added back to the MPartStack.

The details can be seen in the UIEventPublisher.formatData() method from the org.eclipse.e4.ui.internal.workbench package. The position attribute is only used for the move, add, or remove events.

1.2.6. WIDGET event tag

The WIDGET event tag can be used to get the underlying widget, e.g., a Composite of the model element that is associated with the event.

You can directly interact with the underlying widget. In most cases, this is not necessary, because the model elements themselves provide most of the methods to interact with the widget, such as setLabel() or setVisible().

1.3. Convenience methods for working with events

Besides the convenience methods for the event types, the UIEvents class offers two more convenience methods:

Additional convenience methods of the UIEvents class.

1.3.1. The contains method

The contains method can be used to check whether a certain object is part of an event’s property.

Imagine you are listening for changed tags on a certain model element and want to determine if a certain tag is part of the tags of the given model element. In this case, the contains method helps you with that task.

An example of this can be found in the subscribeTopicTagsChanged(Event) method of the ToolBarManagerRenderer class from the Eclipse framework.

@Inject
@Optional
private void subscribeTopicTagsChanged(@UIEventTopic
        (UIEvents.ApplicationElement.TOPIC_TAGS) Event event) {

    Object changedObj = event.getProperty(EventTags.ELEMENT);

    // this should only be applied to MToolbar elements
    if (!(changedObj instanceof MToolBar)) {
        return;
    }

    final MUIElement changedElement = (MUIElement) changedObj;

    // In case tags have been added and the new tag list contains the 
    // IPresentationEngine.HIDDEN_EXPLICITLY String, the element should be hidden
    if (UIEvents.isADD(event)) {
        if (UIEvents.contains(event, UIEvents.EventTags.NEW_VALUE, 
                IPresentationEngine.HIDDEN_EXPLICITLY)) {
            changedElement.setVisible(false);
            changedElement.setToBeRendered(false);
        }
    // If IPresentationEngine.HIDDEN_EXPLICITLY tag was removed, the element should be visible again 
    } else if (UIEvents.isREMOVE(event)) {
        if (UIEvents.contains(event, UIEvents.EventTags.OLD_VALUE, 
                IPresentationEngine.HIDDEN_EXPLICITLY)) {
            changedElement.setVisible(true);
            changedElement.setToBeRendered(true);
        }
    }
}

This code demonstrates how the existence of a certain tag (IPresentationEngine.HIDDEN_EXPLICITLY) is checked with the contains method.

1.3.2. The asIterable method

The asIterable method can be used to get an Iterable of an event’s property.

Let’s take the example where we listened for the UIEvents.ElementContainer.TOPIC_CHILDREN event and checked the new position.

This example fails when we add more than one child to the MPartStack, which results in the UIEvents.EventTypes.ADD_MANY event type. In this case, nothing would be printed to System.out, because we have a list in the EventTags.NEW_VALUE property rather than an MPart.

We need to enhance our subscribeTopicElementContainerChildren method like this:

@Inject
@Optional
public void subscribeTopicElementContainerChildren(@EventTopic
        (UIEvents.ElementContainer.TOPIC_CHILDREN) Event event) {
    Object element = event.getProperty(EventTags.ELEMENT);
    if (!(element instanceof MPartStack)) {
        return;
    }

    if (UIEvents.isADD(event)) {
        // check new value, because we check for addition and old value will
        // be null
        Iterable<?> asIterable = UIEvents.asIterable(event, EventTags.NEW_VALUE);
        for (Object object : asIterable) {
            if (object instanceof MPart) {
                MPart part = (MPart) object;
                System.out.println("Added " + part.getLabel() + " at position: "
                        + event.getProperty(EventTags.POSITION));
            }
        }
    } else if (UIEvents.isREMOVE(event)) {
        // check old value, because we check for remove and new value will
        // be null
        Iterable<?> asIterable = UIEvents.asIterable(event, EventTags.OLD_VALUE);
        for (Object object : asIterable) {
            if (object instanceof MPart) {
                MPart part = (MPart) object;
                System.out.println("Removed " + part.getLabel() + " from position: "
                        + event.getProperty(EventTags.POSITION));
            }
        }
    }
}

2. Optional exercise: Changing the window title on a perspective change

In this exercise, we will change the window title according to the active perspective.

2.1. Add a new model add-on that listens to perspective switches

Create the following PerspectiveSwitchAddon class. This add-on listens for the perspective change event.

package com.vogella.tasks.ui.addon;

import jakarta.inject.Inject;

import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.di.extensions.EventTopic;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.UIEvents.EventTags;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.osgi.service.event.Event;

public class PerspectiveSwitchAddon {

    @Inject
    private EModelService modelService;

    @Inject
    @Optional
    public void subscribeTopicSelectedElement(
            @EventTopic(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT) Event event) {
        Object newValue = event.getProperty(EventTags.NEW_VALUE);

        // only run this, if the NEW_VALUE is a MPerspective
        if (!(newValue instanceof MPerspective)) {
            return;
        }

        MPerspective perspective = (MPerspective) newValue;

        // Get the MWindow, where we want to change the label
        MWindow topLevelWindowOfPerspective = modelService.getTopLevelWindowFor(perspective);

        topLevelWindowOfPerspective.setLabel(perspective.getLabel() + " Window");

    }
}

Now we add a new model add-on via a model fragment.

perspective switch addon

2.2. Validate

Start your application again. Switch perspectives and observe the window title. It should change to the name of the perspective.

If the add-on is not visible, try to start your runtime with the -clearPersistedState flag to remove persisted model information.

3. Application Model Events online resources