Picking services

Modelio v3.5

What is ‘picking’ in Modelio

In several places in Modelio, when a form or a dialog requires a model element to be chosen to fill a particular field, the user can directly designate a particular element by simply clicking on it.
In Modelio this specific interaction is called “picking”, it can be briefly described as the following sequence:

  • A form or dialog field, called the picking client, requires a model element to be chosen (usually there is a small symbol next to the field)
  • The user clicks into the client field thereby activating or entering the picking session
  • The mouse cursor shape on the screen changes for the picking cursor when the mouse is hovering a model element considered as valid by the client field. Note that only certain parts of the GUI are able to provide a picking cursor (for example the model browser or diagrams) while some others will not react at all. Picking-able GUI parts are called picking providers
  • If the user left-clicks while hovering a valid element (picking cursor is displayed) the hovered element is selected in the client field and the picking session terminates.
  • The picking session can be aborted at anytime either by pressing the ESC key or by selecting another (non-picking client) field in the form/dialog.

Pricking service provides the programmatic support to picking clients and picking providers:

  • the picking service interface is IPickingService
  • the IPickingService instance can be obtained from the IModuleContext of the module via IModelioServices

The picking service can be used by a module to either implement a picking client, a picking provider or both.

Quick outline:

  1. Implement a picking client
    1. Defining a picking client
    2. Branching the picking client to the gui
  2. Implement a picking provider
    1. The IPickingProvider interface
    2. Registering the provider
    3. Firing picking events

Picking operations

The following sequence diagram illustrates a typical picking operation.

fig1

Implementing a picking client

You may want to implement a picking client when you need the end-user to designate a particular element in the application.

A picking client is an object that implements the IPickingClient interface. The responsibilities of a picking client are:

  1. to start a picking session at a given moment, typically when a GUI widget enters in edition.
  2. to accept or refuse the model elements hovered by the mouse cursor while the user is navigating in the application to choose an element. This will change the cursor shape accordingly.
  3. to process the picked element, which is the (accepted) element clicked on by the user, and end the picking session.
  4. to manage the eventual aborting of the picking session it has started.

In most cases, a dedicated class will be in charge of these responsibilities.

Defining a picking client

In the following code fragment, a SWT Text widget is configured to behave as a picking client.

 1class MyPickingClient implements IPickingClient {
 2    private IPickingSession pickSession;
 3
 4    //...
 5    @Override
 6    public boolean acceptElement(MObject target) {
 7        return (target instanceof BpmnActivity);
 8    }
 9
10    @Override
11    public boolean setElement(MObject target) {
12        // Process the 'picked' target
13        System.out.println("picked element" + target.getName());
14 
15        // Get the picking service and terminate the picking session !
16        IPickingService pickingService = MyModule.getInstance().getModelioContext().getModelioServices().getPickingService();
17        pickingService.endPickingSession(this.pickSession);
18        this.pickSession = null;                              // Avoid keeping wrong references
19        return true;
20    }
21
22    @Override
23    public void pickingAborted() {
24        IPickingService pickingService = MyModule.getInstance().getModelioContext().getModelioServices().getPickingService();
25        pickingService.endPickingSession(this.pickSession);
26        this.pickSession = null;                              // Avoid keeping wrong references
27    }
28
29    public void setSession(IPickingSession aPickSession) {
30        this.pickSession = aPickSession;
31    }
32
33    ...
34}
35

Line 1: we choose to code out picking client in a dedicated class MyPickingClient that implements IPickingClient.
Line 6: the acceptElement() method is called for each model element hovered by the mouse. In this method you indicate if the hovered element can be picked or not for example by testing its type, name, status, and so on.
Returning true will change the mouse cursor to the picking accepted cursor shape (a hand symbol), returning false will change the cursor to a forbidden picking cursor shape. Here, the method will only accept elements which are instances of BpmnActivity.
Line 11: the setElement() method is called when the user clicks on a model element previously accepted by the acceptElement() method above. The target element is the element finally chosen by the end-user, ie the picked element. In this method you have to process the chosen element (what has to be done is application specific and cannot be detailed here).
Line 17: The setElement() method is also responsible for ending the picking session by calling the endPickingSession() method of the navigation service INavigationService. Note that this latter method requires the currently active picking session as parameter.
Line 23: the pickingAborted() method is called by Modelio whenever the current picking operation has to terminate for any reason (for example the user canceled the picking interaction by pressing the ESC key, or he has activated another picking field somewhere else which has the main effect of aborting the current picking session to start a new one). Usually, this method only cares about ending the current picking session, but it might be used to carry out some additional cleanup if needed (resetting variables, freeing resources and so on).
Line 29: this setter accessor is used to store the picking session when it starts. The stored value will be used to end the picking session in the setElement method. The responsible for calling this accessor is the code that actullay starts the picking session (see below).

Branching the picking client to the gui

In a GUI, such a picking client object could typically be created and activated in the FocusListener of a Text field as shown in the following code fragment.

 1class MyTextFocusListener implements FocusListener {
 2    //...
 3    @Override
 4    public void focusGained(FocusEvent e) {
 5       // create and initialize a picking client instance
 6       MyPickingClient pickingClient = new MyPickingClient();
 7       
 8       // start the picking session, passing the picking client
 9       IPickingService pickingService = MyModule.getInstance().getModuleContext().getModelioServices().getPickingService();
10       IPickingSession pickSession = pickingService.startPickingSession( pickingClient );
11       pickingClient.setSession(pickSession);
12                
13    }
14    ...
15}

