septembre
2009
A l’étape du billet précédant [step18] nous avons mis en place les fonctionnalité de suppression de state, d’actions et connections dans la page Graphics GEF. A ce stade nous avons des outils de la palette GEF et des actions GEF qui permettent de mettre à jour le modèle EMF via des Command GEF. Plus exactement ce sont les EditPolciy installés sur chacun des EditPart GEF qui réagissent aux outils/actions et qui interprètent les Request GEF en Command GEF. Les EditPolicy pourrait mettre à jour directement le modlèle EMF sans passer par des Command GEF. Pourquoi utiliser des Command GEF? L’editor GEF GaaphicalEditor gère en interne une stack de Command GEF (GraphicalEditor#getEditDomain()#getCommandStack()) qui dépile/empile les Command GEF executées dans l’editor via les outils des palettes et les Actions GEF.
Cette stack de Command GEF permet de gérer le undo/redo dans l’editor GEF en appelant pour chaque action GEF UndoAction/RedoAction la méthode Command#undo()/redo() de la dernière Command GEF exécutée. A ce stade nous n’avons pas implémentées ces 2 méthodes dans nos Command GEF. Dans ce billet nous allons les implémenter pour gérer le undo/redo :
- après avoir ajouté un state/action via l’outil de création de state/action en implémentant les méthodes undo/redo de la command GEF CreateCommand
- après avoir créé une connection entre un state/action via l’outil de création de connections en implémentant les méthodes undo/redo de la command GEF ConnectionCreateCommand
- après avoir supprimé un state/action via l’action GEF DeleteAction en implémentant les méthodes undo/redo de la command GEF DeleteCommand
- après avoir supprimé une connection via l’action GEF DeleteAction en implémentant les méthodes undo/redo de la command GEF DeleteConnectionCommand
Les fonctionnalités undo/redo seront accéssibles via :
- le menu contextuel de la page GEF Graphics avec les entrée de menu « Undo » et « Redo ».
- le menu global « Edit » avec les entrée de menu « Undo » et « Redo ».
- la combinaison de touches « Ctrl+Z » et « Ctrl+Y »
Vous pouvez télécharger le projet org.example.workflow_step19.zip présenté dans ce billet.
GEF UndoAction/RedoAction
GEF installe par défaut les actions org.eclipse.gef.ui.actions.UndoAction et org.eclipse.gef.ui.actions.RedoAction dans son registry d’actions, qui permet d’appeler via la CommandStack de l’editor, les méthodes undo/redo de la dernière Command GEF exécutée. Voici le code de la méthode UndoAction#run() :
getCommandStack().undo();
}
Et voici la méthode CommandStack#undo() :
//Assert.isTrue(canUndo());
Command command = (Command)undoable.pop();
notifyListeners(command, PRE_UNDO);
try {
command.undo();
redoable.push(command);
notifyListeners();
} finally {
notifyListeners(command, POST_UNDO);
}
}
Avant d’implémenter les méthodes undo/redo de chacune de nos Command GEF, nous devons appeler les méthodes UndoAction#run() et RedoAction#run() par :
- le menu contextuel de la page GEF Graphics avec les entrée de menu « Undo » et « Redo ».
- le menu global « Edit » avec les entrée de menu « Undo » et « Redo ».
- la combinaison de touches « Ctrl+Z » et « Ctrl+Y »
Undo/Redo – Menu Contextuel
Modifiez le code de la méthode WorkflowContextMenuProvider#buildContextMenu(IMenuManager menu) de la classe org.example.workflow.presentation.graphical.actions.WorkflowContextMenuProvider comme suit :
GEFActionConstants.addStandardActionGroups(menu);
IAction action = getActionRegistry().getAction( ActionFactory.UNDO.getId());
menu.appendToGroup(GEFActionConstants.GROUP_UNDO, action);
action = getActionRegistry().getAction(ActionFactory.REDO.getId());
menu.appendToGroup(GEFActionConstants.GROUP_UNDO, action);
action = getActionRegistry().getAction(ActionFactory.DELETE.getId());
if (action.isEnabled())
menu.appendToGroup(GEFActionConstants.GROUP_EDIT, action);
}
Ici nous ajoutons l’action UndoAction dans le menu contextuel de la page GEF Graphics via le code :
menu.appendToGroup(GEFActionConstants.GROUP_UNDO, action);
Dans les exemples GEF la constante GEFActionConstants.UNDO est utilisée mais celle-ci est marquée depracated et conseille d’utiliser la constante ActionFactory.UNDO.getId().
Relancez le plugin et vous devez voir apparaître les 2 actions (désactivées) « Undo » et « Redo » dans le menu contextuel :
Undo/Redo – Menu global
Pour lier les entrées de menu « Undo » et « Redo » appartenant au menu globale « Edit », nous allons procéder de la même manière que l’action DeleteAction. Pour cela modifiez la méthode GraphicalWorkkflowActionBarContributor#buildActions() de la classe org.example.workflow.presentation.graphical.actions.GraphicalWorkkflowActionBarContributor comme suit :
addRetargetAction(new UndoRetargetAction());
addRetargetAction(new RedoRetargetAction());
addRetargetAction(new DeleteRetargetAction());
}
Undo/Redo – « Ctrl+Z »/ »Ctrl+Y »
Pour bénéficier du « Ctrl+Z » et « Ctrl+Y » nous n’avons rien à faire car nous avons lier les entrées de menu « Undo » et « Redo » appartenant au menu globale « Edit » qui sont elle-même lié à ces combinaisons de touches.
Command#undo() / Command#redo()
A ce stade les appels de CommandStack#undo() et CommandStack#redo() via le menu contextuel, le menu globale ou « Ctrl+Z », « Ctrl+Y » est opérationnel. Nous pouvons maintenant implémenter les méthodes Command#undo() / Command#redo() pour chacune de nos Command GEF. Par défaut la classe de base org.eclipse.gef.commands.Command implémente la méthode Command#redo() comme ceci :
execute();
}
autrement dit elle appelle la méthode Command#execute(), ce qui dans la plupart des cas fonctionne très bien et évite à la classe qui étend org.eclipse.gef.commands.Command d’implémenter la méthode Command#redo(). La méthode Command#undo() est implémentée comme ceci :
}
autrement dit elle ne fait rien.
Undo/Redo – CreateCommand
Pour rappel notre classe org.example.workflow.presentation.graphical.commands.CreateCommand s’occupe d’ajouter un state/action aux workflow via sa méthode CreateCommand#execute() :
public void execute() {
if (getConnectableNode() instanceof StateType) {
getWorkflow().getState().add((StateType)getConnectableNode());
}
else {
if (getConnectableNode() instanceof ActionType) {
getWorkflow().getAction().add((ActionType)getConnectableNode());
}
}
}
La méthode CreateCommand#undo() doit effectuer son inverse, autrement dit supprimer le state/action qui a été ajouté au workflow. Pour cela implémentez la méthode CreateCommand#undo() comme ceci :
public void undo() {
if (getConnectableNode() instanceof StateType) {
getWorkflow().getState().remove((StateType)getConnectableNode());
}
else {
if (getConnectableNode() instanceof ActionType) {
getWorkflow().getAction().remove((ActionType)getConnectableNode());
}
}
}
Ici nous n’avons pas besoin d’implémenter la méthode CreateCommand#redo() car elle est identique à CreateCommand#execute().
Relancez le plugin, ajoutez un state via l’ouil de création de state puis cliquez sur le menu contextuel, l’item « Undo » doit être activé :
Cliquez sur l’item « Undo » pour annuler le state ajouté. Apuyez sur « Ctrl+Y » pour effectuer un Redo par le clavier, le state doit ré-apparaître (ceci permet de vérifier que le menu global est bien opérationnel).
Undo/Redo – ConnectionCreateCommand
Pour rappel notre classe org.example.workflow.presentation.graphical.commands.ConnectionCreateCommand s’occupe de créer une connection entre un state et une action via sa méthode ConnectionCreateCommand#execute() :
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);
}
}
La méthode ConnectionCreateCommand#undo() doit effectuer son inverse, autrement dit supprimer la connection. Pour cela implémentez la méthode ConnectionCreateCommand#undo() comme ceci :
public void undo() {
if (source instanceof StateType) {
ActionType action = (ActionType) target;
action.setFromState(null);
} else {
ActionType action = (ActionType) source;
action.setToState(null);
}
}
Ici nous n’avons pas besoin d’implémenter la méthode ConnectionCreateCommand#redo() car elle est identique à ConnectionCreateCommand#execute.
Relancez le plugin, liez un state et un action à l’aide de l’outil de création de connection puis testez que le undo/redo fonctionne correctement.
Undo/Redo – DeleteCommand
Pour rappel notre classe org.example.workflow.presentation.graphical.commands.DeleteCommand s’occupe de supprimer le state/action selectionné via sa méthode DeleteCommand#execute() :
public void execute() {
getChildren().remove(getConnectableNode());
}
La méthode DeleteCommand#undo() doit effectuer son inverse, autrement dit ajouter le state/action qui a été supprimé du workflow. Pour cela implémentez la méthode DeleteCommand#undo() comme ceci :
public void undo() {
getChildren().add(getConnectableNode());
}
Mias ceci ne suffit pas. En effet ce code fonctionne très bien pour les state/action qui ne sont pas liés par des connections. Dans le cas contraire nous perdons les informations de connections.
Pour gérer le undo correctement, il faut restaurer les connections existantes. Pour cela modifiez le code de DeleteCommand comme suit :
import java.util.ArrayList;
import java.util.List;
import org.eclipse.gef.commands.Command;
import org.example.workflow.model.ActionType;
import org.example.workflow.model.ConnectableNode;
import org.example.workflow.model.Connection;
import org.example.workflow.model.StateType;
import org.example.workflow.model.WorkflowType;
import org.example.workflow.model.util.ConnectionUtils;
public class DeleteCommand extends Command {
private WorkflowType workflow;
private ConnectableNode connectableNode;
private List<Connection> sourceConnections = new ArrayList<Connection>();
private List<Connection> targetConnections = new ArrayList<Connection>();
@Override
public void execute() {
getChildren().remove(getConnectableNode());
}
@Override
public void undo() {
getChildren().add(getConnectableNode());
for (Connection connection : sourceConnections) {
ConnectableNode source = connection.getSource();
ConnectableNode target = connection.getTarget();
ConnectionUtils.connect(source, target);
}
for (Connection connection : targetConnections) {
ConnectableNode source = connection.getSource();
ConnectableNode target = connection.getTarget();
ConnectionUtils.connect(source, target);
}
}
public WorkflowType getWorkflow() {
return workflow;
}
public void setWorkflow(WorkflowType workflow) {
this.workflow = workflow;
}
public ConnectableNode getConnectableNode() {
return connectableNode;
}
public void setConnectableNode(ConnectableNode connectableNode) {
this.connectableNode = connectableNode;
sourceConnections.addAll(connectableNode.getSourceConnections());
targetConnections.addAll(connectableNode.getTargetConnections());
}
protected List getChildren() {
if (connectableNode instanceof StateType)
return workflow.getState();
if (connectableNode instanceof ActionType)
return workflow.getAction();
return null;
}
}
Dans la méthode DeleteCommand#setConnectableNode(ConnectableNode connectableNode) on stocke les connections initialies sources et targets du node :
this.connectableNode = connectableNode;
sourceConnections.addAll(connectableNode.getSourceConnections());
targetConnections.addAll(connectableNode.getTargetConnections());
}
Puis dans la méthode DeleteCommand#undo() on reconnecte les node en utilisant les connections source et target initialie :
public void undo() {
getChildren().add(getConnectableNode());
for (Connection connection : sourceConnections) {
ConnectableNode source = connection.getSource();
ConnectableNode target = connection.getTarget();
ConnectionUtils.connect(source, target);
}
for (Connection connection : targetConnections) {
ConnectableNode source = connection.getSource();
ConnectableNode target = connection.getTarget();
ConnectionUtils.connect(source, target);
}
}
Relancez le plugin et supprimez l’action a1 qui est lié aux states s1 et s2, puis effectuez un « Ctrl+Z », l’action a1 doit être à nouveau lié à s1 et s2.
Undo/Redo – DeleteConnectionCommand
Pour rappel notre classe org.example.workflow.presentation.graphical.commands.DeleteConnectionCommand s’occupe de supprimer une connection entre un state et une action via sa méthode DeleteConnectionCommand#execute() :
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);
}
}
La méthode DeleteConnectionCommand#undo() doit effectuer son inverse, autrement dit remettre à jour la connection. Pour cela implémentez la méthode DeleteConnectionCommand#undo() comme ceci :
public void undo() {
if (connection.getSource() instanceof ActionType) {
ActionType action = (ActionType) connection.getSource();
StateType state = (StateType)connection.getTarget();
action.setToState(state);
} else {
if (connection.getTarget() instanceof ActionType) {
ActionType action = (ActionType) connection.getTarget();
StateType state = (StateType)connection.getSource();
action.setFromState(state);
}
}
}
Relancez le plugin, supprimez un lien entre un state et une action à l’aide de la touche « Suppr », puis testez que le undo/redo fonctionne correctement.
Conclusion
Ici nous avons mis en place les fonctionnalités de Undo/Redo dans la page GEF Graphics. Les Command GEF permettent de gérer le Undo/Redo via la stack de Command. On ne modifie jamais le modèle directement on passe toujours par une Command. EMF.Edit utilise d’ailleurs le même principe. Cependant nous sommes dans un contexte de multi page et le Undo/Redo fonctionne très bien lorsque l’on reste sur une page (ex : on modifie le modèle EMF workflow via la page Graphics GEF, puis on effectue un Undo), mais pas lorsque l’on modifie une page (ex : page Graphics GEF) puis que l’on clique sur une autre page (ex : page Source ou Selection) et que l’on tente d’effectuer un Undo. Ceci s’explique par le fait que chaque page (Selection, Graphics GEF, Source XML) a sa propre stack de command. De plus les Command sont de diiférentes natures :
- la page Selection et Source gère en interne une Command de stack de type Command EMF.
- la page GEF Graphics gère en interne une Command de stack de type Command GEF.
Pour régler ce problème, il faudrait que notre editor multi page partage la même stack de command. Mais la difficulté est que les Command sont de différentes natures. Aujourd’hui je n’ai pas encore étudié cette problématique, mais d’après ce que j’ai pu voir dans JSF Webools, il transforme toutes les Command EMF en Command GEF. Dans leur cas seul la page Source génère des Command EMF :
- la classe org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.EMFCommandStackGEFAdapter qui étent la classe GEF org.eclipse.gef.commands.CommandStack s’occupe de transformer les Command EMF en Command GEF via la classe org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.EMFCommandGEFAdapter. Par exemple elle implémente la méthode GEF org.eclipse.gef.commands.CommandStack#getUndoCommand() comme ceci :
public Command getUndoCommand() {
if (emfCommandStack == null || emfCommandStack.getUndoCommand() == null) {
return null;
}
return new EMFCommandGEFAdapter(emfCommandStack.getUndoCommand());
}où emfCommandStack est la stack de command EMF du modèle SSE récupérée comme ceci :
model = StructuredModelManager.getModelManager().getExistingModelForEdit(doc);
emfCommandStack = ((BasicCommandStack) this.model.getUndoManager().getCommandStack());Un listener est ajouté à la commande de stack EMF :
emfCommandStack.addCommandStackListener(this);
pour indiquer à la commande de stack GEF EMFCommandStackGEFAdapter que celle-ci change (ajout d’une nouvelle commande GEF) et qu’elle notifie tous ses listeners GEF org.eclipse.gef.commands.CommandStackEvent qu’il y a un changement dans la stack :
public void commandStackChanged(EventObject event) {
this.notifyListeners();
} - la classe org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.EMFCommandGEFAdapter étend la classe GEF org.eclipse.gef.commands.Command et attend dans son constructeur une Command EMF
org.eclipse.emf.common.command.Command. Par exemple elle implémente la méthode GEF org.eclipse.gef.commands.Command#undo() comme ceci :public void undo() {
if (emfCommand == null) {
return;
}
emfCommand.undo();
}où emfCommand est la Command EMF passé dans le constructeur de EMFCommandGEFAdapter.
L’editor multi page FacesConfigEditor travaille toujours avec une stack de Command de type GEF. Si la page activée est celle de GEF, c’est celle de GEF qui est utilisé, si c’est une autre page, c’est EMFCommandStackGEFAdapter qui est utilisé. FacesConfigEditor utilise la classe org.eclipse.jst.jsf.facesconfig.ui.pageflow.command.DelegatingCommandStack qui maintient la stack de Command GEF de la page active. L’état dirty qui doit se baser sur la stack de command GEF de la page active est gérée par le listener GEF org.eclipse.jst.jsf.facesconfig.ui.FacesConfigEditor.MultiPageCommandStackListener qui implémente l’interface GEF org.eclipse.gef.commands.CommandStackListener.
Je pense qu’il est plus difficile (voir impossible) de ne travailler qu’avec des Command EMF, autrement dit transformer les Command GEF en Command EMF car une Command GEF est une classe alors qu’une Command EMF est une interface.
18 Commentaires + Ajouter un commentaire
Articles récents
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step5]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step4]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step3]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step2]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step1]
Commentaires récents
- Conception d’un Editeur Eclipse de workflow XML [step 0] dans
- Conception d’un Editeur Eclipse de workflow XML [step 19] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
Bonjour,
Cela fait un petit moment maintenant que ce tutoriel a ete redige mais vu le niveau technique de ma question cela ne devrait pas vous poser de probleme
J’ai telecharge la derniere archive (Etape 19) mais je n’arrive pas a executer l’application…
Je l’ai dans un premier temps importee, puis executee As an Eclipse application sur l’un des trois packages contenu dans l’archive. Mais cela ne me donne pas grand chose :s
Je ne sais pas si ma methode d’import est la bonne (Import d’un projet existant a partir d’une archive). Et ensuite comment lancer l’application ?
Merci d’avance pour toute precision.
PS: desole pour les accents, le site ne prend pas en charge les combinaisons de touches du clavier americain :s
Gregoire
Bonjour Thomas,
Merci pour toutes ces eloges, ca fait bien plaisir:) Le fait d’avoir redige ces billets m’a force (du moins j’ai tente) a aller au bout des choses en terme de comprehension. Mais tout ce que j’ai mis dans ces billets, ce sont juste le fruit de mon etude sur le sujet. La seule experience sur les editeurs EMF+GEF sont celui de ce blog. je n’ai jamais du realiser un editeur dans un cadre professionnel, tout ca pour dire que je n’ai pas la science infuse et que certaine fois je dis peut etre de grosses betises. D’ou l’interet de ce blog pour debattre.
Je suis aujourd’hui sur d’autres sujets (Spring DM, OSGi, DLTK….) et je t’avoues que lorqu’on est plus dedans, c’est assez difficile de repondre pertinement a des questions concernant le sujet Editeur EMF+GEF.
Bon courage pour ton projet
Angelo
Bonjour Angelo,
Je te remercie de ta réponse, et je pense effectivement que de gérer EMF et GEF simultanément reste plus évident (en terme de maîtrise sur les fonctionnalités programmées) que de passer par GMF, avec qui finalement, on aura besoin de faire la même chose, mais cette fois ci au sein du framework… C’est quand même moins pratique.
En ce qui concerne les translator j’ai amélioré mon outil de génération et c’est presque utilisable pour des gros modèles. Etant donné que le modèle reste quand même quelque chose qui (espérons le) ne change pas tous les jours, passer par un programme externe pour générer les classes de translator ne me pose pas spécialement un soucis déontologique.
Je n’ai pas le temps de travailler plus dessus pour le moment, mais je sens qu’avec ton travail et les diverses sources que tu as recensées parmi lesquelles psykokwak, j’ai les cartes en main pour faire quelque chose de correct.
Si tu le permets, je te solliciterais peut être encore, et peut être pourrions-nous échanger nos différentes avancées dans le domaine des éditeurs basés sur EMF.
Encore merci pour la mutualisation des connaissances et bravo pour le travail accompli (ceux qui s’y pencherons conviendront que c’est balèze, en temps de travail aussi bien qu’en difficulté technique)
Bien cordialement et à bientôt,
Thomas.
Bonjour Thomas,
>J’ai suivi avec attention tous tes articles et je dois dire chapeau!
Merci!
>En revanche il y a plusieurs inconvénients:
>- L’utilisation des translators (qui eux n’utilisent pas le ecore !), j’ai >construit une petite moulinette en java pour m’en sortir, ce qui génère tout >de même plus de 1000 lignes de codes… Cela reste super pratique (surtout >une fois le code généré) mais ca pourrait l’être encore plus si ca se >construisait tout seul!
C’est le GROS souci des Translator. Ca n’utilise pas le ecore. J’avais commence a l’epoque a creer un mini projet qui a partir d’un EClass, instancie les bon translators. Le projet se trouve sur https://tk-ui.svn.sourceforge.net/svnroot/tk-ui/wst/org.eclipse.wst.common.emf.ext/
D’apres mes souvenir il faut utiliser https://tk-ui.svn.sourceforge.net/svnroot/tk-ui/wst/org.eclipse.wst.common.emf.ext/src/org/eclipse/wst/common/emf/resource/EcoreTranslatorResourceImpl.java
(Je n’ai pas d’exempel sous la main de ce que j’avais fait). Ca gere les Translator basiques (attributs….)
>- Le problème se répète dans une moindre mesure avec l’utilisation de gef, >car il faut définir « à la main » (eh oui je suis feignant)les éléments que >l’on souhaite définir en tant que modèle graphique.
Exactement.
>Tout cela pour amener les questions:
> Est-ce que GMF est moins sensible au changement de modèle?
> Est-ce que GMF est suffisament mature pour supporter un projet de cet ordre?
> Enfin, et là c’est subjectif, est-il préferrable d’utiliser GMF qui est > »clé-en-main » ou de gérer GEF et GMF séparément? (je suppose que GMF est plus >rigide en terme de personnalisation?)
Je ne peux pas te repondre car je ne connais pas GMF (j’ai juste fait un tutorial). Ce qui me genait avec GMF c’est son cote boite noir et c’est pour ca que je suis parti sur EMF+GEF. Je n’ai jamais vu d’exemple avec GMF qui gere un workflow XML qui peut etre autant mis a jour grahiquement (ca GMF le gere bien) que par l’utilisateur (l’utilisateur saisit le XML). Le gros problème c’est la synchronisation entre un editeur de texte et le modele EMF. A ce jour je ne connais que les Translator.
J’ai eu une longue discussion avec Benjamin Cabe a ce sujet qui s’occupe de developper PDE Incubator ou le fichier plugin.xml (de ce que j’ai compris) est represente par un modele EMF. Il faut aussi synchroniser le fichier plugin.xml avec le modele EMF quand l’utilisateur saisit du contenu XML. Et a l’epoque il rechargeait le modele EMF a chacune des frappes. Je ne sais pas ce qui est fait aujourd’hui.
GMF m’avait pose 2 problemes pour la conception d’un workflow XML :
1) je n’ai jamais reussi a personnaliser correctement la serialisation EMF en XML. plus exactement, je n’ai jamais reussi a serialiser mon instance EMF en XML sans namespace. J’avais toujours un namespace ‘wf’ comme :
…
2) la synchronisation editeur de texte avec EMF, GMF ne gere pas ca, donc c’est pour cela que je suis partir sur EMF+GEF.
Angelo
P.PS:
je voulais dire EMF pour la dernière question:
» – Enfin, et là c’est subjectif, est-il préferrable d’utiliser GMF qui est « clé-en-main » ou de gérer GEF et EMF séparément? (je suppose que GMF est plus rigide en terme de personnalisation?) »
Merci!
Bonjour Angelo,
J’ai suivi avec attention tous tes articles et je dois dire chapeau!
Je suis en train de réfléchir à concevoir moi-même un éditeur de workflow avec un schéma un peu plus étoffé, à savoir probablement le xpdl 2.1.
La génération fournie par EMF est vraiment nickel avec ce modèle et les possibilités d’exploitation sont vraiment intéressantes. En revanche il y a plusieurs inconvénients:
– L’utilisation des translators (qui eux n’utilisent pas le ecore !), j’ai construit une petite moulinette en java pour m’en sortir, ce qui génère tout de même plus de 1000 lignes de codes… Cela reste super pratique (surtout une fois le code généré) mais ca pourrait l’être encore plus si ca se construisait tout seul!
– Le problème se répète dans une moindre mesure avec l’utilisation de gef, car il faut définir « à la main » (eh oui je suis feignant)les éléments que l’on souhaite définir en tant que modèle graphique.
En fait mon problème est que le xpdl est un schéma qui évolue encore (2 updates en 3 ans) et comme le développement d’un éditeur est à priori quand même assez lourd, il serait sympathique de recommencer le moins de choses possibles à chaque mise à jour de modèle.
Tout cela pour amener les questions:
– Est-ce que GMF est moins sensible au changement de modèle?
– Est-ce que GMF est suffisament mature pour supporter un projet de cet ordre?
– Enfin, et là c’est subjectif, est-il préferrable d’utiliser GMF qui est « clé-en-main » ou de gérer GEF et GMF séparément? (je suppose que GMF est plus rigide en terme de personnalisation?)
Merci de vos réponses et encore félicitation pour ce blog qui est vraiment très complet.
Thomas.
(PS: je rejoins la demande d’un utilisateur du forum: ce serait super d’avoir tout ton tuto au format PDF, tel que l’a fait psykokwak, même si c’est déjà bien comme ca)
Ma petite remarque au commentaire de @Guybrush (« Tutorial sans interet. »):
J’ai entendu recement quelq’un qui pensait – opinion que je partage – que, souvent, on pourrait comparer les commentaires d’un article avec les ecritures sur les murs des toilettes publiques (ils ont la meme valeur et sont ecrits par les meme gens).
Sinon, merci Angelo, super tutoriel, ca donne un point de depart excelent.
Emil.
J’ai essaye de dégrossir les problématiques d’un editeur XML de workflow mais je n’ai pas tout traité. Il va falloir fouiller dans les sources de GEF et EMF. Dans les examples GEF il y a je croies des examples avec le PropertySheet.
Bon courage
Angelo
Bonjour,
le tuto touche à sa fin (pour moi :p).
je me demande comment on peut remplir la property sheet,
de sorte que lorsqu’on sélectionne un élément graphique, ses propriétés s’affichent tout en permettant la possibilité de modifier ces propriétés.
@Guybrush
Commentaire sans intérêt
Bonjour Guybrush
Pourrais tu m’expliquer stp pourquoi tu trouves les billets sans interêts? Peut etre que pour toi il est facile de mettre en place un éditeur XML avec EMF + GEF, mais me concernant je n’avais jamais rien trouvé sur le sujet. C’est pour cela que j’ai décidé de rediger cette série de billets qui m’a obligé a bien comprendre tout le code.
Angelo
Tutorial sans interet.
>Très honnetement, je ne sais plus si tu as bien exliqué le code dès les premiers billets. Mais je pense que oui, il n’y a pas de raison que ce >soit le cas pour la deuxième partie du tuto et pas la première. Non simplement, je pense qu’au début je nageais dans la semoule, parceque c’est >quant même un très gros morceau.
Oui ca demarre avec EMF qui est super chaud a comprendre. Et je n’ai parle que de trucs basiques de EMF.
>Si je peux me permettre, j’ai juste une petite remarque : je ne trouve pas que ce mode de diffusion (blog) soit très pratique.
Tout a fait d’accord avec toi! L’idéal serait de rediger des articles DVP, mais ca me prendrait encore plus de temps. Mon but etait de rediger un billet/semaine pour eviter de me demotiver. Je ne sais pas si tu as vu mais j’ai migre de blog. Mon nouveau blog est http://angelozerr.wordpress.com/ ou j’ai demarre une serie de billets sur Spring DM, OSGi et Eclipse RCP et je vais prendre en compte ta remarque pour passer facilement d’un billet a un autre. Deja le « Category Cloud » te propose les mot cle (Spring DM…) utilises dans les billets et il te gere uen taille en fonction de sa pertinence.
>Ou alors, et c’est bien possible; je n’ai pas compris quelque chose et il faut qu’on m’explique.
>Je n’ai pas trouvé de moyen de voir le blog dans son intégralité et naviguer d’un billet à l’autre. Par exemple, j’ai trouvé ce blog en >arrivant sur le billet n°19. impossible de linker directement le premier billet (Step 0) autrement qu’en passant par les liens dans l’intro de >chaque billet qui amène au billet précédent (« dans le billet précédent N°… »). Du coup pour arriver au Step 0, obligé de passer par >19,18,17,… => Très peu pratique pour la navigation.
Pour voir le blog dans son intégralité il faut que tu selectionne « Akrogen » dans la liste « Liste des blogs »
>A mon avis, je n’ai pas dù comprendre ou trouver le bon outil car je serai surpris que le lecture de blg ne sois pas plus … »pratique ».
C’est une des raison pour laquelle je suis passe sur wordpress ou je trouve que ca fait plus pro (couleur syntaxique sur le code Java, Catgeory Cloud »….)
>Sinon, ne prend pas mal cette remarque qui n’enlève rien à la qualité du contenu et au mérite que tu as de l’avoir produit.
Je n ele prends pas mal au contraire. Comme je t’ai dit j epense que l’ideal serait de rediger des articles et pas des posts mais je n’ai pas le courage de faire ca (ca me prendrait trop de temps). Moi je voulais garder une trace de mon etude a travers mes billets et avoir des retours dessus sans passer un mois pour rediger un billet.
>Encore une fois merci et bravo !
Merci, ca fait plaisir:)
Angelo
Bonjour Angelo,
Merci pour tes encouragements.
>J’ai tente d’expliquer le code mais peut eter qu’a des moments j’ai fait l’impasse sur certaisn points. Il ne faut pas hesiter a me soulever ces points. je corrigerai les billets.
Très honnetement, je ne sais plus si tu as bien exliqué le code dès les premiers billets. Mais je pense que oui, il n’y a pas de raison que ce soit le cas pour la deuxième partie du tuto et pas la première. Non simplement, je pense qu’au début je nageais dans la semoule, parceque c’est quant même un très gros morceau.
Si je peux me permettre, j’ai juste une petite remarque : je ne trouve pas que ce mode de diffusion (blog) soit très pratique. Ou alors, et c’est bien possible; je n’ai pas compris quelque chose et il faut qu’on m’explique.
Je n’ai pas trouvé de moyen de voir le blog dans son intégralité et naviguer d’un billet à l’autre. Par exemple, j’ai trouvé ce blog en arrivant sur le billet n°19. impossible de linker directement le premier billet (Step 0) autrement qu’en passant par les liens dans l’intro de chaque billet qui amène au billet précédent (« dans le billet précédent N°… »). Du coup pour arriver au Step 0, obligé de passer par 19,18,17,… => Très peu pratique pour la navigation.
A mon avis, je n’ai pas dù comprendre ou trouver le bon outil car je serai surpris que le lecture de blg ne sois pas plus … »pratique ».
Sinon, ne prend pas mal cette remarque qui n’enlève rien à la qualité du contenu et au mérite que tu as de l’avoir produit. Encore une fois merci et bravo !
Bonjour kase74 ,
>Merci pour ce tuto. Je débute avec eclipse et java.
Bon courage! S’attaquer a Eclipse voir DOM-SSE sans connaitre Java, c’est super chaud. Bravo!
>J’avoue que les premiers billets m’ont fait peur car beaucoup de code à copier coller sans même comprendre à quoi il sert,
J’ai tente d’expliquer le code mais peut eter qu’a des moments j’ai fait l’impasse sur certaisn points. Il ne faut pas hesiter a me soulever ces points. je corrigerai les billets.
>mais petit à petit le brouillard se dissipe.
Tant mieux.
>Pour bien faire il faudrait que je reprenne le tuto depuis 0 mais le courage et le temps me manque …
Oui je comprends que ca soit long a relire tout ca, mais je le fais souvent avec d’autres tutos que je trouve sur le net. Et la deuxieme lecture est très enrichissante.
>Sinon, encore merci et félicitation pour ce travail qui, j’imagine, à été très laborieux.
Merci. Ca été très long, mais c’était un très bon exercice pour moi car ca m’a oblige a aller au fond des choses.
Angelo
Bonjour Angelo,
Merci pour ce tuto. Je débute avec eclipse et java. J’avoue que les premiers billets m’ont fait peur car beaucoup de code à copier coller sans même comprendre à quoi il sert, mais petit à petit le brouillard se dissipe.
Pour bien faire il faudrait que je reprenne le tuto depuis 0 mais le courage et le temps me manque …
Sinon, encore merci et félicitation pour ce travail qui, j’imagine, à été très laborieux.
Cordialement.
Hi CarlosSM ,
>First, congratulations for your blog, I think that is an amazing job, the second …
Thank a lot!
>Sorry I don’t speak French, I hope you can understand me
Yes, I can understand but my english is very bad.
>Once you have a workflow editor finished, can you integrate the workflow editor easily in another RCP project with different views? I’m trying >it and I think too complicated…
I have never used GEF/EMF editor with RCP project. So I’m sorry I can’t help you. I think it’s not easy because in my workflow editor, I use SSE (DOMM-SSE and EMF2DDOMMSSERenderer) and I think it use IFile depdendency (I’m not sure with that). Sorry I can’t help you with that, but I think it’s possible if you use GEF (without SSE), (but you will not have XML editor), because GMF is enable (I think) to generate RCP application with GEF.
>Thank you anyway for your great work!
I’m happy that my work can help you.
Regards Angelo
First, congratulations for your blog, I think that is an amazing job, the second … Sorry I don’t speak French, I hope you can understand me
My question is this:
Once you have a workflow editor finished, can you integrate the workflow editor easily in another RCP project with different views? I’m trying it and I think too complicated…
Thank you anyway for your great work!