Construction de commandes avec la plateforme Eclipse

5 807 vues

Publié le

Ce support de cours s'intéresse à détailler la construction de commandes avec la plateforme Eclipse. Il fait partie de la série des supports de cours liée au Workbench. Les aspects suivants sont étudiés : les actions, construction par déclaration et programmation de commandes et de handlers, éléments menuContribution par déclaration, raccourcis clavier, restrictions (visibleWhen, enabledWhen, activeWhen), paramétrer les commandes, restrictions par programmation et réutilisation (plug-in Spy et traces).

Publié dans : Technologie, Sports
0 commentaire
2 j’aime
Statistiques
Remarques
  • Soyez le premier à commenter

Aucun téléchargement
Vues
Nombre de vues
5 807
Sur SlideShare
0
Issues des intégrations
0
Intégrations
786
Actions
Partages
0
Téléchargements
162
Commentaires
0
J’aime
2
Intégrations 0
Aucune incorporation

Aucune remarque pour cette diapositive

Construction de commandes avec la plateforme Eclipse

  1. 1. Développement de clients riches : Plateforme Eclipse Chapitre 3 : Conception de plug-ins Workbench : Commands Mickaël BARON - 2009 mailto:baron.mickael@gmail.com ou mailto:baron@ensma.fr
  2. 2. Licence Creative Commons Contrat Paternité Partage des Conditions Initiales à l'Identique 2.0 France keulkeul.blogspot.com http://creativecommons.org/licenses/by-sa/2.0/fr Commands - M. Baron - Page 2
  3. 3. Organisation du cours sur le Workbench : Commands Avant, il y avait les Actions … Construction par déclaration et programmation Eléments menuContribution par déclaration Tous les exemples du cours Raccourcis clavier sont disponibles directement à l’adresse mbaron.developpez.com/eclipse/commands Restrictions (visibleWhen, enabledWhen, activeWhen) Paramétrer les commandes keulkeul.blogspot.com Restrictions par programmation Réutilisation Commands - M. Baron - Page 3
  4. 4. Déroulement du cours Pédagogie du cours Illustration avec de nombreux exemples qui sont disponibles à l’adresse mbaron.developpez.com/eclipse/commands Des bulles d’aide tout au long du cours Logiciels utilisés Eclipse 3.4.2 Ganymede Pré-requis Connaître la structure d’un plug-ins et savoir créer une extension Structure du Workbench keulkeul.blogspot.com Ceci est une astuce Remerciement Developpez.com : [TODO] Ceci est une alerte … Commands - M. Baron - Page 4
  5. 5. Ressources … Des billets sur les généralités sur les commandes blog.eclipse-tips.com/2009/01/commands-part-1-actions-vs-commands.html blog.eclipse-tips.com/2009/01/commands-part-2-selection-and.html blog.eclipse-tips.com/2008/12/commands-part-3-parameters-for-commands.html blog.eclipse-tips.com/2009/01/commands-part-4-misc-items.html blog.eclipse-tips.com/2009/02/commands-part-5-authentication-in-rcp.html blog.eclipse-tips.com/2009/03/commands-part-6-toggle-radio-menu.html blog.eclipse-tips.com/2009/05/commands-part-7-adding-standard.html blog.eclipse-tips.com/2009/02/customizing-about-dialog.html blog.eclipse-tips.com/2009/10/associating-command-with-job.html blog.eclipse-tips.com/2009/06/keyboard-accessibility-thru-command.html bugs.eclipse.org/bugs/show_bug.cgi?id=223445 hermanlintvelt.blogspot.com/2009/06/eclipse-rcp-commands-api-review-of-part.html keulkeul.blogspot.com hermanlintvelt.blogspot.com/2009/05/eclipse-rcp-commands-api-part-one.html hermanlintvelt.blogspot.com/2009/06/eclipse-rcp-commands-part-3-visiblewhen.html konigsberg.blogspot.com/2008/06/screencast-using-property-testers-in.html www.vogella.de/blog/?p=421 richclientplatform.blogspot.com/2007/07/new-menu-contribution-extension.html www.vimeo.com/1217595?pg=embed&sec=1217595 Commands - M. Baron - Page 5
  6. 6. Ressources … (suite) Des articles sur les généralités sur les commandes wiki.eclipse.org/Platform_Command_Framework wiki.eclipse.org/Menu_Contributions wiki.eclipse.org/Menus_Extension_Mapping wiki.eclipse.org/Command_Core_Expressions www.eclipsecon.org/2008/?page=sub/&id=221 www.vogella.de/articles/RichClientPlatform/article.html#commands www.vogella.de/articles/EclipseCommands/article.html www.vogella.de/articles/EclipsePlugIn/ar01s04.html www.ibm.com/developerworks/library/os-eclipse-3.3menu/index.html svn2.assembla.com/svn/eclipsecommands/trunk/EclipseCommands/contents/article.html keulkeul.blogspot.com Des livres Eclipse – Building Commercial-Quality Plug-ins, 2004 - ISBN : 0-321-22847-2 Eclipse – Rich Client Platform, 2005 – ISBN : 0-321-33461-2 Eclipse Plug-ins, 3rd Edition, 2008 – ISBN : 0-321-55346-2 Commands - M. Baron - Page 6
  7. 7. Introduction Dans les parties Perspective, View et Editor nous avons abordé le concept d’actions permettant d’ajouter de nou- veaux éléments pour un menu une barre d’outils un menu contextuel Workbench fournit deux APIs pour ajouter des éléments Actions (celle utilisée dans les précédents transparents) keulkeul.blogspot.com Command (apparue depuis Eclipse 3.3) L’API Actions est plus ancienne et doit être remplacée par l’API Commands Commands - M. Baron - Page 7
  8. 8. Introduction Les points d’extension concernant l’API Actions sont toujours utilisables Du côté des vues, des éditeurs et des perspectives il existe toujours cette relation avec les actions Pour vos prochains développements, préférer l’utilisation de la nouvelle API Commands keulkeul.blogspot.com Dans la suite nous présentons rapidement l’API Actions en insistant sur ces désavantages puis nous nous orienterons vers l’API Commands Commands - M. Baron - Page 8
  9. 9. Ancienne API Actions Depuis le début de ce support de cours, nous avons abordé rapidement la notion d’Actions via l’interface IAction La description de cette interface met en avant l’absence de flexibilité qui a conduit à définir la nouvelle API Command public interface IAction { void setText(String text); Caractéristiques liées void setImageDescriptor(ImageDescriptor image); à la présentation de void setAccelerator(int keycode); void setEnabled(boolean enabled); Caractéristique liée au l’action traitement de l’action void run(); // ... more setters and getters } Les aspects liés à la présentation (setText, setEnabled, …) keulkeul.blogspot.com sont fortement couplés avec le traitement de l’action (run) Par conséquent il est difficile de fournir plusieurs textes, images, … raccourcis clavier pour une même action Commands - M. Baron - Page 9
  10. 10. Ancienne API Actions La plateforme Eclipse fournit différents points d’extension permettant d’ajouter des actions à différents éléments du Workbench org.eclipse.ui.actionSets : ajouter des actions au menu et à la barre d’outils d’une application Eclipse org.eclipse.ui.editorActions : ajouter des actions au menu et à la barre d’outils d’une application Eclipse pour un type d’éditeur donné org.eclipse.ui.actionSetPartAssociations : permet d’associer une action à un ensemble de Parts (View et Editor) org.eclipse.ui.viewActions : ajouter des actions au menu et à la barre d’outils d’une vue keulkeul.blogspot.com org.eclipse.ui.popupMenus : ajouter des actions à un menu contextuel Nous montrons dans la suite un exemple exploitant le point d’extension org.eclipse.ui.actionSets Commands - M. Baron - Page 10
  11. 11. Ancienne API Actions Exemple : ajouter une action au menu et à la barre d’outils d’une application Eclipse avec l’API Actions Ajout d’une action au menu Ajout d’une action à la barre d’outils keulkeul.blogspot.com Projet ActionsExamples Commands - M. Baron - Page 11
  12. 12. Ancienne API Actions Exemple (suite) : ajouter une action au menu et à la barre d’outils d’une application Eclipse avec l’API Actions Création d’extension à partir du point d’extension org.eclipse.ui.actionSets keulkeul.blogspot.com Template pour créer rapidement cette extension Commands - M. Baron - Page 12
  13. 13. Ancienne API Actions Exemple (suite) : ajouter une action au menu et à la barre d’outils d’une application Eclipse avec l’API Actions Création d’un élément actionSet keulkeul.blogspot.com plugin.xml du projet Onglet Extensions ActionsExamples Commands - M. Baron - Page 13
  14. 14. Ancienne API Actions Exemple (suite) : ajouter une action au menu et à la barre d’outils d’une application Eclipse avec l’API Actions Création d’un élément menu Texte affiché dans le menu de l’application keulkeul.blogspot.com Possibilité d’ajouter le nouveau menu à un menu existant plugin.xml du projet Onglet Extensions ActionsExamples Commands - M. Baron - Page 14
  15. 15. Ancienne API Actions Exemple (suite) : ajouter une action au menu et à la barre d’outils d’une application Eclipse avec l’API Actions Création d’un élément action keulkeul.blogspot.com plugin.xml du projet Onglet Extensions ActionsExamples Commands - M. Baron - Page 15
  16. 16. Ancienne API Actions Exemple (suite) : ajouter une action au menu et à la barre d’outils d’une application Eclipse avec l’API Actions Précise où doit être affiché l’action dans la barre de menu Précise où doit être affiché l’action dans la barre d’outils keulkeul.blogspot.com Classe de type IWorkbenchWindowActionDelegage codant le traitement de l’action Commands - M. Baron - Page 16
  17. 17. API Commands La nouvelle API Commands permet de construire une Command, désignée commande dans la suite de ce cours Le principal avantage d’une commande est la séparation stricte de l’IHM du comportement Pour utiliser une commande, vous avez besoin Donner une description déclarative de la commande Définir le comportement via un Handler (activation, traitement, …) Préciser à quel endroit de l’IHM la commande s’applique (menu, keulkeul.blogspot.com barre d’outils de l’application ou d’une vue, …) Si vous commencez de nouveaux développements préférez l’utilisation de cette nouvelle API Commands - M. Baron - Page Commands 17
  18. 18. Construction par déclaration Une commande est déclarée par l’intermédiaire du point d’extension org.eclipse.ui.commands Point d’extension org.eclipse.ui.commands keulkeul.blogspot.com Un template « Hello, World » pour définir une commande Commands - M. Baron - Page 18
  19. 19. Construction par déclaration Définition des attributs d’une commande keulkeul.blogspot.com Un handler par défaut peut être défini, dans la suite on plugin.xml du projet Onglet Extensions montrera qu’il est possible CommandsExamples d’extraire cette relation Commands - M. Baron - Page 19
  20. 20. Construction par déclaration Le comportement de la commande doit être défini par un objet de type IHandler La classe AbstractHandler fournit une implémentation des différentes méthodes Object execute(ExecutionEvent event) : traitement réalisé quand la commande est déclenchée package eclipse.workbench.commandsexamples.handler; public class FirstHandler extends AbstractHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { keulkeul.blogspot.com MessageDialog.openInformation(Display.getDefault().getActiveShell(), "CommandsExamples Plug-in","Hello, Eclipse world with First Handler"); return ""; } } Classe FirstHandler.java du projet CommandsExamples Commands - M. Baron - Page 20
  21. 21. Associer une commande à un Handler Le point d’extension org.eclipse.ui.handlers permet de définir explicitement un Handler à une commande L’Handler n’est donc plus défini au niveau de la commande L’intérêt est de pouvoir spécifier des contraintes différentes pour une même classe de comportement (objet IHandler) Différentes contraintes keulkeul.blogspot.com Contraintes seront étudiées à la fin activeWhen : comportement actif ou pas de cette partie enabledWhen : comportement activé ou pas Commands - M. Baron - Page 21
  22. 22. Associer une commande à un Handler L’association commande / handler est déclarée par le point d’extension org.eclipse.ui.handlers Point d’extension org.eclipse.ui.handlers keulkeul.blogspot.com Commands - M. Baron - Page 22
  23. 23. Associer une commande à un Handler Définition du couple commande / handler Identifiant de la commande Classe de type IHandler keulkeul.blogspot.com correspondant au traitement de la commande plugin.xml du projet Onglet Extensions CommandsExamples Commands - M. Baron - Page 23
  24. 24. Associer une commande à des images Dans le même ordre d’idée, il est possible de découpler dans la définition de la commande les différentes images utilisées Le point d’extension org.eclipse.ui.commandImages permet de définir pour une commande les images à afficher l’image par défaut l’image utilisée lorsque la commande est désactivée l’image utilisée lorsque le curseur de la souris est au dessus de la keulkeul.blogspot.com représentation graphique de la commande style : dans quel contexte ces images sont utilisées : barre de menu ou barre d’outils. Si aucune valeur, cela concerne la barre de menu, si toolbar cela concerne la barre d’outils Commands - M. Baron - Page 24
  25. 25. Associer une commande à des images L’association commande / image est déclarée par le point d’extension org.eclipse.ui.commandImages Point d’extension org.eclipse.ui.commandImages keulkeul.blogspot.com Commands - M. Baron - Page 25
  26. 26. Associer une commande à des images Définition du couple commande / images Les différentes Identifiant de la commande images utilisées keulkeul.blogspot.com Si aucune valeur, cela concerne la barre de menu, si la valeur vaut plugin.xml du projet toolbar cela concerne la barre d’outils Onglet Extensions CommandsExamples Commands - M. Baron - Page 26
  27. 27. Construction par programmation Précédemment nous avons montré comment créer déclara- tivement des commandes et des comportements (Handler) La construction de commandes et de handlers de manière programmatique est obtenue via l’utilisation des interfaces ICommandService : pour créer des commandes IHandlerService : pour créer des handlers L’accès aux instances des deux interfaces IHandlerService et ICommandService est obtenu via le Workbench keulkeul.blogspot.com ICommandService cs = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class); IHandlerService hs = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class); La destruction des commandes et handler sont à la charge du programmeur Commands - M. Baron - Page 27
  28. 28. Construction par programmation L’interface ICommandService fournit les principaux services Category getCategory(String catId) : récupère la catégorie catId, si non existante, elle est créée Command getCommand(String comId) : récupère la commande comId, si non existante, elle est créée Category[] getDefinedCategories() : récupère la liste complète des catégories du Workbench void addExecutionListener(IExecutionListener iel) : ajoute un écouteur lors de l’exécution de la commande keulkeul.blogspot.com La classe Command décrit une commande void define(String name, String description, Category cat) : précise le nom de la commande, la description et la catégorie à laquelle la commande appartient (cat ne peut être null)Commands - M. Baron - Page 28
  29. 29. Construction par programmation Exemple : construire une commande Récupération du service de création de commandes Construction de la catégorie public class ViewCommandPart extends ViewPart { public void createPartControl(Composite parent) { ICommandService cs = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class); Category category = cs.getCategory("eclipse.workbench.commandsexample.commandscategory"); Command thirdCommand = cs.getCommand("eclipse.workbench.commandsexample.thirdcommand"); thirdCommand.define("Third Command", "", category); // Suite concernant la construction du Handler } Construction de la commande thirdcommand keulkeul.blogspot.com Association de la commande à la catégorie Classe ViewCommandPart.java du projet CommandsExamples Commands - M. Baron - Page 29
  30. 30. Construction par programmation L’interface IHandlerService fournit les principales méthodes suivantes IHandlerActivation activateHandler(String commandId, IHandler handler) : associe une commande à un handler Object executeCommand(String commandId, Event event) throws ExecutionException … : exécute un handler à partir de l’identifiant de la commande keulkeul.blogspot.com Pour rappel, la classe IHandler a été étudiée précédemment, la classe AbstractHandler fournit une abstraction des principales méthodes Commands - M. Baron - Page 30
  31. 31. Construction par programmation Exemple (suite) : exécuter une commande public class ViewCommandPart extends ViewPart { public void createPartControl(Composite parent) { Récupération du service de // Suite du précédent transparent handler IHandlerService hs = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); IHandler handler = new AbstractHandler() { public Object execute(ExecutionEvent event) throws ExecutionException { MessageDialog.openInformation(Display.getDefault().getActiveShell(), "CommandsExamples Plug-in", "Hello, Eclipse world with Third Handler"); return null; } }; hs.activateHandler("eclipse.workbench.commandsexample.thirdcommand", handler); Button callCommand = new Button(parent, SWT.PUSH); Associer une commande avec un callCommand.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { try { handler IHandlerService hs = (IHandlerService)PlatformUI.getWorkbench() .getService(IHandlerService.class); hs.executeCommand("eclipse.workbench.commandsexample.thirdcommand", null); } catch (Exception e1) { keulkeul.blogspot.com e1.printStackTrace(); } } Exécuter un handler à partir de }); callCommand.setText("Call Third Command"); } } l’identifiant d’une commande Classe ViewCommandPart.java du projet CommandsExamples Commands - M. Baron - Page 31
  32. 32. Construction par programmation Le Workbench fournit des écouteurs permettant de notifier le client lors de l’exécution d’une ou plusieurs commandes ICommandService#addExecutionListener(IExecutionListener el) : ajout d’un écouteur sur l’ensemble des exécutions de commandes Command#addExecutionListener(IExecutionListener el) : ajout d’un écouteur sur une exécution de commande IExecutionListener fournit les services suivants void postExecuteFailure(String comId, ExecutionException ex) : déclenchée si l’exécution d’une commande a échoué keulkeul.blogspot.com void postExecuteSucess(String comId, Object returnValue) : déclenchée si l’exécution d’une commande a réussi void preExecute(String comId, ExecutionEvent event) : déclenchée pour avertir qu’une commande va être exécutée Commands - M. Baron - Page 32
  33. 33. Construction par programmation Exemple : écouter l’exécution d’une commande public class ViewCommandPart extends ViewPart { public void createPartControl(Composite parent) { // Suite du précédent transparent ICommandService cs = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class); cs.addExecutionListener(new IExecutionListener() { public void postExecuteSuccess(String commandId, Object returnValue) { System.out.println(".postExecuteSuccess() : " + commandId); } public void preExecute(String commandId, ExecutionEvent event) { System.out.println(".preExecute()"); } public void postExecuteFailure(String commandId, ExecutionException exception) { System.out.println(".postExecuteFailure()"); } Ecouteur sur la totalité }); ... des exécutions de thirdCommand.addExecutionListener(new IExecutionListener() { commande public void notHandled(String commandId, NotHandledException exception) { System.out.println(".notHandled()"); } public void postExecuteSuccess(String commandId, Object returnValue) { System.out.println(".postExecuteSuccess()"); } keulkeul.blogspot.com public void preExecute(String commandId, ExecutionEvent event) { System.out.println(".preExecute()"); } ... }); Ecouteur sur l’exécution } } d’une commande Classe ViewCommandPart.java du projet CommandsExamples Commands - M. Baron - Page 33
  34. 34. Commande dans le Workbench Pour l’instant nous savons construire une commande et un handler de manière déclarative et programmatique Nous montrons dans la suite comment ajouter une com- mande à l’interface graphique du Workbench Pour rappel les éléments graphiques qui peuvent être étendus sont les suivants keulkeul.blogspot.com Menu général de l’application Barre d’outil de l’application Barre d’outil, menu localisé et menu contextuel d’une vue donnée Commands - M. Baron - Page 34
  35. 35. Commande dans le Workbench L’ajout d’une commande au Workbench est obtenu par le point d’extension org.eclipse.ui.menus Point d’extension org.eclipse.ui.menus keulkeul.blogspot.com Commands - M. Baron - Page 35
  36. 36. Commande dans le Workbench Création d’un élément de type menuContribution permettant de préciser où sera placée la commande dans le Workbench (locationURI) keulkeul.blogspot.com Ajout d’un élément menuContribution Commands - M. Baron - Page 36
  37. 37. Commande dans le Workbench Modification de la valeur de l’attribut locationURI L’attribut locationURI permet d’indiquer où sera placée la commande keulkeul.blogspot.com Dans la suite nous donnons des explications sur la construction de l’attribut locationURI Commands - M. Baron - Page 37
  38. 38. Commande dans le Workbench La valeur de l’attribut locationURI est de la forme suivante [scheme]:[id]?[placement] scheme : le type d’élément graphique à étendre. Les valeurs autorisées sont : menu, toolbar et popup id : identifiant du menu, barre d’outil, menu contextuel ou vue placement : les contraintes de placement de la commande par rapport aux autres éléments du scheme. Deux contraintes sont autorisées : before=<id> et after=<id> Où id peut être un séparateur, un identifiant de menu, un élément d’un menu, ou la valeur additions (signifie placer en dernier) keulkeul.blogspot.com Exemple Ajoute un élément avant l’élément Quit menu:file?before=quit situé dans le menu File Commands - M. Baron - Page 38
  39. 39. Commande dans le Workbench La plateforme fournit des identifiants sur les éléments standards org.eclipse.ui.main.menu : identifiant de la barre de menu d’une application Eclipse org.eclipse.ui.main.toolbar : identifiant de la barre d’outils d’une application Eclipse org.eclipse.ui.popup.any : identifiant de tous les menus contextuels Exemples menu:org.eclipse.ui.main.menu : ajout un élément au menu de keulkeul.blogspot.com l’application Eclipse toolbar:viewcommandId : ajout un élément à la barre d’outil de la vue identifiée par viewcommandId Commands - M. Baron - Page 39
  40. 40. Commande dans le menu Pour ajouter une commande à un menu la valeur du scheme de l’attribut locationURI doit être à menu A partir d’un élément menuContribution, plusieurs types d’éléments peuvent être ajoutés à un menu existant Une commande (élément command) Un sous menu (élément menu) Un séparateur (élément separator) keulkeul.blogspot.com Un sous menu dynamique (élément dynamic) Dans la suite des transparents nous présentons la mise en œuvre de certains de ces éléments Commands - M. Baron - Page 40
  41. 41. Commande dans le menu Exemple : ajoute une commande au menu principal d’une application Eclipse Une commande est ajoutée directement à la barre de menu de l’application Eclipse keulkeul.blogspot.com Lors de l’utilisation de la commande une boîte de dialogue est affichée Commands - M. Baron - Page 41
  42. 42. Commande dans le menu Exemple (suite) : ajoute une commande au menu principal d’une application Eclipse Ajout d’une commande au niveau du menu principal de l’application Eclipse keulkeul.blogspot.com plugin.xml du projet CommandsExamples Commands - M. Baron - Page 42
  43. 43. Commande dans le menu Exemple (suite) : ajoute une commande au menu principal Réutilisation d’une commande d’une application Eclipse définie précédemment keulkeul.blogspot.com Ajout d’un élément command L’élément command permet de préciser les caractéristiques graphiques de la commande plugin.xml du projet (texte, image, …) CommandsExamples Commands - M. Baron - Page 43
  44. 44. Commande dans le menu Exemple : ajoute une commande dans un sous menu du menu principal d’une application Eclipse Un menu a été ajouté à la barre de menu principale de l’application Eclipse Lors de l’utilisation d’une commande du menu « Commands Example » une boîte de dialogue est affichée keulkeul.blogspot.com Commands - M. Baron - Page 44
  45. 45. Commande dans le menu Exemple (suite) : ajoute une commande dans un sous menu du menu principal d’une application Eclipse Ajout d’un menu au niveau du menu principal de l’application Eclipse keulkeul.blogspot.com plugin.xml du projet CommandsExamples Commands - M. Baron - Page 45
  46. 46. Commande dans le menu Exemple (suite) : ajoute une commande dans un sous menu du menu principal d’une application Eclipse Caractéristiques graphiques de l’élément menu Ajout d’un élément menu keulkeul.blogspot.com Ajout de deux éléments command à l’élément menu plugin.xml du projet CommandsExamples Commands - M. Baron - Page 46
  47. 47. Commande dans le menu Exemple (suite) : ajoute une commande dans un sous menu du menu principal d’une application Eclipse keulkeul.blogspot.com plugin.xml du projet CommandsExamples Commands - M. Baron - Page 47
  48. 48. Commande dans une barre d’outil Pour ajouter une commande à une barre d’outils la valeur du scheme de l’attribut locationURI doit être à toolbar A partir d’un élément menuContribution, plusieurs types d’éléments peuvent être ajoutés à une barre d’outils Un séparateur (élément separator) Une barre d’outils dynamique (élément dynamic) keulkeul.blogspot.com Un composant graphique (élément control) Dans la suite des transparents nous présentons la mise en œuvre de certains de ces éléments Commands - M. Baron - Page 48
  49. 49. Commande dans une barre d’outil Exemple : ajouter une commande dans la barre d’outil principale d’une application Eclipse Une commande est ajoutée directement à la barre d’outils principale de l’application Eclipse keulkeul.blogspot.com Lors de l’utilisation de la commande une boîte de dialogue est affichée Commands - M. Baron - Page 49
  50. 50. Commande dans une barre d’outil Exemple (suite) : ajouter une commande dans la barre d’outil principale d’une application Eclipse Ajout d’une commande au niveau de la barre d’outils de l’application Eclipse keulkeul.blogspot.com plugin.xml du projet CommandsExamples Commands - M. Baron - Page 50
  51. 51. Commande dans une barre d’outil Exemple (suite) : ajouter une commande dans la barre d’outil principale d’une application Eclipse Identifiant de la nouvelle barre d’outils contenant l’élément command. Possibilité d’étendre directement cette nouvelle barre d’outils keulkeul.blogspot.com Un élément toolbar est ajouté à l’élément plugin.xml du projet menuContribution. Une nouvelle barre d’outils CommandsExamples est ajoutée à la barre d’outils principale Commands - M. Baron - Page 51
  52. 52. Commande dans une barre d’outil Exemple (suite) : ajouter une commande dans la barre d’outil principale d’une application Eclipse Réutilisation d’une commande définie précédemment keulkeul.blogspot.com Ajout d’un élément command à la nouvelle barre d’outils L’élément command permet de préciser les caractéristiques graphiques de la commande plugin.xml du projet (texte, image, …) CommandsExamples Commands - M. Baron - Page 52
  53. 53. Commande dans un menu localisé d’une vue Exemple : ajouter une commande dans le menu localisé d’une vue Menu localisé de la vue contenant une commande keulkeul.blogspot.com Vue …commandsexample.views.viewcommandid du projet CommandsExamples Commands - M. Baron - Page 53
  54. 54. Commande dans un menu localisé d’une vue Exemple (suite) : ajouter une commande dans le menu localisé d’une vue Scheme vaut menu et id pointe sur l’identifiant de la vue keulkeul.blogspot.com La création de l’élément command est identique aux précédents transparents plugin.xml du projet CommandsExamples Commands - M. Baron - Page 54
  55. 55. Commande dans une barre d’outils de vue Exemple : ajouter une commande dans la barre d’outils d’une vue Menu localisé de la vue contenant une commande keulkeul.blogspot.com Vue …commandsexample.views.viewcommandid du projet CommandsExamples Commands - M. Baron - Page 55
  56. 56. Commande dans une barre d’outils de vue Exemple (suite) : ajouter une commande dans la barre d’outils d’une vue Scheme vaut toolbar et id pointe sur l’identifiant de la vue La création de l’élément command est keulkeul.blogspot.com identique aux précédents transparents plugin.xml du projet CommandsExamples Commands - M. Baron - Page 56
  57. 57. Commande dans un menu contextuel de vue Pour ajouter une commande à un menu contextuel d’une vue la valeur du scheme de l’attribut locationURI doit être à popup et l’id doit pointer sur l’identifiant de la vue A partir d’un élément menuContribution, plusieurs types d’éléments peuvent être ajoutés à un menu contextuel Une commande (élément command) Un sous menu (élément menu) Un séparateur (élément separator) keulkeul.blogspot.com Un sous menu dynamique (élément dynamic) Il s’agit des mêmes éléments que pour l’enrichissement d’une barre de menu (générale ou localisée) Commands - M. Baron - Page 57
  58. 58. Commande dans un menu contextuel de vue Exemple : ajouter une commande dans le menu contextuel d’une vue Menu contextuel de la vue contenant plusieurs commandes keulkeul.blogspot.com Vue …commandsexample.views.popupviewcommandid du projet CommandsExamples Commands - M. Baron - Page 58
  59. 59. Commande dans un menu contextuel de vue Exemple (suite) : ajouter une commande dans le menu contextuel d’une vue public class PopupViewPart extends ViewPart { private TableViewer viewer; public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(1, false)); GridData myGridData = new GridData(GridData.FILL_BOTH); viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setContentProvider(new ViewContentProvider()); viewer.setLabelProvider(new ViewLabelProvider()); viewer.setInput(this.getViewSite()); viewer.getControl().setLayoutData(myGridData); this.getSite().setSelectionProvider(viewer); createContextMenu(); } Construction programmatique du keulkeul.blogspot.com ... // Suite dans le prochain transparent } contenu du menu contextuel PopupViewPart.java du projet CommandsExamples Commands - M. Baron - Page 59
  60. 60. Commande dans un menu contextuel de vue Exemple (suite) : ajouter une commande dans le menu contextuel d’une vue public class PopupViewPart extends ViewPart { // Suite du précédent transparent Construction d’une action via l’API private void createContextMenu() { JFace (voir cours Perspectives et Views) final Action action1 = new Action("Action 1") { public void run() { System.out.println("Action 1 Performed"); } }; CommandContributionItemParameter commandParameter = new CommandContributionItemParameter( PlatformUI.getWorkbench(), "contributionitem", "eclipse.workbench.commandsexample.firstcommand", CommandContributionItem.STYLE_PUSH); final IContributionItem ref = new CommandContributionItem(commandParameter); MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); Construction d’une commande via menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager mgr) { IContributionItem (voir dans les prochains transparents) mgr.add(action1); mgr.add(ref); } }); keulkeul.blogspot.com // Create menu. Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); // Register menu for extension. Déclare le menu contextuel dans le } getSite().registerContextMenu(menuMgr, viewer); Workbench, obligatoire pour } l’enrichir via les extensions PopupViewPart.java du projet CommandsExamples Commands - M. Baron - Page 60
  61. 61. Commande dans un menu contextuel de vue Exemple (suite) : ajouter une commande dans le menu contextuel d’une vue Scheme vaut popup et id pointe sur l’identifiant de la vue keulkeul.blogspot.com La création de l’élément command est identique aux précédents transparents plugin.xml du projet CommandsExamples Commands - M. Baron - Page 61
  62. 62. Commande dans un menu contextuel de vue Exemple (suite) : ajouter une commande dans le menu contextuel d’une vue Récupération de la sélection courante Correspond au handler de la commande via le service de sélection ajoutée au menu contextuel public class ShowSelectedHandler extends AbstractHandler { @SuppressWarnings("unchecked") public Object execute(ExecutionEvent event) throws ExecutionException { ISelection selection = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getSelection(); if (selection != null & selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection) selection; for (Iterator<Object> iterator = structuredSelection.iterator(); iterator .hasNext();) { Object element = iterator.next(); System.out.println(element.toString()); keulkeul.blogspot.com } } return null; } } ShowSelectedHandler.java du projet CommandsExamples Commands - M. Baron - Page 62
  63. 63. Commande dans un menu contextuel de vue Exemple : ajouter une commande dans le menu contextuel d’une vue avec une contrainte de positionnement Cette commande doit être placée avant First Command identifiée par contributionitem (définie programmatiquement) keulkeul.blogspot.com Vue …commandsexample.views.popupviewcommandid du projet CommandsExamples Commands - M. Baron - Page 63
  64. 64. Commande dans un menu contextuel de vue Exemple (suite) : ajouter une commande dans le menu contextuel d’une vue avec une contrainte de positionnement Utilisation de la contrainte de placement before=contributionitem pour placer avant la commande keulkeul.blogspot.com First Command plugin.xml du projet CommandsExamples Commands - M. Baron - Page 64
  65. 65. Commande dans un menu contextuel de vue Exemple : ajouter une commande dans tous les menus contextuels des vues Une commande est ajoutée à tous les menus contextuels de vues keulkeul.blogspot.com Commands - M. Baron - Page 65
  66. 66. Commande dans un menu contextuel de vue Exemple (suite) : ajouter une commande dans tous les menus contextuels des vues Scheme vaut popup et id pointe sur l’identifiant org.eclipse.ui.popup.any keulkeul.blogspot.com plugin.xml du projet CommandsExamples Commands - M. Baron - Page 66
  67. 67. Aller plus loin avec menuContribution A partir d’un élément menuContribution, il est également possible d’ajouter deux types d’éléments Dynamic Construction dynamique d’éléments d’un menu ou d’une barre d’outils Nécessite le développement par programmation des éléments à afficher A utiliser si le nombre d’éléments à afficher dans un menu ou une barre d’outils n’est pas connu à l’avance control keulkeul.blogspot.com Création personnalisée de l’élément graphique à afficher (non disponible pour les éléments de menus) A utiliser quand il est intéressant d’afficher autre chose qu’un label Commands - M. Baron - Page 67
  68. 68. Aller plus loin avec menuContribution : dynamic Exemple : ajout dynamique d’éléments dans un menu Les deux commandes sont ajoutées dans le sous menu Dynamic Example keulkeul.blogspot.com Projet CommandsExamples Commands - M. Baron - Page 68
  69. 69. Aller plus loin avec menuContribution : dynamic Exemple (suite) : ajout dynamique d’éléments dans un menu Création d’un élément dynamic Classe de type IContributionItem keulkeul.blogspot.com plugin.xml du projet CommandsExamples Commands - M. Baron - Page 69
  70. 70. Aller plus loin avec menuContribution : dynamic Exemple (suite) : ajout dynamique d’éléments dans un menu A utiliser comme implémentation abstraite de IContributionItem public class ContributionItemExample extends CompoundContributionItem { protected IContributionItem[] getContributionItems() { IContributionItem[] tab = new IContributionItem[2]; CommandContributionItemParameter commandParameter = new CommandContributionItemParameter( PlatformUI.getWorkbench(), "contributionfirstitem", "eclipse.workbench.commandsexample.firstcommand", CommandContributionItem.STYLE_PUSH); IContributionItem ref = new CommandContributionItem(commandParameter); tab[0] = ref; commandParameter = new CommandContributionItemParameter( PlatformUI.getWorkbench(), "contributionseconditem", "eclipse.workbench.commandsexample.secondcommand", CommandContributionItem.STYLE_PUSH); ref = new CommandContributionItem(commandParameter); keulkeul.blogspot.com tab[1] = ref; return tab; Utilisation de commandes définies } } précédemment ContributionItemExample.java du projet CommandsExamples Commands - M. Baron - Page 70
  71. 71. Aller plus loin avec menuContribution : control Exemple : création d’un élément personnalisé dans une barre d’outils Création d’un élément dans la barre d’outils à partir de l’API SWT keulkeul.blogspot.com Projet CommandsExamples Commands - M. Baron - Page 71
  72. 72. Aller plus loin avec menuContribution : control Exemple (suite) : création d’un élément personnalisé dans une barre d’outils Classe de type WorkbenchWindowControlContribution Création d’un élément keulkeul.blogspot.com control plugin.xml du projet CommandsExamples Commands - M. Baron - Page 72
  73. 73. Aller plus loin avec menuContribution : control Exemple (suite) : création d’un élément personnalisé dans une barre d’outils public class WorkbenchWindowCustomControlContribution extends WorkbenchWindowControlContribution { protected Control createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; composite.setLayout(layout); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); final Label label = new Label(composite, SWT.NONE); label.setText("Click"); Composants SWT Button button = new Button(composite, SWT.PUSH); button.setText("Call"); button.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IHandlerService hs = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class); try { hs.executeCommand("eclipse.workbench.commandsexample.firstcommand", null); } catch (Exception e1) { e1.printStackTrace(); keulkeul.blogspot.com } } }); return composite; } } Déclenchement d’une commande WorkbenchWindowCustomControlContribution.java du projet CommandsExamples Commands - M. Baron - Page 73
  74. 74. Raccourci clavier Possibilité d’exécuter une commande via un raccourci clavier en utilisant le point d’extension org.eclipse.ui.bindings A noter que le point d’extension org.eclipse.ui.commands permet de construire des raccourcis clavier mais ces éléments sont DEPRECATED et ne doivent pas être utilisés Dans la suite nous montrons comment construire des keulkeul.blogspot.com raccourcis claviers (key) et des regroupements de raccourcis claviers (scheme) Commands - M. Baron - Page 74
  75. 75. Raccourci clavier Ajouter un extension basée sur le point d’extension org.eclipse.ui.bindings Création d’extension à partir du point d’extension org.eclipse.ui.bindings keulkeul.blogspot.com Commands - M. Baron - Page 75
  76. 76. Raccourci clavier Ajouter un élément key permettant de construire un raccourci clavier keulkeul.blogspot.com plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 76
  77. 77. Raccourci clavier Construire un raccourci clavier actif quand la touche F11 est déclenchée keulkeul.blogspot.com plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 77
  78. 78. Raccourci clavier Paramétrer les attributs d’un élément key sequence définit la séquence de touches schemeId définit le regroupement de du clavier qui active la commande raccourcis clavier. Par défaut org.eclipse.ui.defaultAcceleratorConfiguration keulkeul.blogspot.com platform définit le système sur commandId définit l’identifiant lequel le raccourci est défini (win32, de la commande à appeler lors gtk, motif, carbon, photon) de l’utilisation de la touche F11 Commands - M. Baron - Page 78
  79. 79. Raccourci clavier : sequence L’attribut sequence est utilisée pour définir une séquence de raccourcis clavier Plusieurs raccourcis clavier peuvent être spécifiés dans une séquence séparés par un espace Chaque raccourcis clavier se compose d’une ou plusieurs touches maintenues enfoncées et sont séparées par + Les modifiers (touches spécifiques) reconnus sont : M1, M2, M3, M4, ALT, COMMAND, CTRL et SHIFT M1 : COMMAND sur MAC OS X et CTRL sur les autres plateformes M2 : SHIFT keulkeul.blogspot.com M3 : OPTION sur MAC OS X et ALT sur les autres plateformes M4 : CTRL sur MAC OS X et indéfinis pour les autres plateformes Préférez l’utilisation de M1, M2, M3 et M4 pour être indé- pendant de la plateforme cible Commands - M. Baron - Page 79
  80. 80. Raccourci clavier : sequence Différents exemples de séquence CTRL+L : maintenir enfoncées les touches CTRL et L M1+L : maintenir enfoncées les touches COMMAND et L (MAC OS X) ou CTRL et L sur les autres systèmes CTRL+L F11 F12 : maintenir enfoncées les touches CTRL et L puis la touche F11 et enfin la touche F12 keulkeul.blogspot.com Quand plusieurs raccourcis claviers se succèdent une fenêtre flottante contextualise l’interaction en cours Commands - M. Baron - Page 80
  81. 81. Raccourci clavier : scheme Un scheme est un regroupement de raccourcis clavier org.eclipse.ui.defaultAcceleratorConfiguration est l’identifiant par défaut de la plateforme Eclipse Pour construire et utiliser son propre scheme vous devrez Définir un scheme via le point d’extension org.eclipse.ui.bindings Attribuer l’identifiant du scheme à l’attribut schemeId de l’élément key keulkeul.blogspot.com Posséder un product (extrait du cours sur Eclipse RCP) Définir un fichier de configuration et positionner l’identifiant du scheme dans une valeur de propriétés Commands - M. Baron - Page 81
  82. 82. Raccourci clavier : scheme Définir un scheme via le point d’ext. org.eclipse.ui.bindings Construction d’un élément scheme permettant de regroupant de raccourcis clavier keulkeul.blogspot.com plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 82
  83. 83. Raccourci clavier : scheme Définir un scheme via le point d’ext. org.eclipse.ui.bindings Définition de l’identifiant du scheme Précise le nom du scheme keulkeul.blogspot.com Relation d’héritage avec un scheme déjà créé plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 83
  84. 84. Raccourci clavier : scheme Attribuer l’identifiant du scheme à l’attribut schemeId de key Définition de la séquence du raccourci clavier Préciser l’identifiant du scheme précédemment créé keulkeul.blogspot.com L’identifiant de la commande qui sera appelée plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 84
  85. 85. Raccourci clavier : scheme (création d’un product) Construction d’une application Eclipse RCP Construction d’un nouveau projet Plug-in (File -> New -> Project -> Plug-in Project) keulkeul.blogspot.com Une présentation détaillée sur la manière de construire un product sera donnée dans la partie Eclipse RCP Commands - M. Baron - Page 85
  86. 86. Raccourci clavier : scheme (création d’un product) Construction d’une application Eclipse RCP (suite) Différents paramétrages identiques à ceux utilisés lors de la création d’un plug-in keulkeul.blogspot.com S’assurer que l’option Rich Client Application est active Commands - M. Baron - Page 86
  87. 87. Raccourci clavier : scheme (création d’un product) Construire le fichier de configuration du product Construction d’un nouveau projet Plug-in (File -> New -> Other … -> Product Configuration) Choisir le projet où sera créé le product Définir le nom du product keulkeul.blogspot.com Créer le product à partir d’une configuration allégée Commands - M. Baron - Page 87
  88. 88. Raccourci clavier : scheme (création d’un product) Configuration du fichier de configuration du product Nécessite la création de l’identifiant du product keulkeul.blogspot.com Commandskeybindingrcpexamples.product du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 88
  89. 89. Raccourci clavier : scheme (création d’un product) Configuration du fichier de configuration du product (suite) Projet où l’identifiant du product sera créé Identifiant du product keulkeul.blogspot.com Identifiant de l’application sur laquelle le product est associé Commands - M. Baron - Page 89
  90. 90. Raccourci clavier : scheme (création d’un product) Configuration du fichier de configuration du product (suite) Lors de la création de l’identifiant une extension basée sur org.eclipse.core.runtime.products a été créée keulkeul.blogspot.com Nom donné à l’identifiant du product plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 90
  91. 91. Raccourci clavier : scheme (création d’un product) Définir un fichier de configuration des préférences utilisateur Clé (KEY_CONFIGURATION_ID) permettant d’activer un scheme Identifiant du scheme qui doit être rendu actif org.eclipse.ui/KEY_CONFIGURATION_ID = eclipse.workbench.commandskeybindingrcpexamples.specificdefaultacceleratorconfigurationid org.eclipse.ui/SHOW_PROGRESS_ON_STARTUP = false pluginpreference.ini du projet CommandsKeyBindingRCPExamples keulkeul.blogspot.com Le fichier pluginpreference.ini est placé à la racine du projet Commands - M. Baron - Page 91
  92. 92. Raccourci clavier : scheme (création d’un product) Déclarer le fichier pluginpreference.ini dans le fichier de confi- guration du product Définition d’une propriété à l’extension products pour keulkeul.blogspot.com configurer le product plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 92
  93. 93. Raccourci clavier : scheme (création d’un product) Déclarer le fichier pluginpreference.ini dans le fichier de confi- guration du product (suite) Nom de la propriété à ajouter : preferenceCustomization Nom du fichier de configuration : keulkeul.blogspot.com pluginpreference.ini plugin.xml du projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 93
  94. 94. Raccourci clavier : scheme Exécuter l’application et reproduire la séquence : F9 Lors de l’appui sur la touche F9, la commande keyspecificcommandid est déclenchée keulkeul.blogspot.com Projet CommandsKeyBindingRCPExamples Commands - M. Baron - Page 94
  95. 95. Restrictions : généralités L’API Commands permet d’appliquer des restrictions sur les handlers et sur les éléments menuContributions Au niveau des handlers les restrictions sont activeWhen : si le handler est inactif, aucune commande n’est associée à ce handler enabledWhen : si le handler est désactivé, l’exécution du handler ne peut être réalisée (si activé doit obligatoirement être actif) Si handler est désactivé la commande apparaîtra grisée si elle est affichée dans un menu (peut importe si le handler est actif ou inactif) Au niveau des éléments menuContributions la restriction est keulkeul.blogspot.com visibleWhen : si invisible, l’élément graphique associé à la commande ne s’affiche pas (un élément dans un menu par exemple) La description de restrictions est réalisée par le langage Core Expressions Commands - M. Baron - Page 95
  96. 96. Restrictions : exemples Exemple : Afficher une commande dans la barre d’outils principale quand un élément est sélectionné Un élément dans le service de sélection keulkeul.blogspot.com La commande est affichée dans Pas d’élément dans le la barre d’outils principale service de sélection Commands - M. Baron - Page 96
  97. 97. Restrictions : exemples Exemple (suite) : Afficher une commande dans la barre d’outils principale quand un élément est sélectionné Une restriction visibleWhen au niveau d’un élément menuContribution Commande est affichée quand un keulkeul.blogspot.com seul élément de la sélection est sélectionné plugin.xml du projet CommandsExpressionsExamples Commands - M. Baron - Page 97
  98. 98. Restrictions : exemples Exemple : Afficher une commande dans la barre d’outils principale quand une vue est active La vue « View Command Expression » La vue « View Command Expression » n’est pas active est active keulkeul.blogspot.com La commande « Second Command La commande « Second Command Expression » est maintenant activée Expression » est désactivée Commands - M. Baron - Page 98
  99. 99. Restrictions : exemples Exemple (suite) : Afficher une commande dans la barre d’outils principale quand une vue est active Une restriction enabledWhen au niveau d’un élément handler keulkeul.blogspot.com Commande est activée quand la vue plugin.xml du projet active est « View Command Expression » CommandsExpressionsExamples Commands - M. Baron - Page 99
  100. 100. Restrictions : construire Expression Definitions Dans les exemples précédents nous avons montré comment construire des expressions en les associant directement aux différentes restrictions (visibleWhen, enabledWhen, …) Il peut être intéressant de mutualiser ces expressions de manière à les réutiliser sans avoir à recopier les définitions Le point d’extension org.eclipse.core.expressions.definitions permet de construire ces expressions Les restrictions utiliseront ensuite une référence à la nouvelle keulkeul.blogspot.com définition Ajouter une dépendance sur org.eclipse.core.expressions pour exploiter le point d’extension org.eclipse.core.expressions.definitions Commands - M. Baron - Page 100
  101. 101. Restrictions : construire Expression Definitions Point d’extension permettant de créer des définitions d’expression keulkeul.blogspot.com Commands - M. Baron - Page 101
  102. 102. keulkeul.blogspot.com Restrictions : construire Expression Definitions Création d’une définition d’expression : plugin.xml du projet un élément du service de sélection CommandsExpressionsExamples Commands - M. Baron - Page 102
  103. 103. Restrictions : utiliser Expression Definitions Définition d’un nouvel élément menuContribution avec une restriction visibleWhen dont l’expression a été définie précédemment Utilisation du sous élément keulkeul.blogspot.com reference de visibleWhen (s’appliquerait également à enabledWhen et activeWhen) plugin.xml du projet CommandsExpressionsExamples Commands - M. Baron - Page 103
  104. 104. Restrictions : utiliser Expression Definitions Pas d’élément dans le service de sélection keulkeul.blogspot.com Lors de la sélection d’un élément, les deux commandes sont affichées dans la barre d’outils principale Commands - M. Baron - Page 104
  105. 105. Restrictions : Core Expressions en détail Nous avons montré dans les exemples précédent des expressions basiques La plateforme Eclipse fournit un ensemble d’éléments pour la définition d’expressions plus complexes Dans la suite, nous nous proposons d’étudier chacun de ces éléments keulkeul.blogspot.com Commands - M. Baron - Page 105

×