Tutorial: GEF (Graphical Editing Framework) [Part 9]
Par Jean Charles MAMMANA, lundi 18 juin 2007 à 16:38 :: Programmation :: #20 :: rss
Nous avons vu précédemment que nous pouvions ajouter des EditPolicy a un EditPart, mais celles-ci
ne faisaient qu'utiliser les actions deja prévues par GEF (Par exemple, supprimer un composant).
Nous allons maintenant créer notre propre action et l'ajouter dans un EditPolicy ainsi qu'au menu contextuel de l'application. Disons que nous voulons pouvoir renommer les services mais pas les employées ni l'entreprise.
Nous allons maintenant créer notre propre action et l'ajouter dans un EditPolicy ainsi qu'au menu contextuel de l'application. Disons que nous voulons pouvoir renommer les services mais pas les employées ni l'entreprise.
Pour cela, nous creerons les éléments suivants:
N'oublions pas qu'une fois que l'action est créée, il faut l'enregistrer dans l'ActionRegistry de notre MyGraphicalEditor...
... et specifier son raccourci dans le menu contextuel de l'editeur (en recuperant l'action globale grace a son identifiant Eclipse dans AppContextMenuProvider)...
... puis dans le menu global de l'application (dans MyGraphicalEditorActionBarContributor).
Cette classe reprend le principe de org.eclipse.gef.editpolicies.ComponentEditPolicy pour créer une commande de renommage : elle verifie le type de la requete, puis elle crée la commande correspondante (en se servant des extended data pour recuperer le nouveau nom).


