Tutorial: GEF (Graphical Editing Framework) [Part 4]
Par Jean Charles MAMMANA, vendredi 25 mai 2007 à 16:40 :: Programmation :: #10 :: rss
Maintenant qu'on peut agir sur notre graph, on va ajouter des fonctionnalités de gestion. Comme la gestion de l'undo/redo géré entièrement par GEF ainsi que la suppression de composants.
Dans un premier temps, il va falloir ajouter une toolbar à notre éditeur qui contiendra les boutons des actions qu'on souhaite implémenter.
Pour ajouter une toolbar à notre éditeur, il faut lui ajouter un Contributor. On va créer une class qu'on va nommer MyGraphicalEditorActionBarContributor qui héritera simplement d'ActionBarContributor.
Maintenant on édite plugin.xml. Dans l'onglet Extensions, on donne le chemin de notre class dans contributorClass pour notre éditeur.
Maintenant il ne reste plus qu'à afficher notre toolbar (vide). Pour cela, dans la méthode preWindowOpen(); de la class ApplicationWorkbenchWindowAdvisor, il faut ajouter (à la fin)
On va ajouter la gestion de l'undo/redo dans notre éditeur.
Dans la class MyGraphicalEditorActionBarContributor, on ajoute ceci :
Il ne reste plus qu'a configurer les commandes qui nous intéressent, c'est à dire ServiceChangeLayoutCommand et EmployeChangeLayoutCommand :
Lancez le programme.. Vous avez les deux flèches d'undo/redo :)
On va maintenant ajouter la suppression. On veut pouvoir supprimer un service complet ou juste un employé.
Le principe est sensiblement le même que pour l'undo/redo :
- On ajoute le contrôle dans la toolbar.
- On crée la commande.
- On crée une nouvelle policy qui utilise la nouvelle commande.
- On applique cette policy dans ServicePart et EmployePart.
- On on lève un évent dans addChild() et removeChild() dans Node puis on update la vue en faisant un refreshChildren() dans l'editpart de entreprise et service.
Dans MyGraphicalEditorActionBarContributor, on ajoute l'action et le bouton delete :
La commande est toute simple, voici le code :
Puis on crée la policy associé.
On remarque que le principe est le même que celui de l'undo/redo..
On installe cette policy ServicePart et EmployePart :
On crée une nouvelle proprieté dans la class Node puis on leve un event de cette proprieté pour addchild et removechild :
Il ne reste plus qu'a reactualiser la vue quand le modele à changé :
La boucle est bouclé. Vous pouvez lancer le programme et admirer le résultat...
Vous pouvez télécharger le projet ICI
Pour ajouter une toolbar à notre éditeur, il faut lui ajouter un Contributor. On va créer une class qu'on va nommer MyGraphicalEditorActionBarContributor qui héritera simplement d'ActionBarContributor.
package tutogef;
import org.eclipse.gef.ui.actions.ActionBarContributor;
public class MyGraphicalEditorActionBarContributor extends ActionBarContributor {
@Override
protected void buildActions() {
}
@Override
protected void declareGlobalActionKeys() {
}
}
import org.eclipse.gef.ui.actions.ActionBarContributor;
public class MyGraphicalEditorActionBarContributor extends ActionBarContributor {
@Override
protected void buildActions() {
}
@Override
protected void declareGlobalActionKeys() {
}
}
Maintenant on édite plugin.xml. Dans l'onglet Extensions, on donne le chemin de notre class dans contributorClass pour notre éditeur.