Line 4: The choice was made to activate the picking as soon as the GUI component gains the focus because this is simple to show in an example. In real life the activation condition might differ and be more sophisticated (detecting the start of the edition mode of a component for example).
Line 6: create a new picking client instance of the previously defined MyPickingClient class Line 10 start a picking session, passing this picking client instance to the picking service. The picking service returns the new started picking session.
Line 11: pass back the newly created picking session to the picking client. This is required for the client to be ble to close the picking session when needed.

So far we have seen how you can configure a GUI component to become a picking client, allowing quick and easy choice of a model element by the user. In Modelio, model elements can mainly be picked from the model browser or the diagrams. This is because these GUI parts are currently designed to be picking providers.
When developing a module and using the Modelio API picking services, you can also define any GUI component of your module to become picking provider

Implementing a picking provider

The reason why you may want your module to implement a picking provider, is that your module is somewhat displaying a set of model elements that are of interest for a user to be quickly and easily chosen. For example, the diagrams, which are great model elements displayer, are typical picking providers. A picking click in a diagram to designate an element is definitely a quick and easy interaction for a end-user. So when your module is displaying a valuable list of model elements, consider making of it a picking provider.

Being a picking provider implies several responsibilities which are defined by the interface IPickingProvider and several methods of the IPickingService. The overall process of implementing a picking provider consists in:

  • designing an implementation of the IPickingProvider
  • registering this provider in the picking service (typically at module start)
  • firing picking events according to the user’s mouse activity

The IPickingProvider interface

In most cases, a dedicated class will be in charge of these responsibilities. The methods of this interface are called by Modelio to notify your provider about the starting, respectively ending, of picking session. Remember that a picking session is started when the user activates a picking client (ie a gui component requiring the selection of a model element).

 1class MyPickingProvider implements IPickingProvider {
 2    private IPickingSession pickSession;
 3
 4    ...
 5    @Override
 6    public void enterPickingMode(IPickingSession session) {
 7        System.out.println("entering picking mode");
 8        this.pickSession = session;
 9        
10        //... code is really application specific here ...
11    }
12
13    @Override
14    public void leavePickingMode(IPickingSession session) {
15        System.out.println("leaving picking mode");
16        this.pickSession = null;
17
18        //... code is really application specific here, probably undoing what has been done in the enterPickingMode() method.
19    }
20    
21    
22    ...
23

Line 6: the enterPickingMode(IPickingSession session) method is called by Modelio when a picking session is started. From this moment, your picking provider must be able to fire proper picking events depending on the user mouse activity. You should also take care of animating the mouse cursor depending on these events.
For example, if your module is displaying a list of elements in a SWT List, you have to configure this list to fire picking events when the user hovers over its elements and when he selects a particular element in it.

Line 15: the leavePickingMode(IPickingSession session) method is called by Modelio when a picking session is ended whatever the reason. From this moment your provider must not fire any picking events anymore. Usually, actions carried out in this method consists more or less in un-doing what has been done in the enterPickingMode() method. In our example of a SWT list of elements previously configured to fire picking events, the leavePickingMode()would probably reconfigure the listeners of the SWT List so that no more events are sent.

Registering the provider

Once you have designed and implemented your picking provider, you have to register an instance of it to the picking service. Being registered, your provider will be called by Modelio each time a picking session is started or ended. The proper place to register your provider is typically the start method of your module, once your module GUI components are initialized, but other solutions are possible.

 1    ...
 2    
 3    MyPickingProvider pickingProvider = new MyPickingProvider(...);
 4    ...
 5    // Registering a picking provider
 6    IPickingService pickingService = MyModule.getInstance().getModuleContext().getModelioServices().getPickingService();
 7    pickingService.getPickingService().registerPickingProvider(pickingProvider );
 8    ...
 9    // Unregistering a picking provider
10    pickingService.unregisterPickingProvider(pickingProvider ); 
11    ...
12

The above code fragment is straightforward. Simply call the register/unregister methods of the picking service and you are done. Note, however, that you have to keep a reference the picking provider instance to be able to pass it back to the unregisterPickingProvider() method when unregistering it.

Firing picking events

Obviously, one of the main role of a picking provider is to animate the picking session by firing so-called picking events. There are only two kind of events:

  1. hovering events, which have to be fired while the mouse is hovering model elements. These events are transmitted by the picking service to the active picking client by calling its acceptElement() method.
    Hovering events are fired by calling the hoverElement() method of IPickingSession and passing the hovered element as parameter.
    The returned boolean is the value returned by the picking client acceptElement() method.
  2. selection events, which have to be fired when the user actually clicks on a hovered element. These events are transmitted by the picking service to the active picking client by calling its setElement() method. Selection events are fired by calling the selectElement() method of IPickingSession and passing the selected element as parameter.

Where and when firing events methods should be called highly depends on the application and the GUI component actually used to display and select elements. For a SWT List, a typical place to fire selection events is the ISelectionListener() of the List. Hovering the List is probably more tricky and may require to deal with MouseListener events.

A SWT Canvas based event firer could look like this:

 1class MyPickingEventFirer implements MouseListener {
 2    private IPickingSession pickSession;
 3    private Canvas swtCanvas;
 4
 5    ...
 6
 7    public void activate(IPickingSession pickSession) {
 8        this.pickSession = pickSession;
 9        this.swtCanvas.addMouseListener(this);
10    }
11
12    public void disable() {
13        this.pickSession = null;
14        this.swtCanvas.removeMouseListener(this);
15    }
16
17    @Override
18    public void mouseUp(MouseEvent e) {
19        if (e.button == 1) {
20            MObject selectedElement = ... // application specific
21            if (selectedElement != null && this.pickSession.hoverElement(selectedElement)) {
22                this.pickSession.selectElement(selectedElement);
23            }
24        }
25    }    
26

PickingSequenceDiagram.png (25.6 KB) Philippe Vlaemynck, 05 February 2016 13:59