Vous pouvez télécharger le résultat ICI
Tutorial rédigé par Jonathan Gramain
- Un Wizard pour demander le nouveau nom a l'utilisateur
- Un Command qui effectuera le renommage, et sera donc intégré a l'undo/redo
- Une Action qui lancera le wizard et créera une requête
- Un EditPolicy pour générer la commande depuis la requête de renommage, et qui pourra éventuellement être complète pour d'autres opérations d'édition
Création du Wizard
Ce n'est pas vraiment du GEF, mais les wizards (assistants) sont un élément tres utile dans Eclipse et tres personnalisable, c'est pourquoi nous allons en créer un ici pour la simple tache de demander un nouveau nom a l'utilisateur. Le Wizard ne contiendra qu'un Label et un champ de saisie Text pour demander a l'utilisateur le nouveau nom, tout en affichant l'ancien dans le champ de saisie. Le Wizard est en fait un conteneur de IWizardPage, chacune représentant une étape du déroulement de l'assistant. Notre wizard ne contiendra en fait qu'une seule page, que nous implémentons en créant une classe héritant de WizardPage.public class RenameWizard extends Wizard {
private class RenamePage extends WizardPage {
public Text nameText;
public RenamePage(String pageName) {
super(pageName);
setTitle("Rename");
setDescription("Rename a component");
}
@Override
public void createControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
Label lab = new Label(composite, SWT.NONE);
lab.setText("Rename to: ");
nameText = new Text(composite, SWT.NONE);
nameText.setText(oldName);
RowLayout l = new RowLayout();
composite.setLayout(l);
setControl(composite);
}
}
private String oldName;
private String newName;
public RenameWizard(String oldName) {
this.oldName = oldName;
this.newName = null;
addPage(new RenamePage("MyRenamePage"));
}
@Override
public boolean performFinish() {
RenamePage page = (RenamePage)getPage("MyRenamePage");
if (page.nameText.getText().isEmpty()) {
page.setErrorMessage("Le champ nom est vide!");
return false;
}
newName = page.nameText.getText();
return true;
}
public String getRenameValue() {
return newName;
}
}
La méthode performFinish() est appelée quand l'utilisateur appuie sur le bouton Finish.
Si elle renvoie true, l'assistant se termine avec succès, sinon il reste bloque sur la même page
en attendant que l'utilisateur corrige son erreur.
private class RenamePage extends WizardPage {
public Text nameText;
public RenamePage(String pageName) {
super(pageName);
setTitle("Rename");
setDescription("Rename a component");
}
@Override
public void createControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
Label lab = new Label(composite, SWT.NONE);
lab.setText("Rename to: ");
nameText = new Text(composite, SWT.NONE);
nameText.setText(oldName);
RowLayout l = new RowLayout();
composite.setLayout(l);
setControl(composite);
}
}
private String oldName;
private String newName;
public RenameWizard(String oldName) {
this.oldName = oldName;
this.newName = null;
addPage(new RenamePage("MyRenamePage"));
}
@Override
public boolean performFinish() {
RenamePage page = (RenamePage)getPage("MyRenamePage");
if (page.nameText.getText().isEmpty()) {
page.setErrorMessage("Le champ nom est vide!");
return false;
}
newName = page.nameText.getText();
return true;
}
public String getRenameValue() {
return newName;
}
}
Création de la commande
Nous nous retrouvons a nouveau dans les meandres de GEF. Nous allons créer une Command qui exécutera le renommage proprement dit. Comme pour l'implémentation de DeleteCommand, nous implémentons les méthodes execute() et undo() de la commande. On sauvegarde l'ancien et le nouveau nom dans la commande pour pouvoir les rétablir lors de l'undo/redo.public class RenameCommand extends Command{
private Node model;
private String oldName;
private String newName;
public void execute() {
this.oldName = model.getName();
this.model.setName(newName);
}
public void setModel(Object model) {
this.model = (Node)model;
}
public void setNewName(String newName) {
this.newName = newName;
}
public void undo() {
this.model.setName(oldName);
}
}
private Node model;
private String oldName;
private String newName;
public void execute() {
this.oldName = model.getName();
this.model.setName(newName);
}
public void setModel(Object model) {
this.model = (Node)model;
}
public void setNewName(String newName) {
this.newName = newName;
}
public void undo() {
this.model.setName(oldName);
}
}
Création de l'action
Maintenant que nous pouvons effectuer l'opération de renommage et que nous pouvons demander un nom a l'utilisateur, il faut creer une action pour lancer l'assistant et créer la commande. L'action que nous créons s'appelle RenameAction. Lors de son execution, elle va créer une Request GEF possédant le type "rename" (un nouveau type crée pour l'occasion), et on se sert de ses "extended data" (une table de correspondances Object vers Object) pour sauvegarder le nouveau nom dans la requête (on choisit comme clef "newName" et sa valeur, le nouveau nom). Le type "rename" sera reconnu par notre EditPolicy, ce que nous verrons plus loin. Enfin, on appelle getCommand() sur le premier EditPart sélectionné, pour demander a GEF de traiter la requête grace a ses EditPolicies et de renvoyer la commande generée (qui peut être constituée de plusieurs commandes chainées, mais qui ici sera une simple commande de renommage).public class RenameAction extends SelectionAction {
public RenameAction(IWorkbenchPart part) {
super(part);
setLazyEnablementCalculation(false);
}
protected void init() {
setText("Rename...");
setToolTipText("Rename");
// On spécifie l'identifiant utilise pour associer cette action a l'action globale de renommage
// intégrée a Eclipse
setId(ActionFactory.RENAME.getId());
// Ajout d'une icone pour l'action. N'oubliez pas d'ajouter une icone dans le dossier "icones"
// du plugin :)
ImageDescriptor icon = AbstractUIPlugin.imageDescriptorFromPlugin("TutoGEF", "icons/rename-icon.png");
if (icon != null)
setImageDescriptor(icon);
setEnabled(false);
}
@Override
protected boolean calculateEnabled() {
// On laisse les EditPolicy decider si la commande est disponible ou non
Command cmd = createRenameCommand("");
if (cmd == null)
return false;
return true;
}
public Command createRenameCommand(String name) {
Request renameReq = new Request("rename");
HashMap<String, String> reqData = new HashMap<String, String>();
reqData.put("newName", name);
renameReq.setExtendedData(reqData);
EditPart object = (EditPart)getSelectedObjects().get(0);
Command cmd = object.getCommand(renameReq);
return cmd;
}
public void run() {
Node node = getSelectedNode();
RenameWizard wizard = new RenameWizard(node.getName());
WizardDialog dialog = new WizardDialog(getWorkbenchPart().getSite().getShell(), wizard);
dialog.create();
dialog.getShell().setSize(400, 180);
dialog.setTitle("Rename wizard");
dialog.setMessage("Rename");
if (dialog.open() == WizardDialog.OK) {
String name = wizard.getRenameValue();
execute(createRenameCommand(name));
}
}
// Helper
private Node getSelectedNode() {
List objects = getSelectedObjects();
if (objects.isEmpty())
return null;
if (!(objects.get(0) instanceof EditPart))
return null;
EditPart part = (EditPart)objects.get(0);
return (Node)part.getModel();
}
}
public RenameAction(IWorkbenchPart part) {
super(part);
setLazyEnablementCalculation(false);
}
protected void init() {
setText("Rename...");
setToolTipText("Rename");
// On spécifie l'identifiant utilise pour associer cette action a l'action globale de renommage
// intégrée a Eclipse
setId(ActionFactory.RENAME.getId());
// Ajout d'une icone pour l'action. N'oubliez pas d'ajouter une icone dans le dossier "icones"
// du plugin :)
ImageDescriptor icon = AbstractUIPlugin.imageDescriptorFromPlugin("TutoGEF", "icons/rename-icon.png");
if (icon != null)
setImageDescriptor(icon);
setEnabled(false);
}
@Override
protected boolean calculateEnabled() {
// On laisse les EditPolicy decider si la commande est disponible ou non
Command cmd = createRenameCommand("");
if (cmd == null)
return false;
return true;
}
public Command createRenameCommand(String name) {
Request renameReq = new Request("rename");
HashMap<String, String> reqData = new HashMap<String, String>();
reqData.put("newName", name);
renameReq.setExtendedData(reqData);
EditPart object = (EditPart)getSelectedObjects().get(0);
Command cmd = object.getCommand(renameReq);
return cmd;
}
public void run() {
Node node = getSelectedNode();
RenameWizard wizard = new RenameWizard(node.getName());
WizardDialog dialog = new WizardDialog(getWorkbenchPart().getSite().getShell(), wizard);
dialog.create();
dialog.getShell().setSize(400, 180);
dialog.setTitle("Rename wizard");
dialog.setMessage("Rename");
if (dialog.open() == WizardDialog.OK) {
String name = wizard.getRenameValue();
execute(createRenameCommand(name));
}
}
// Helper
private Node getSelectedNode() {
List objects = getSelectedObjects();
if (objects.isEmpty())
return null;
if (!(objects.get(0) instanceof EditPart))
return null;
EditPart part = (EditPart)objects.get(0);
return (Node)part.getModel();
}
}
N'oublions pas qu'une fois que l'action est créée, il faut l'enregistrer dans l'ActionRegistry de notre MyGraphicalEditor...
public void createActions() {
super.createActions();
ActionRegistry registry = getActionRegistry();
IAction action = new RenameAction(this);
registry.registerAction(action);
getSelectionActions().add(action.getId());
}
super.createActions();
ActionRegistry registry = getActionRegistry();
IAction action = new RenameAction(this);
registry.registerAction(action);
getSelectionActions().add(action.getId());
}
... et specifier son raccourci dans le menu contextuel de l'editeur (en recuperant l'action globale grace a son identifiant Eclipse dans AppContextMenuProvider)...
public void buildContextMenu(IMenuManager menu) {
IAction action;
// ...
action = getActionRegistry().getAction(ActionFactory.RENAME.getId());
menu.appendToGroup(GEFActionConstants.GROUP_EDIT, action);
}
IAction action;
// ...
action = getActionRegistry().getAction(ActionFactory.RENAME.getId());
menu.appendToGroup(GEFActionConstants.GROUP_EDIT, action);
}
... puis dans le menu global de l'application (dans MyGraphicalEditorActionBarContributor).
public void contributeToMenu(IMenuManager menuManager) {
// TODO
}
// TODO
}
Creation de l'EditPolicy
Nous avons la commande qui effectue le renommage proprement dit, et l'action qui est capable de creer une requete pour recuperer cette commande depuis les EditPolicies. Il reste a ajouter une EditPolicy pour comprendre la nouvelle requete et creer la commande de renommage. Nous allons creer une nouvelle EditPolicy, nommee AppRenamePolicy, qui héritera directement de AbstractEditPolicy car nous allons definir nous-meme la methode getCommand().public class AppRenamePolicy extends AbstractEditPolicy{
public Command getCommand(Request request) {
if (request.getType().equals("rename"))
return createRenameCommand(request);
return null;
}
protected Command createRenameCommand(Request renameRequest) {
RenameCommand command = new RenameCommand();
command.setModel(getHost().getModel());
command.setNewName((String)renameRequest.getExtendedData().get("newName"));
return command;
}
}
public Command getCommand(Request request) {
if (request.getType().equals("rename"))
return createRenameCommand(request);
return null;
}
protected Command createRenameCommand(Request renameRequest) {
RenameCommand command = new RenameCommand();
command.setModel(getHost().getModel());
command.setNewName((String)renameRequest.getExtendedData().get("newName"));
return command;
}
}
Cette classe reprend le principe de org.eclipse.gef.editpolicies.ComponentEditPolicy pour créer une commande de renommage : elle verifie le type de la requete, puis elle crée la commande correspondante (en se servant des extended data pour recuperer le nouveau nom).
Association de la nouvelle EditPolicy aux EditPart
Il reste a specifier lesquels de nos EditPart auront le privilege de pouvoir etre renommes. Nous installons le nouvel EditPolicy dans ServicePart et ServiceTreePart (une ligne a ajouter dans chacun des fichiers correspondant pour offrir le renommage des services dans la vue graphique et dans l'arbre).protected void createEditPolicies() {
// ...
installEditPolicy(EditPolicy.NODE_ROLE, new AppRenamePolicy());
}
// ...
installEditPolicy(EditPolicy.NODE_ROLE, new AppRenamePolicy());
}
Dernière touche, activation d'une propriété pour mettre a jour les vues
Le renommage est maintenant fonctionnel, mais les vues n'ont aucun moyen de savoir qu'elles doivent se mettre a jour. Pour cela, nous allons creer une nouvelle propriete dans Node que nous allons activer lorsque le nouveau nom changera, et les methodes propertyChange() des EditPart concernes seront a l'ecoute de cette nouvelle propriete pour se mettre a jour automatiquement. En code, cela donne:- Dans Node:
// Declarations...
public static final String PROPERTY_RENAME = "NodeRename";
// ...
public void setName(String name) {
String oldName = this.name;
this.name = name;
getListeners().firePropertyChange(PROPERTY_RENAME, oldName, this.name);
}
- Dans ServicePart et ServiceTreeEditPart:
public void propertyChange(PropertyChangeEvent evt) {
// ...
if (evt.getPropertyName().equals(Node.PROPERTY_RENAME)) refreshVisuals();
}
Enjoy :)


Tutorial rédigé par Jonathan Gramain

Commentaires
1. Le mardi 19 juin 2007 à 17:27, par hebus
2. Le mardi 19 juin 2007 à 18:10, par Psykokwak
3. Le vendredi 29 juin 2007 à 10:21, par Stévan
4. Le vendredi 29 juin 2007 à 12:25, par Psykokwak
5. Le lundi 02 juillet 2007 à 08:33, par lyonnais
6. Le vendredi 28 septembre 2007 à 18:00, par SosMicro
7. Le vendredi 28 septembre 2007 à 20:03, par Prof
8. Le vendredi 28 septembre 2007 à 23:27, par SosMicro
9. Le samedi 29 septembre 2007 à 00:56, par Prof
10. Le mercredi 19 août 2009 à 11:36, par JeDecouvreLOrdinateur
Ajouter un commentaire