Maintenant il ne reste plus qu'à afficher notre toolbar (vide). Pour cela, dans la méthode preWindowOpen(); de la class ApplicationWorkbenchWindowAdvisor, il faut ajouter (à la fin)
configurer.setShowCoolBar(true);
On va ajouter la gestion de l'undo/redo dans notre éditeur.
Dans la class MyGraphicalEditorActionBarContributor, on ajoute ceci :
public class MyGraphicalEditorActionBarContributor extends ActionBarContributor {
@Override
protected void buildActions() {
addRetargetAction(new UndoRetargetAction());
addRetargetAction(new RedoRetargetAction());
}
(...)
public void contributeToToolBar(IToolBarManager toolBarManager) {
toolBarManager.add(getAction(ActionFactory.UNDO.getId()));
toolBarManager.add(getAction(ActionFactory.REDO.getId()));
}
}
Ca à pour but d'ajouter les contrôles d'undo/redo dans notre bar et de leur configurer leur action (géré par GEF).@Override
protected void buildActions() {
addRetargetAction(new UndoRetargetAction());
addRetargetAction(new RedoRetargetAction());
}
(...)
public void contributeToToolBar(IToolBarManager toolBarManager) {
toolBarManager.add(getAction(ActionFactory.UNDO.getId()));
toolBarManager.add(getAction(ActionFactory.REDO.getId()));
}
}
Il ne reste plus qu'a configurer les commandes qui nous intéressent, c'est à dire ServiceChangeLayoutCommand et EmployeChangeLayoutCommand :
public class EmployeChangeLayoutCommand extends AbstractLayoutCommand {
(...)
private Rectangle oldLayout;
(...)
public void setModel(Object model) {
this.model = (Employe)model;
// On sauvegarde l'ancien layout avant de le changer.
this.oldLayout = ((Employe)model).getLayout();
}
// Méthode hérité appelé lors de l'action undo.
public void undo() {
this.model.setLayout(this.oldLayout);
}
}
(...)
private Rectangle oldLayout;
(...)
public void setModel(Object model) {
this.model = (Employe)model;
// On sauvegarde l'ancien layout avant de le changer.
this.oldLayout = ((Employe)model).getLayout();
}
// Méthode hérité appelé lors de l'action undo.
public void undo() {
this.model.setLayout(this.oldLayout);
}
}
public class ServiceChangeLayoutCommand extends AbstractLayoutCommand {
(...)
private Rectangle oldLayout;
(...)
public void setModel(Object model) {
this.model = (Service)model;
// On sauvegarde l'ancien layout avant de le changer.
this.oldLayout = ((Service)model).getLayout();
}
// Méthode hérité appelé lors de l'action undo.
public void undo() {
this.model.setLayout(this.oldLayout);
}
}
(...)
private Rectangle oldLayout;
(...)
public void setModel(Object model) {
this.model = (Service)model;
// On sauvegarde l'ancien layout avant de le changer.
this.oldLayout = ((Service)model).getLayout();
}
// Méthode hérité appelé lors de l'action undo.
public void undo() {
this.model.setLayout(this.oldLayout);
}
}
Lancez le programme.. Vous avez les deux flèches d'undo/redo :)

