Pour cela, nous creerons les éléments suivants:
  • 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.

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);
          }
}

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

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

... 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);
        }

... puis dans le menu global de l'application (dans MyGraphicalEditorActionBarContributor).
        public void contributeToMenu(IMenuManager menuManager) {
        // 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;
        }
}

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

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 :)



Vous pouvez télécharger le résultat ICI
Tutorial rédigé par Jonathan Gramain