This article describes how you can react on changes of 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 to notify event listeners if this happens.

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

Also if your application model changes like your windows, perspectives, parts and their containers, e.g., application, perspective stacks, part sash container or part stacks, an event is propagated by the IEventBroker.

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

1.1. Example: the part activation event

A topic, which is fired by Eclipse is the 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 like EventTags.ELEMENT, from which we can receive the element, which is concerned in this event. But there are more and therefore you should have a look at the UIEvents class and its internal interfaces like UIEvents.EventTags.

1.2. Extract event information

The UIEvents.EventTags interface define 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, which has been changed.

So if you for instance listen to changes of 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 is 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 in general is the model element, which is concerned in the event.

1.2.3. NEW_VALUE and OLD_VALUE event tags

The NEW_VALUE and OLD_VALUE properties contain the changed value of the element’s attribute.

In the old Eclipse 3.x days, you would have registered an IPerspectiveListener for the WorkbenchWindow and implement the perspectiveChanged(…​) method. But in Eclipse 4 you’d rather have to think in a DOM(Document Object Model) manner, where <ELEMENT ATTNAME="NEW_VALUE"> are manipulated and fired within events.

A example for 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());
}

In case you want to listen to a kind of "perspective changed" event, you need to listen to the change of the selectedElement attribute of the MPerspectiveStack and can get the new and old MPerspective from EventTags.NEW_VALUE and EventTags.OLD_VALUE properties.

So you actually listen to the changed selectedElement attribute value of the MPerspectiveStack, rather than looking for a "perspective change" event.

So it is good to keep the DOM structure of the application model in mind and think of the changes, which can occur within the application model.

1.2.4. TYPE event tag

With the TYPE event tag you may determine what action has been done to the application model.

The different types can be found in the UIEvents.EventTypes interface.

With these event types you are able to figure out, whether a model element was added, created, moved, removed or an attribute was set.

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

Convenience methods for event types.

The isADD(Event) and isREMOVE(Event) methods apply for both single and many, e.g., ADD and ADD_MANY)

1.2.5. POSITION event tag

The POSITION event tag contains the new position of model elements. So this property is only set in case a collection of elements is concerned.

If you move parts in a MPartStack or between different MPartStacks, you can also listen to changes of 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));
        }
    }
}

In case you drag and drop a MPart within a MPartStack, the MPart will be removed and afterwards added to the MPartStack.

The details can be seen in the UIEventPublisher.formatData() method from the org.eclipse.e4.ui.internal.workbench package, where the position on is only the for 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, which is concerned in the event.

So you are also able to directly interact with the underlying widget. But in most cases this is not necessary, because the model elements themselves provide the most of the methods to interact with the widget, like setLabel or setVisible methods and so on.

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 listen 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 for 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 to the UIEvents.ElementContainer.TOPIC_CHILDREN event and checked the new position.

This sample lacks in case we add more than one child to the MPartStack, so that we have the UIEvents.EventTypes.ADD_MANY event type. In this case nothing would be printed to the system out, because we have a list in the EventTags.NEW_VALUE property rather than an MPart.

So 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. Exercise: Changing the window title on perspective change

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

2.1. Add a new model add-on which listen to perspective switches

Now we add a new model-addon via an model fragment. This add-on will listen to the perspective change event.

Perspective switch addon

The implementation of the PerspectiveSwitchAddon looks like this:

package com.vogella.tasks.ui.addon;

import javax.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);

        // for demo purpose only print out attributes of topLevelWindow
        System.out.println(topLevelWindowOfPerspective);
    }
}

2.2. Validate

Start your application again. Switch perspectives and check for the output on the Console.

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

3. Application Model Events online resources

If you need more assistance we offer Online Training and Onsite training as well as consulting