On va maintenant ajouter la suppression. On veut pouvoir supprimer un service complet ou juste un employé.
Le principe est sensiblement le même que pour l'undo/redo :
- On ajoute le contrôle dans la toolbar.
- On crée la commande.
- On crée une nouvelle policy qui utilise la nouvelle commande.
- On applique cette policy dans ServicePart et EmployePart.
- On on lève un évent dans addChild() et removeChild() dans Node puis on update la vue en faisant un refreshChildren() dans l'editpart de entreprise et service.
Dans MyGraphicalEditorActionBarContributor, on ajoute l'action et le bouton delete :
public class MyGraphicalEditorActionBarContributor extends ActionBarContributor {
@Override
protected void buildActions() {
(...)
addRetargetAction(new DeleteRetargetAction());
}
(...)
public void contributeToToolBar(IToolBarManager toolBarManager) {
(...)
toolBarManager.add(getAction(ActionFactory.DELETE.getId()));
}
@Override
protected void buildActions() {
(...)
addRetargetAction(new DeleteRetargetAction());
}
(...)
public void contributeToToolBar(IToolBarManager toolBarManager) {
(...)
toolBarManager.add(getAction(ActionFactory.DELETE.getId()));
}
La commande est toute simple, voici le code :
package tutogef.commands;
import org.eclipse.gef.commands.Command;
import tutogef.model.Node;
public class DeleteCommand extends Command{
private Node model;
private Node parentModel;
public void execute() {
this.parentModel.removeChild(model);
}
public void setModel(Object model) {
this.model = (Node)model;
}
public void setParentModel(Object model) {
parentModel = (Node)model;
}
public void undo() {
this.parentModel.addChild(model);
}
}
import org.eclipse.gef.commands.Command;
import tutogef.model.Node;
public class DeleteCommand extends Command{
private Node model;
private Node parentModel;
public void execute() {
this.parentModel.removeChild(model);
}
public void setModel(Object model) {
this.model = (Node)model;
}
public void setParentModel(Object model) {
parentModel = (Node)model;
}
public void undo() {
this.parentModel.addChild(model);
}
}
Puis on crée la policy associé.
package tutogef.editpolicies;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.ComponentEditPolicy;
import org.eclipse.gef.requests.GroupRequest;
import tutogef.commands.DeleteCommand;
public class AppDeletePolicy extends ComponentEditPolicy{
protected Command createDeleteCommand(GroupRequest deleteRequest) {
DeleteCommand command = new DeleteCommand();
command.setModel(getHost().getModel());
command.setParentModel(getHost().getParent().getModel());
return command;
}
}
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.ComponentEditPolicy;
import org.eclipse.gef.requests.GroupRequest;
import tutogef.commands.DeleteCommand;
public class AppDeletePolicy extends ComponentEditPolicy{
protected Command createDeleteCommand(GroupRequest deleteRequest) {
DeleteCommand command = new DeleteCommand();
command.setModel(getHost().getModel());
command.setParentModel(getHost().getParent().getModel());
return command;
}
}
On remarque que le principe est le même que celui de l'undo/redo..
On installe cette policy ServicePart et EmployePart :
protected void createEditPolicies() {
(...)
installEditPolicy(EditPolicy.COMPONENT_ROLE,new AppDeletePolicy());
}
(...)
installEditPolicy(EditPolicy.COMPONENT_ROLE,new AppDeletePolicy());
}
On crée une nouvelle proprieté dans la class Node puis on leve un event de cette proprieté pour addchild et removechild :
public class Node {
(...)
public static final String PROPERTY_ADD = "NodeAddChild";
public static final String PROPERTY_REMOVE = "NodeRemoveChild";
(...)
public boolean addChild(Node child) {
boolean b = this.children.add(child);
if (b) {
child.setParent(this);
getListeners().firePropertyChange(PROPERTY_ADD, null, child);
}
return b;
}
(...)
public boolean removeChild(Node child) {
boolean b = this.children.remove(child);
if (b)
getListeners().firePropertyChange(PROPERTY_REMOVE, child, null);
return b;
}
(...)
public static final String PROPERTY_ADD = "NodeAddChild";
public static final String PROPERTY_REMOVE = "NodeRemoveChild";
(...)
public boolean addChild(Node child) {
boolean b = this.children.add(child);
if (b) {
child.setParent(this);
getListeners().firePropertyChange(PROPERTY_ADD, null, child);
}
return b;
}
(...)
public boolean removeChild(Node child) {
boolean b = this.children.remove(child);
if (b)
getListeners().firePropertyChange(PROPERTY_REMOVE, child, null);
return b;
}
Il ne reste plus qu'a reactualiser la vue quand le modele à changé :
public class EntreprisePart extends AppAbstractEditPart{
(...)
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Node.PROPERTY_ADD)) refreshChildren();
if (evt.getPropertyName().equals(Node.PROPERTY_REMOVE)) refreshChildren();
}
}
(...)
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Node.PROPERTY_ADD)) refreshChildren();
if (evt.getPropertyName().equals(Node.PROPERTY_REMOVE)) refreshChildren();
}
}
public class ServicePart extends AppAbstractEditPart {
(...)
@Override
public void propertyChange(PropertyChangeEvent evt) {
(...)
if (evt.getPropertyName().equals(Node.PROPERTY_ADD)) refreshChildren();
if (evt.getPropertyName().equals(Node.PROPERTY_REMOVE)) refreshChildren();
}
(...)
@Override
public void propertyChange(PropertyChangeEvent evt) {
(...)
if (evt.getPropertyName().equals(Node.PROPERTY_ADD)) refreshChildren();
if (evt.getPropertyName().equals(Node.PROPERTY_REMOVE)) refreshChildren();
}
La boucle est bouclé. Vous pouvez lancer le programme et admirer le résultat...

Vous pouvez télécharger le projet ICI

Commentaires
1. Le dimanche 27 mai 2007 à 14:40, par Stév
2. Le dimanche 27 mai 2007 à 18:06, par Psykokwak
3. Le mardi 29 mai 2007 à 12:57, par Chris
4. Le mercredi 23 avril 2008 à 21:18, par pyonpyon
Ajouter un commentaire