A l'étape du billet précédant [step15] nous avons mis en place la palette GEF qui contient les 2 outils de création de state et d'actions. Dans ce billet nous allons ajouter l'outil de création de connections qui permettra à partir de la palette GEF de connecter les actions aux states :

Vous pouvez télécharger le projet org.example.workflow_step16.zip présenté dans ce billet.
Notre outil de création de connections s'utilisera en cliquant sur un noeud source (ex : state) puis sur un noeud cible (ex : action). Pour effectuer nos tests de l'outil de connection, ajoutez un state "s3" et une action "a2" dans le diagramme GEF que nous tenterons tout au long de ce billet de connecter via notre outil de création de connection :

Pour ajouter l'outil de connection entre states et actions, nous allons procéder de la même manière que ce que nous avons fait précédemment avec les outils de création de states et d'actions, autrement dit :
Récupérer les 2 icônes des connections que nous allons utiliser dans la palette GEF :
A cette étape nous allons ajouter l'outil de création de connections à notre palette GEF. Pour ajoutez dans la classe org.example.workflow.presentation.graphical.WorkflowEditorPaletteFactory la méthode WorkflowEditorPaletteFactory#createControlGroup() comme suit :
private static PaletteContainer createControlGroup() {
PaletteGroup controlGroup = new PaletteGroup("Control Group");
ToolEntry connectionTool = new ConnectionCreationToolEntry(
"Connection", "Creating connections", null,
ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj16/connection.gif")),
ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj24/connection.gif")));
controlGroup.add(connectionTool);
return controlGroup;
}
La nouvelle méthode WorkflowEditorPaletteFactory#createControlGroup() créé un groupe d'entrée de palettes org.eclipse.gef.palette.PaletteGroup qui est constitué à ce stade de l'outil de création de connections.
A la différence de PaletteDrawer, PaletteGroupe n'affiche pas une entrée avec un libéllé. L'outil de création de connections utilise la classe GEF org.eclipse.gef.palette.ConnectionCreationToolEntry où nous avons passé dans son constructeur :
ajoutez la PaletteGroupe créé dans la palette racine comme ceci :
...
PaletteRoot palette = new PaletteRoot();
palette.add(createControlGroup());
...}
Voici le code en entier de WorkflowEditorPaletteFactory :
package org.example.workflow.presentation.graphical;
import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
import org.eclipse.gef.palette.ConnectionCreationToolEntry;
import org.eclipse.gef.palette.CreationToolEntry;
import org.eclipse.gef.palette.PaletteContainer;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteGroup;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.requests.CreationFactory;
import org.example.workflow.model.WorkflowFactory;
import org.example.workflow.presentation.WorkflowEditorPlugin;
public class WorkflowEditorPaletteFactory {
private static PaletteContainer createComponentDrawer() {
PaletteDrawer componentsDrawer = new PaletteDrawer("Workflow");
// Creation tool of state
CreationToolEntry stateComponent = new CreationToolEntry("State",
"Create a state", new CreationFactory() {
public Object getNewObject() {
return WorkflowFactory.eINSTANCE.createStateType();
}
public Object getObjectType() {
return null;
}
}, ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj16/state.gif")),
ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj24/state.gif")));
componentsDrawer.add(stateComponent);
// Creation tool of action
CreationToolEntry actionComponent = new CreationToolEntry("action",
"Create a action", new CreationFactory() {
public Object getNewObject() {
return WorkflowFactory.eINSTANCE.createActionType();
}
public Object getObjectType() {
return null;
}
}, ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj16/action.gif")),
ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj24/action.gif")));
componentsDrawer.add(actionComponent);
return componentsDrawer;
}
private static PaletteContainer createControlGroup() {
PaletteGroup controlGroup = new PaletteGroup("Control Group");
ToolEntry connectionTool = new ConnectionCreationToolEntry(
"Connection", "Creating connections", null,
ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj16/connection.gif")),
ExtendedImageRegistry.INSTANCE
.getImageDescriptor(WorkflowEditorPlugin.INSTANCE
.getImage("full/obj24/connection.gif")));
controlGroup.add(connectionTool);
return controlGroup;
}
static PaletteRoot createPalette() {
PaletteRoot palette = new PaletteRoot();
palette.add(createControlGroup());
palette.add(createComponentDrawer());
return palette;
}
}
Relancez le plugin, et vous pourrez voir l'outil de création de connections s'afficher dans la palette :

Après avoir sélectionné l'outil de création de connections, cliquez sur le state "s3" pour intialiser la source de la connection. Mais rien ne se passe? Ce comportement est normal, car nous n'avons pas encore implémenté les 2 concepts :
Dans le billet précedant nous avons installé l'EditPolicy WorkflowLayoutEditPolicy dans l'EditPart WorkflowTypePart pour intepréter la Request GEF en Command GEF CreateCommand. Nous allons procéder de la même manière pour rendre opérationnel notre outil de création de connections mais cette fois nous allons installer un EditPolicy (WorkflowNodeEditPolicy) dans les EditPart StateTypePart et ActionTypePart car ce sont eux qui doivent interpréter la Request GEF (réagir lorsque la souris survole l'EditPart) en command GEF ConnectionCreateCommand, command qui permet de créer uen connection entre un state et une action.
Créez la classe org.example.workflow.presentation.graphical.policies.WorkflowNodeEditPolicy comme suit :
package org.example.workflow.presentation.graphical.policies;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy;
import org.eclipse.gef.requests.CreateConnectionRequest;
import org.eclipse.gef.requests.ReconnectRequest;
public class WorkflowNodeEditPolicy extends GraphicalNodeEditPolicy {
@Override
protected Command getConnectionCompleteCommand(
CreateConnectionRequest request) {
return null;
}
@Override
protected Command getConnectionCreateCommand(CreateConnectionRequest request) {
return null;
}
@Override
protected Command getReconnectSourceCommand(ReconnectRequest request) {
return null;
}
@Override
protected Command getReconnectTargetCommand(ReconnectRequest request) {
return null;
}
}
WorkflowNodeEditPolicy hérite de org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy car d'après la javadoc :
A GraphicalNodeEditPolicy is responsible for creating and reconnecting connections graphically.
cet EditPolicy est utilisé pour la création des connections (dans notre cas les Connection EMF).
ATTENTION!!! Supprimez la méthode createEditPolicies() dans les EditPart StateTypePart et ActionTypePart car nous allons implémenter l'installation des EditPolicy dans la classe commune ConnectableNodePart.
Modifier la classe ConnectableNodePart pour installer l'EditPolicy :
@Override
protected void createEditPolicies() {
installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new WorkflowNodeEditPolicy());
}
Relancez le plugin et mettez un point d'arrêt dans WorkflowNodeEditPolicy#getConnectionCreateCommand(CreateConnectionRequest request). Lorsque vous déplacerez la souris sur le state "s3", le debug s'arrêtera dans cette méthode. L'étape suivante est de développer cette commande via la classe ConnectionCreateCommand.
Cette Command GEF doit s'occuper de mettre à jour la connection entre un state et une action. Plus précisement :
Elle doit avant son exécution avoir les noeuds source et cible (target) qui doivent être initialisé correctement. Cette initialisation des source et target doit être géré par l'EditPolicy WorkflowNodeEditPolicy.
Créez la classe org.example.workflow.presentation.graphical.commands.ConnectionCreateCommand comme suit :
package org.example.workflow.presentation.graphical.commands;
import org.eclipse.gef.commands.Command;
import org.example.workflow.model.ActionType;
import org.example.workflow.model.ConnectableNode;
import org.example.workflow.model.StateType;
public class ConnectionCreateCommand extends Command {
private ConnectableNode source;
private ConnectableNode target;
@Override
public void execute() {
if (source instanceof StateType) {
StateType state = (StateType) source;
ActionType action = (ActionType) target;
action.setFromState(state);
} else {
StateType state = (StateType) target;
ActionType action = (ActionType) source;
action.setToState(state);
}
}
public void setSource(ConnectableNode source) {
this.source = source;
}
public void setTarget(ConnectableNode target) {
this.target = target;
}
}
Cette Command GEF doit avoir ces noeuds ConnectableNode source et target initialisés (par l'EditPolicy) avant de pouvoir être éxécuté. Comme vous pouvez le constater la mise à jour des connections s'effectuent en modifiant les propriétés ActionType#setToState(StateType state) et ActionType#setFromState(StateType state). Nous n'avons pas besoin de Connection EMF ici pour lié une action à un state (ce sont nos Adapter EMF qui gèrent ceci). C'est pour cela que notre outil de création de connections n'a pas besoin de factory CreationFactory.
Notre EditPolicy WorkflowNodeEditPolicy doit maintenant s'occuper de préparer la Command GEF ConnectionCreateCommand, autrement dit :
Modifiez le code de WorkflowNodeEditPolicy comme suit :
package org.example.workflow.presentation.graphical.policies;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy;
import org.eclipse.gef.requests.CreateConnectionRequest;
import org.eclipse.gef.requests.ReconnectRequest;
import org.example.workflow.model.ConnectableNode;
import org.example.workflow.presentation.graphical.commands.ConnectionCreateCommand;
public class WorkflowNodeEditPolicy extends GraphicalNodeEditPolicy {
@Override
protected Command getConnectionCompleteCommand(
CreateConnectionRequest request) {
ConnectionCreateCommand cmd = (ConnectionCreateCommand) request
.getStartCommand();
cmd.setTarget(getConnectableNode());
return cmd;
}
@Override
protected Command getConnectionCreateCommand(CreateConnectionRequest request) {
ConnectionCreateCommand cmd = new ConnectionCreateCommand();
cmd.setSource(getConnectableNode());
request.setStartCommand(cmd);
return cmd;
}
private ConnectableNode getConnectableNode() {
return (ConnectableNode) getHost().getModel();
}
@Override
protected Command getReconnectSourceCommand(ReconnectRequest request) {
return null;
}
@Override
protected Command getReconnectTargetCommand(ReconnectRequest request) {
return null;
}
}
La méthode WorkflowNodeEditPolicy#getConnectionCreateCommand(CreateConnectionRequest request) s'occupe d'instancier une Command GEF ConnectionCreateCommand et d'initialiser la source. Le code :
request.setStartCommand(cmd);
permet d'enregistrer la command GEF dans la Request GEF. La méthode WorkflowNodeEditPolicy#getConnectionCompleteCommand(CreateConnectionRequest request) peut ensuite récupérer l'instance ConnectionCreateCommand via le code :
ConnectionCreateCommand cmd = (ConnectionCreateCommand) request.getStartCommand();
et peut ensuite s'occuper de mettre à jour la target.
Relancez le plugin, il est maintenant possible de lier le state "s3" avec l'action "a2" à l'aide de notre outil de création de connections :

A ce state, l'utilisateur peut tenter de lier un noeud source (state/action) avec lui même et peut aussi tenter de lier 2 states entre eux ou 2 actions entre elles. Même si il tente d'effectuer ceci, ceci ne marchera pas car la méthode ConnectionCreateCommand#execute() s'attend à avoir un state (source/target) et une action(source/target). Il est cependant possible d'effectuer un test de validité des 2 noeuds à lier avant l'appel de la méthode ConnectionCreateCommand#execute() en implémentant la méthode ConnectionCreateCommand#canExecute() (qui retourne true par défaut). Les avantages d'effectuer ce test de validité des 2 noeuds dans cette méthode sont :
Ajoutez la méthode ConnectionCreateCommand#canExecute() dans la classe ConnectionCreateCommand comme suit :
@Override
public boolean canExecute() {
// 1) source & target cannot be the same node
if (source.equals(target))
return false;
// 2) Target & Source cannot be StateType & ActionType
if (target != null && source.getClass().equals((target.getClass())))
return false;
if (target instanceof ActionType) {
// 3) action target must not have target connections
ActionType t = (ActionType) target;
if (t.getTargetConnections().size() > 0)
return false;
} else {
if (source instanceof ActionType) {
// 4) action source must not have source connections
ActionType t = (ActionType) source;
if (t.getSourceConnections().size() > 0)
return false;
}
}
// 5) Check for existence of connection already
List<Connection> transistions = source.getTargetConnections();
for (Connection connection : transistions) {
if (connection.getTarget().equals(target))
return false;
}
return true;
}
Relancez le plugin et essayer de lier 2 states entre eux, le curseur de la sourit affichera un ron barré qui vous indique qu'il est impossible de lier 2 states entre eux.
// 5) Check for existence of connection already
List transistions = source.getSourceConnections();
for (Connection connection : transistions) {
if (connection.getTarget().equals(target))
return false;
}
Vous devez être identifié pour poster un commentaire.
| Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 |
Copyright © 2000-2012 - www.developpez.com