Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Eclipse : développement d'éditeurs

Eclipse : développement d'éditeurs

Ce support de cours s'intéresse à détailler la construction d'éditeurs 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 : construction déclarative, registre des éditeurs, cycle de vie, MultiPageEditorPart, écouteurs, éditeur et les commandes, Workspace et les ressources, éditeur et le texte via TextEditor (coloration syntaxique, assistant de contenu, template, outline, spelling checking...).

Mickael BARON

June 25, 2012
Tweet

More Decks by Mickael BARON

Other Decks in Programming

Transcript

  1. Développement de clients riches : Plateforme Eclipse Mickaël BARON -

    2010 mailto:[email protected] ou mailto:[email protected] mickael-baron.fr mickaelbaron Chapitre 3 : Conception de plug-ins Workbench : Editors
  2. 2 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Creative

    Commons Contrat Paternité Partage des Conditions Initiales à l'Identique 2.0 France http://creativecommons.org/licenses/by-sa/2.0/fr Licence
  3. 3 Editors - M. Baron - Page mickael-baron.fr mickaelbaron 3

    À propos de l’auteur … † Mickaël BARON † Ingénieur de Recherche au LIAS † https://www.lias-lab.fr † Equipe : Ingénierie des Données et des Modèles † Responsable des plateformes logicielles, « coach » technique † Ancien responsable Java de Developpez.com (2011-2021) † Communauté Francophone dédiée au développement informatique † https://java.developpez.com † 4 millions de visiteurs uniques et 12 millions de pages vues par mois † 750 00 membres, 2000 forums et jusqu'à 5000 messages par jour mickael-baron.fr mickaelbaron
  4. 4 Editors - M. Baron - Page mickael-baron.fr mickaelbaron †

    Construction déclarative † IEditorPart, IEditorSite, IEditorInput † Registre des éditeurs † Cycle de vie † MultiPageEditorPart † Ecouteurs † Editeur et les commandes † Workspace et les ressources † Editeur et le texte via TextEditor Organisation du cours sur le Workbench : Editors
  5. 5 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workbench

    Editors : déroulement du cours Ceci est une alerte Ceci est une astuce † Pédagogie du cours † Illustration avec de nombreux exemples qui sont disponibles à l’adresse mickael-baron.fr/eclipse/editors † Des bulles d’aide tout au long du cours † Logiciels utilisés † Eclipse 3.5.2 Galileo † Pré-requis † Connaître la structure d’un plug-ins et savoir créer une extension † Structure du Workbench
  6. 6 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workbench

    Editors : ressources … † Des liens sur les éditeurs de manière générale † www.vogella.de/articles/RichClientPlatform/article.html#editor † wiki.eclipse.org/FAQ_What_is_the_difference_between_a_view_and_an_editor%3F † wiki.eclipse.org/FAQ_How_do_I_open_an_editor_on_something_that_is_not_a_file%3F † eclipse.dzone.com/tips/programmatically-split-editor- † www.pajbam.com/wp-content/uploads/2008/04/cours_plugin_eclipse.pdf † Des liens sur le Workspace (gestion de fichiers) † wiki.eclipse.org/FAQ_How_do_I_open_an_editor_on_a_file_outside_the_workspace%3F † www.eclipsezone.com/eclipse/forums/t83786.html † wiki.eclipse.org/index.php/EFS † www.eclipse.org/eclipse/platform-team/docs/ABC%20of%20Platform%20Workspace.ppt † www.eclipse.org/articles/Article-Resource-deltas/resource-deltas.html † hexapixel.com/2009/01/12/rcp-workspaces † www.eclipse.org/articles/Article-Mark My Words/mark-my-words.html † www.eclipse.org/articles/Article-Builders/builders.html † wiki.eclipse.org/FAQ_How_do_I_make_my_compiler_incremental%3F † wiki.eclipse.org/FAQ_How_do_I_implement_an_Eclipse_builder%3F † www.eclipsepluginsite.com/builders-natures-markers.html
  7. 7 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workbench

    Editors : ressources … † Des liens sur l’amélioration d’un TextEditor † beuss.developpez.com/tutoriels/eclipse/plug-in/editor/colors † www.eclipse.org/eclipse/platform-text/development/dev.html † wiki.eclipse.org/FAQ_How_do_I_provide_syntax_coloring_in_an_editor%3F † wiki.eclipse.org/FAQ_What_is_a_document_partition%3F † wiki.eclipse.org/FAQ_How_do_I_add_Content_Assist_to_my_editor%3F † www.eclipse.org/articles/Article-Folding-in-Eclipse-Text-Editors/folding.html † www.ibm.com/developerworks/opensource/library/os-ecca † www.java2s.com/Code/Java/SWT-JFace-Eclipse/SWTCompletionEditor.htm † Des livres † Eclipse – Building Commercial-Quality Plug-ins, 2004 - ISBN : 0-321-22847-2 † Eclipse – Rich Client Platform, 2005 – ISBN : 0-321-33461-2
  8. 8 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Différences entre Vue et Editeur † Un éditeur est commun à toutes les perspectives d’une fenêtre † Si l’éditeur est fermé à partir d’une perspective il est fermé pour toutes les perspectives † Il n’est pas possible d’empiler une vue avec un éditeur † Un éditeur n’est pas détachable † Un éditeur a obligatoirement une barre de titre † Un éditeur n’a pas de barre de menus et de barre d’outils localisées, il partage avec les barres de la fenêtre
  9. 9 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Différences entre Vue et Editeur † Un éditeur peut être instancié plusieurs fois pour un type d’éditeur donné † Une vue ne possède qu’une seule instance (cas particulier avec l’identifiant secondaire) † Un éditeur apparaît à un seul endroit de la page alors qu’une vue peut être déplacée † Un éditeur peut être dans un état « modifié », son contenu peut ainsi être sauvegardé † Un éditeur peut être associé à un nom de fichier ou à une extension et cette association peut être modifiée par l’utilisateur
  10. 10 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Usage de l’éditeur † Les fausses idées concernant un éditeur … † Editeur est dédié uniquement à l’affichage et à la manipulation du contenu d’un fichier † Une vue peut réaliser la même chose † Editeur est dédié à l’affichage du texte † Un éditeur peut aussi afficher des IHMs types formulaires (PDE) † La vue console affiche uniquement du texte † Quand utiliser un éditeur ? † Quand le contenu est considéré comme l’élément central de la fenêtre. Toutes les vues sont utilisées comme support (outline, explorer, …) † Quand il est nécessaire de fournir des actions spécifiques pour l’édition (sauvegarde, menu contextuel, …)
  11. 11 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Editeur interne et externe † Editeur interne † Intégré dans une page d’une fenêtre du Workbench † Le menu et la barre d’outils sont issus de la fenêtre du Workbench † Editeur externe (System Editor) † Démarré dans une fenêtre séparée au Workbench † Après le démarrage d’un éditeur externe, le Workbench n’a plus la main sur l’état de l’éditeur (pas de possibilité d’arrêt) † La collaboration ne peut s’effectuer que par le système de fichier † Editeur mixte (In-Place Editor) † Intégré dans une page d’une fenêtre du Workbench, un éditeur externe (intégration OLE) † Le menu de l’éditeur externe intégré utilise le menu de la fenêtre du Workbench
  12. 12 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Editeur interne et externe † Exemple : Utilisation d’un éditeur Word en mode « In-Place » L’éditeur externe Word est intégré dans une page d’une WorkbenchWindow Le menu de l’application Eclipse est complété par les éléments du menu de Word
  13. 13 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Editeur par défaut † Un éditeur par défaut est celui à utiliser en priorité si d’autres éditeurs traitent le même type d’entrées † Exemple : fichier xml † Plusieurs éditeurs disponibles (texte, xml, m2eclipse, …) † Si l’éditeur xml est utilisé comme défaut, il sera ouvert en priorité † Lors de la définition d’un éditeur (voir extension editors), il est possible de spécifier qu’un éditeur est par défaut † Un éditeur est définit implicitement par défaut, si aucun autre éditeur ne peut traiter son type d’entrée † Si un fichier est ouvert avec un éditeur, cet éditeur ne devient pas par défaut mais sera utilisé pour ré-ouvrir de nouveau ce fichier
  14. 14 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Carte des classes de l’éditeur † Principales interfaces et classes pour la gestion des éditeurs IWorkbenchPage activate(IWorkbenchPart) bringToTop(IWorkbenchPart) findEditor(String) getEditorReferences() hideEditor(IEditorReference) showEditor(IEditorReference) IWorkbenchPartReference getId() getPage() getPart(boolean) IWorkbenchSite getPage() getSelectionProvider() getShell() getWorkbenchWindow() setSelectionProvider(ISelectionProvider) IWorkbenchPart createPartControl(Composite) dispose() getSite() IEditorPart getEditorSite() getEditorInput() init(IEditorSite, IEditorInput) IWorkbenchPartSite getKeyBindingService() registerContextMenu(MenuManager, ISelectionProvider) registerContextMenu(String, MenuManager, ISelectionProvider) IEditorReference getEditor(boolean) getEditorInput() getFactoryId() getName() isPinned() WorkbenchPart dispose() getConfigurationElement() getSite() setPartName(String) EditorPart getEditorSite() getEditorInput() init(IEditorSite, IEditorInput) IEditorSite getActionBars() getActionBarContributor() EditorInput getEditorSite() getEditorInput() init(IEditorSite, IEditorInput) Dépendance Héritage Généraliste Spécifique à un éditeur
  15. 15 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    : Principales classes de l’éditeur † IEditorPart : Contient le code de l’IHM de l’éditeur † IEditorSite : Pont entre l’éditeur et le Workbench † IEditorInput : Descripteur du contenu d’un éditeur † IEditorDescriptor : Descripteur utilisé dans le registre des éditeurs † IEditorReference : Représenter une instance d’un éditeur dans la page active † IEditorActionBarContributor : Utilisé pour contribuer à l’ajout d’actions dans la barre de menus et d’outils
  16. 16 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur interne par extension † Sélectionner le point d’extension org.eclipse.ui.editors Création d’une extension à partir du point d’extension org.eclipse.ui.editors Création d’un éditeur à partir des templates Editor et Multi-page Editor
  17. 17 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur interne par extension † Définir les attributs de l’extension Identifiant de l’éditeur Nom de l’éditeur Extensions des fichiers (ex : html) gérées par l’éditeur Objet de type EditorPart implémentant le contenu d’un éditeur interne Utiliser pour démarrer un éditeur externe (mutuellement exclusif avec class) Objet de type IEditorActionBarContributor utilisé pour ajouter des actions à la barre de menu et à la barre d’outils Les noms des fichiers gérés par l’éditeur L’attribut default permet d’indiquer que cet éditeur est à utiliser en priorité pour les fichiers dont l’extension est html ou txt Utilisation d’une police par défaut
  18. 18 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur interne par extension † Etendre un éditeur de type EditorPart package eclipse.workbench.viewexample.views public class SimpleEditor extends EditorPart { public SimpleEditor() { } public void doSaveAs() { } public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); setInput(input); } public boolean isDirty() { return false; } public boolean isSaveAsAllowed() { return false; } public void createPartControl(Composite parent) { Label myLabel = new Label(parent, SWT.NONE); myLabel.setText("Simple View"); } public void setFocus() { } public void doSave(IProgressMonitor monitor) { } } SimpleEditor.java du Projet EditorExample
  19. 19 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur interne par extension † Pour utiliser cet éditeur plusieurs solutions sont à envisager † Ouvrir un fichier au format html ou txt (File -> Open File …) † Faire appel explicitement à la méthode openEditor à partir d’un WorkbenchPage (voir plus tard) † A noter que si aucun éditeur n’est défini pour une extension donnée d’un fichier † Ce fichier n’est pas ouvert par un éditeur Eclipse † Un éditeur externe (System Editor) est utilisé si l’extension a été associée à un éditeur † Si l’éditeur d’Eclipse est défini comme éditeur par défaut, le fichier est automatiquement ouvert dans cet éditeur † Il est possible d’utiliser d’autres éditeurs en utilisant le menu Open With
  20. 20 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur interne par extension † Exemple : Ouverture du fichier pom.xml par trois éditeurs Plusieurs éditeurs peuvent éditer ce fichier Via l’éditeur de M2 Eclipse Via l’éditeur XML Des éditeurs fournis par Eclipse L’éditeur système Via l’éditeur sytème « Firefox »
  21. 21 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur externe par extension † Précédemment l’attribut class a permis de développer un éditeur interne † Pour développer des éditeurs externes il faut utiliser les attributs launcher ou command † A noter que class, launcher et command sont mutuellement exclusif † Utilisation de l’attribut launcher † Nécessite la création d’une classe implémentant IEditorLauncher † void open(IPath file) : appelée pour ouvrir le fichier dont le chemin est passé en paramètre † Utilisation de l’attribut command † Commande du système (par exemple : notepad.exe) † Ne pas confondre avec les commandes Eclipse !!!!
  22. 22 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur externe par extension † Exemple : Création d’un éditeur externe via launcher L’attribut launcher est renseigné Si l’attribut launcher doit être renseigné, ne pas modifier les attributs class et command Cet éditeur est associé aux fichiers dont l’extension est extlaunch
  23. 23 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur externe par extension † Exemple (suite) : Création d’un éditeur externe via launcher public class SimpleEditorEditorLauncher implements IEditorLauncher { public void open(IPath file) { final Program findProgram = Program.findProgram("txt"); boolean result = findProgram.execute(file.toFile().getAbsolutePath()); System.out.println("External Editor has been launched ? : " + result); } } SimpleEditor.java du Projet EditorExample Utilisation de l’API Program pour démarrer un éditeur externe Recherche au niveau du système d’exploitation le programme associé à l’extension txt (pour être sur d’obtenir un résultat) Exécution du programme (notepad.exe) en donnant en paramètre le chemin du fichier
  24. 24 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur externe par extension † Exemple (suite) : Création d’un éditeur externe via launcher Utilisation de l’éditeur interne texte (via Open With) Utilisation de l’éditeur externe (notepad.exe) (également via Open With)
  25. 25 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d’un éditeur externe par extension † Exemple : Création d’un éditeur externe via command L’attribut command est renseigné Cet éditeur est associé aux fichiers dont l’extension est extCommand Si l’attribut command doit être renseigné, ne pas modifier les attributs class et launcher Très simple d’utilisation mais ne permet pas de définir des paramètres dérivés
  26. 26 Editors - M. Baron - Page mickael-baron.fr mickaelbaron EditorPart

    † La définition d’un nouvel éditeur est obtenue en héritant de la classe EditorPart † Des méthodes abstraites doivent être implémentées † void init(IEditorSite site, IEditorInput input) : initialise l’éditeur † void createPartControl(Composite parent) : création de l’IHM par des composants SWT où parent est le conteneur de la vue † void setFocus() : composant qui aura le focus lors de l’activation † void doSave(IProgressMonitor monitor) : sauvegarde le contenu de l’éditeur † void doSaveAs() : sauvegarde le contenu de l’éditeur dans un autre emplacement † boolean isDirty() : indique si le contenu a changé † boolean isSaveAsAllowed() : « Save As » est-il supporté ? † boolean isSaveOnCloseNeeded() : La sauvegarde est-elle nécessaire avant la fermeture de l’éditeur ?
  27. 27 Editors - M. Baron - Page mickael-baron.fr mickaelbaron EditorPart

    † Des méthodes qui peuvent être redéfinies … † void setInitializationData(…) : appelée lors de la création de l’extension † String getTitleToolTip() : message pour la bulle d’aide de l’éditeur † Des méthodes à exploiter … † IEditorSite getEditorSite() : retourne le site de l’éditeur (étudier par la suite) † IEditorInput getEditorInput() : retourne l’EditorInput de l’éditeur (étudier par la suite) † void setPartName(String ti) : modifie directement le titre de l’éditeur en place d’utiliser les données de l’extension
  28. 28 Editors - M. Baron - Page mickael-baron.fr mickaelbaron EditorPart

    et IEditorSite † Le site d’un éditeur est une sorte de pont entre l’éditeur (définit par EditorPart) et le Workbench † Il faut voir le site comme un objet qui permet de lier l’éditeur au contexte de l’application † L’interface IWorkbenchSite est une abstraction d’un site † IWorkbenchPage getPage() : la page dans laquelle l’éditeur est stockée † Shell getShell() : la fenêtre physique d’où est contenue l’éditeur † IWorkbenchWindow getWorkbenchWindow() : la fenêtre déclarative contenant l’éditeur † void registerContextMenu(MenuManager mM, ISelectionProvider sP) : expose un menu vers le Workbench pour permettre son extension ultérieur † void setSelectionProvider(ISelectionProvider sP) : déclare un SelectionProvider (TableViewer par exemple) au service de sélection
  29. 29 Editors - M. Baron - Page mickael-baron.fr mickaelbaron EditorPart

    et IEditorSite † L’interface IEditorSite (sous type de IWorkbenchSite) fournit des méthodes spécifiques † IActionBars getActionBars() : la barre d’action de l’éditeur † IEditorActionBarContributor getActionBarContributor() : retourne un objet IEditorActionBarContributor utilisé pour la création des actions A l’ouverture de l’éditeur des commandes (ou actions) sont ajoutées à la barre d’action et menu L’ajout d’éléments dans la barre de menu et la barre d’outils sera étudié dans la suite
  30. 30 Editors - M. Baron - Page mickael-baron.fr mickaelbaron IEditorInput

    † Un objet IEditorInput est un descripteur léger sur le contenu d’un éditeur † Il ne s’agit pas du modèle de l’éditeur mais d’une description sur la source de l’éditeur utilisé par l’EditorPart † A chaque création d’une nouvelle instance d’EditorPart, un objet de type IEditorInput doit être passé en paramètre † L’éditeur à ouvrir doit supporter l’objet IEditorInput passé en paramètre † Possibilité d’adapter un objet IEditorInput en autre objet puisque l’objet est de type IAdaptable † Exemple : adapter un CustomEditorInput en FileEditorInput de telle sorte que CustomEditorInput soit utilisable par des éditeurs manipulant des fichiers
  31. 31 Editors - M. Baron - Page mickael-baron.fr mickaelbaron IEditorInput

    † Principales méthodes de IEditorInput † boolean exists() : vérifie si l’éditeur existe † ImageDescriptor getImageDescriptor() : retourne une image † String getToolTipText() : retourne le texte de la bulle d’aide † String getName() : retourne un nom † En pratique un éditeur qui édite le contenu d’un fichier utilise en FileEditorInput † Contient un attribut de type File permettant d’accéder au contenu du fichier † Principales méthodes de FileEditorInput † IFile getFile() : retourne le fichier contenant la source Lors de la définition de votre propre IEditorInput redéfinissez obligatoirement la méthode equals
  32. 32 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des éditeurs † Le registre des éditeurs est utilisé par le conteneur Eclipse pour stocker l’intégralité des éditeurs créés † Il n’existe qu’un seul registre d’éditeur géré par le Workbench † A partir de ce registre il est possible de † Chercher un éditeur par son identifiant † Chercher tous les éditeurs associés à un nom de fichier † Chercher l’éditeur par défaut par son nom IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry(); L’interface IEditorRegistry est détaillée dans la suite
  33. 33 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des éditeurs † L’interface IEditorRegistry dispose des méthodes suivantes † IEditorDescriptor findEditor(String id) : recherche un éditeur par son id † IEditorDescriptor[] getEditors(String fileName) : retourne les éditeurs disponibles pour un fichier donné † IEditorDescriptor getDefaultEditor(String fileName) : retourne l’éditeur par défaut pour un fichier donné † boolean isSystemExternalEditorAvailable(String fileName) : vérifie s’il existe un éditeur externe disponible pour un fichier donné † void setDefaultEditor(String fileNameOrExt, String id) : modifie l’éditeur par défaut suivant le nom de fichier ou l’extension † Un objet IEditorDescriptor décrit un éditeur du point de vue de sa déclaration (informations de l’extension)
  34. 34 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des éditeurs † Exemple : Modifier et accéder à l’éditeur par défaut Deux commandes permettent de modifier l’éditeur par défaut des fichiers de type html La commande Launcher Editor Default associe l’éditeur SimpleExternalEditorWithLauncherId au fichier html Le traitement de cette commande • affiche l’ancien éditeur par défaut • affecte le nouveau éditeur par défaut • affiche enfin le nouveau éditeur par défaut 1 2 3
  35. 35 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des éditeurs † Exemple (suite) : Modifier et accéder à l’éditeur par défaut public class LauncherEditorCommandHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { final IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry(); IEditorDescriptor defaultEditor = editorRegistry.getDefaultEditor("*.html"); if (defaultEditor != null) { System.out.println(defaultEditor.getLabel()); } editorRegistry.setDefaultEditor("*.html","eclipse.workbench.EditorExample.SimpleExternalEditorWithLauncherId"); defaultEditor = editorRegistry.getDefaultEditor("*.html"); if (defaultEditor != null) { System.out.println(defaultEditor.getLabel()); } return null; } } LauncherEditorCommandHandler.java du Projet EditorExample Récupération de l’éditeur par défaut des fichiers dont l’extension est html Modification de l’éditeur par défaut des fichiers de type html
  36. 36 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des éditeurs † L’interface IEditorDescriptor décrit l’éditeur dans le registre des éditeurs (relation avec les extensions) † String getId() : identifiant de l’éditeur † String getLabel() : le nom de l’éditeur † ImageDescriptor getImageDescriptor() : image de l’éditeur † boolean isInternal() : utilise un éditeur interne pour l’ouverture ? † boolean isOpenExternal() : utilise un éditeur externe pour l’ouverture ? † boolean isOpenInPlace() : utilise un éditeur externe intégré à Eclipse ? † … † Le registre ne donne pas accès aux instances des objets IEditorPart (voir cycle de vie)
  37. 37 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des éditeurs † Exemple : Interroger le registre des éditeurs public class EditorRegistryCommandHandler extends AbstractHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { final IEditorRegistry editorRegistry = PlatformUI.getWorkbench().getEditorRegistry(); final IEditorDescriptor[] editors = editorRegistry.getEditors("*.txt"); for (IEditorDescriptor iEditorDescriptor : editors) { System.out.println(iEditorDescriptor.getLabel()); } return null; } } EditorRegistryCommandHandler.java du Projet EditorExample Récupère les éditeurs associés à ce type d’extension Deux éditeurs sont disponibles
  38. 38 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie † Un objet IWorkbenchPage permet de gérer le cycle de vie des instances d’une classe IEditorPart (celle utilisée pour programmer l’interface d’un éditeur) † Chaque instance de type IEditorPart est associée un objet de type IEditorReference † Une instance IEditorReference est associée une instance IEditorPart † Il peut exister dans une même page plusieurs instances du même éditeur (pas d’identifiant secondaire) † Les méthodes fournies par la page permettent généralement d’afficher ou de cacher des éditeurs
  39. 39 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie † Principales méthodes de IWorkbenchPage en relation avec la notion d’éditeur † IEditorPart getActiveEditor() : l’éditeur actuellement actif † boolean closeAllEditors(boolean save) : ferme tous les éditeurs sans possibilité de sauvegarde si save vaut false † boolean closeEditor(IEditorPart e, boolean save) : ferme un éditeur désigné par e † IEditorPart findEditor(IEditorInput e) : cherche un éditeur via son IEditorInput † IEditorPart[] getDirtyEditors() : retourne l’ensemble des éditeurs dont le contenu a changé (nécessite une sauvegarde) † IEditorReferences[] getEditorReferences() : retourne l’ensemble des éditeurs † IEditorPart openEditor(…) : ouvre un éditeur (détailler dans le prochain transparent)
  40. 40 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Le comportement de l’ouverture d’un éditeur est différent selon la configuration utilisée pour la méthode openEditor † Trois configurations différentes † Ouvre un nouvel éditeur s’il n’existe pas un éditeur ayant un même input (MATCH_INPUT) † Ouvre un nouvel éditeur s’il n’existe pas un éditeur ayant un même identifiant (MATCH_ID) † Ouvre un nouvel éditeur même s’il existe un éditeur ayant un même input ou un même identifiant (MATCH_NONE) † Surcharge de la méthode openEditor † IEditorPart openEditor(IEditorInput input, String editorID) : ouvre un éditeur s’il n’existe pas un éditeur ouvert ayant un même input † IEditorPart openEditor(final IEditorInput input, final String editorID, final boolean activate, final int matchFlags) : ouvre un éditeur en spécifiant la configuration
  41. 41 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple : Ouvrir un éditeur pour un fichier présent dans le Workspace public class OpenEditorCommandHandler extends AbstractHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); ISelectionService selService = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService(); final ISelection selection = selService.getSelection(); if (selection instanceof TreeSelection) { TreeSelection currentFile = (TreeSelection)selection; final IFile firstElement = (IFile)currentFile.getFirstElement(); IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(firstElement.getName()); try { activePage.openEditor(new FileEditorInput(firstElement), desc.getId()); } catch (PartInitException e) { e.printStackTrace(); } } return null; } } OpenEditorCommandHandler.java du Projet EditorExample Récupération de l’éditeur associé au type du fichier sélectionné (ici le fichier sample.html) Ouverture de l’éditeur s’il n’existe pas un éditeur déjà ouvert ayant le même input
  42. 42 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple (suite) : Ouvrir un éditeur pour un fichier présent dans le Workspace Les fichiers doivent être présents dans le répertoire du workspace pour être ouvert via la méthode open(…) L’utilisation de la méthode openEditor ne s’applique que pour les fichiers présents dans le Workspace, sauf cas …
  43. 43 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple : Ouvrir un éditeur pour un fichier non présent dans le Workspace (solution 1) public class OpenExternalEditorCommandHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { try { IWorkspace ws = ResourcesPlugin.getWorkspace(); IProject project = ws.getRoot().getProject("SimpleEditor"); if (!project.exists()) project.create(null); if (!project.isOpen()) project.open(null); final IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); final Shell shell = activeWorkbenchWindow.getShell(); String name = new FileDialog(shell, SWT.OPEN).open(); if (name == null) return null; IPath location = new Path(name); IFile file = project.getFile(location.lastSegment()); file.createLink(location, IResource.NONE, null); IWorkbenchPage page = activeWorkbenchWindow.getActivePage(); if (page != null) IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName()); page.openEditor(new FileEditorInput(file), desc.getId()); } catch(Exception e) { e.printStackTrace(); } return null; } } OpenExternalEditorCommandHandler.java du Projet EditorExample Une boîte de dialogue est utilisée pour choisir un fichier sur le système hôte Création d’un lien entre le fichier physique et le workspace Utilisation de l’éditeur par défaut pour l’ouverture
  44. 44 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple (suite) : Ouvrir un éditeur pour un fichier non présent dans le Workspace (solution 1) Le fichier setup.log est localisé à l’emplacement c:\setup.log L’icône précise qu’il s’agit d’un lien Pas de fichier setup.log dans le répertoire du workspace
  45. 45 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple : Ouvrir un éditeur pour un fichier non présent dans le Workspace (solution 2) public class OpenExternalEditorWithEFSCommandHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); final Shell shell = activeWorkbenchWindow.getShell(); String name= new FileDialog(shell, SWT.OPEN).open(); if (name == null) return null; name = name.replace('\\','/'); IFileStore fileStore = EFS.getLocalFileSystem().getStore(URI.create(name)); if (!fileStore.fetchInfo().isDirectory() && fileStore.fetchInfo().exists()) { IWorkbenchPage page= activeWorkbenchWindow.getActivePage(); try { IDE.openEditorOnFileStore(page, fileStore); } catch (PartInitException e) { e.printStackTrace(); } } return null; } } OpenExternalEditorWithEFCCommandHandler.java du Projet EditorExample Depuis la version Eclipse 3.3, EFS (Eclipse File System) a été défini pour simplifier la gestion des fichiers Utilisation de la méthode openEditorOnFileStore définie dans la classe IDE
  46. 46 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple : Ouvrir un éditeur pour un contenu qui n’est pas un fichier Affichage du contenu qui ne provient pas d’un fichier (une chaîne de texte) Exemple issu de la FAQ Eclipse http://wiki.eclipse.org/FAQ_How_do_I_open_an_editor_on_something_that_is_not_a_file%3F
  47. 47 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple (suite) : Ouvrir un éditeur pour un contenu qui n’est pas un fichier public class OpenEditorWithNoFileCommandHandler extends AbstractHandler { class StringStorage implements IStorage { priva te String string; StringStorage(String input) { this.string = input; } public InputStream getContents() throws CoreException { return new ByteArrayInputStream(string.getBytes()); } public IPath getFullPath() { return null; } public Object getAdapter(Class adapter) { return null; } public String getName() { int len = Math.min(5, string.length()); return string.substring(0, len).concat("..."); } public boolean isReadOnly() { return true; } } ... // Suite dans le prochain transparent OpenEditorWithNoFileCommandHandler.java du projet EditorExample
  48. 48 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple (suite) : Ouvrir un éditeur pour un contenu qui n’est pas un fichier public class OpenEditorWithNoFileCommandHandler extends AbstractHandler { class StringInput implements IStorageEditorInput { private IStorage storage; StringInput(IStorage storage) { this.storage = storage; } public boolean exists() { return true; } public ImageDescriptor getImageDescriptor() { return null; } public String getName() { return storage.getName(); } public IPersistableElement getPersistable() { return null; } public IStorage getStorage() { return storage; } public String getToolTipText() { return "String-based file: " + storage.getName(); } public Object getAdapter(Class adapter) { return null; } } ... // Suite dans le prochain transparent OpenEditorWithNoFileCommandHandler.java du projet EditorExample
  49. 49 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple (suite) : Ouvrir un éditeur pour un contenu qui n’est pas un fichier public class OpenEditorWithNoFileCommandHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); String string = "This is the text file contents"; IStorage storage = new StringStorage(string); IStorageEditorInput input = new StringInput(storage); IWorkbenchPage page = window.getActivePage(); if (page != null) { try { page.openEditor(input, "org.eclipse.ui.DefaultTextEditor"); } catch (PartInitException e) { e.printStackTrace(); } } return null; } OpenEditorWithNoFileCommandHandler.java du projet EditorExample Contenu à afficher Définition d’un IEditorInput dédié à la gestion d’une chaîne de caractères Ouverture de l’éditeur qui gère l’édition et l’affichage du texte
  50. 50 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple : Ouvrir plusieurs instances d’un même éditeur Plusieurs instances d’un même éditeur
  51. 51 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : openEditor … † Exemple (suite) : Ouvrir plusieurs instances d’un même éditeur public class OpenSeveralEditorInstances extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); ISelectionService selService = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService(); final ISelection selection = selService.getSelection(); if (selection instanceof TreeSelection) { TreeSelection currentFile = (TreeSelection)selection; final IFile firstElement = (IFile)currentFile.getFirstElement(); IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(firstElement.getName()); try { activePage.openEditor( new FileEditorInput(firstElement), desc.getId(), true, IWorkbenchPage.MATCH_NONE); } catch (PartInitException e) { e.printStackTrace(); } } return null; } } OpenSeveralEditorInstances.java du projet EditorExample Précise qu’il n’y a pas de comparaison entre les instances d’éditeur
  52. 52 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie † L’interface IEditorReference permet de représenter une instance d’un éditeur dans la page active † String getName() : le nom de l’éditeur † IEditorPart getEditor(boolean restore) : l’éditeur associé à cette instance † IEditorInput getEditorInput() : l’EditorInput associé à cette instance † boolean isPinned() : indique si l’éditeur est épinglé (voir transparent suivant pour explication) † L’accès à un objet IViewReference se fait par l’intermédiaire du Workbench Page actif
  53. 53 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : isPinned ? † Possibilité d’ouvrir plusieurs éditeurs dans une page pouvant aboutir à un nombre important d’éditeurs ouverts † La plateforme Eclipse permet de restreindre le nombre d’éditeur ouvert dans une page en fermant automatique- ment les anciens éditeurs † Preferences -> General -> Editors … activer l’option Close editors … Seuls trois éditeurs doivent rester ouverts
  54. 54 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : isPinned ? † Un éditeur ne peut être fermé automatiquement dans les cas suivants † si son état est dirty (contenu modifié) † si son état est pinned (épinglé) † Comment « épingler » un éditeur … Dés que l’option Close Editors automatically est activée, l’action Pin Editor est disponible Possibilité de modifier programmatiquement l’état de la caractéristique pinned d’un éditeur L’action Pin Editor sur l’éditeur courant active ou pas l’état épinglé
  55. 55 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : de l’ouverture à la fermeture † Ouverture de l’éditeur † void init(IEditorSite site, IEditorInput input) : initialisation du contenu de l’éditeur † void createPartControl(Composite parent) : création de l’IHM † Modification du contenu † void firePropertyChange(PROP_INPUT) : déclenche un changement † boolean isDirty() : précise s’il y a des changements † Persistance † void doSave(IProgressMonitor monitor) : sauvegarder le contenu † void doSaveAs() : sauvegarder le contenu dans un autre objet † Fermeture † dispose() : destruction des objets 1 2 3 4
  56. 56 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : ouverture d’un éditeur † Ouverture d’un éditeur procède à la création d’une instance EditorPart dans le Workbench Page actif † Ordre d’appel des méthodes de la classe de type EditorPart † Constructeur de la classe de type EditorPart † void init(IEditorSite site, IEditorInput input) : ne pas oublier de stocker le site et l’input dans le traitement de cette méthode † void createPartControl(Composite parent) † Méthodes appelées par la plateforme Eclipse uniquement à la création de l’instance d’un éditeur 1 2 3
  57. 57 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : éditeur en utilisation † Tout au long de l’utilisation d’une instance d’un EditorPart, son contenu peut évoluer † Exemple : un texte a été modifié, une sauvegarde peut être réalisée † La méthode isDirty() permet de retourner à la page courante l’état de ce changement † Si un changement est repéré, les procédures de sauvegarde du contenu sont proposées † La commande Save est activée † La commande Save As est activée si la méthode isSaveAsAllowed() retourne vraie † La sauvegarde est proposée à l’utilisateur si la méthode isSaveOnCloseNeeded() retourne vraie
  58. 58 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : fermeture d’un éditeur † La sauvegarde du contenu est gérée directement par l’éditeur dans la classe de type EditorPart † Plusieurs méthodes sont appelées en fonction du type de sauvegarde à réaliser † void doSave(IProgressMonitor monitor) : appelée pour réaliser une sauvegarde sur une ressource † void doSaveAs() : appelée pour effectuer une sauvegarde de type « Sauvegarder Comme » † A la suite de la sauvegarde, il faut penser à modifier l’état du changement retourné par la méthode isDirty() † La fermeture d’un éditeur provoque la suppression d’une instance de type EditorPart (méthode dispose() appelée)
  59. 59 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : show et hide † Depuis la version Eclipse 3.5 (Galileo), une API permet de cacher et de rendre visible un éditeur sans destruction de ces instances † Son utilisation reste limitée et seulement deux méthodes sont proposées † void hideEditor(IEditorReference) : cache un éditeur en utilisant son EditorReference † void showEditor(IEditorReference) : affiche un éditeur précédemment caché † Il n’est pas possible de récupérer la liste des éditeurs qui sont dans un état caché
  60. 60 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : show et hide † Exemple : Cacher et ouvrir un éditeur public class HideEditorCommandHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); final IWorkbenchPartReference reference = activePage.getReference(activePage.getActiveEditor()); if (reference instanceof IEditorReference) { activePage.hideEditor((IEditorReference)reference); Activator.getDefault().getHiddenEditors().add((IEditorReference)reference); } return null; } } HideEditorCommandHandler.java du projet EditorExample Ajoute chaque référence (IEditorReference) des éditeurs à cacher dans l’Activator Handler utilisé pour cacher des éditeurs L’ajout explicite des éditeurs dans l’Activator est nécessaire puisque l’API ne permet pas de connaître les éditeurs cachés
  61. 61 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie : show et hide † Exemple (suite) : Cacher et ouvrir un éditeur public class ShowEditorCommandHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); final List<IEditorReference> hiddenEditors = Activator.getDefault().getHiddenEditors(); if (!hiddenEditors.isEmpty()) { activePage.showEditor(hiddenEditors.get(0)); hiddenEditors.remove(0); } return null; } } ShowEditorCommandHandler.java du projet EditorExample public class ShowAllHiddenEditorsHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); WorkbenchPage activeWorkbencPage = (WorkbenchPage)activePage; final List<IEditorReference> hiddenEditors = Activator.getDefault().getHiddenEditors(); hiddenEditors.clear(); activeWorkbencPage.resetHiddenEditors(); return null; } } ShowAllHiddenEditorsHandler.java du projet EditorExample Appel de showEditor pour afficher l’éditeur puis suppression dans l’Activator Appel de resetHiddenEditors pour réafficher tous les éditeurs cachés ou Permet de réafficher un seul éditeur Permet de réafficher tous les éditeurs
  62. 62 Editors - M. Baron - Page mickael-baron.fr mickaelbaron MultiPageEditorPart

    † Un MultiPageEditorPart est un éditeur avec plusieurs pages où chaque page peut contenir † un éditeur (EditorPart) ou † un composant SWT † Un MultiPageEditorPart hérite de EditorPart et par conséquent il se comporte comme un éditeur classique (cycle de vie identique) Une page contenant un éditeur Une page contenant des composants SWT (Composite, Button, …)
  63. 63 Editors - M. Baron - Page mickael-baron.fr mickaelbaron MultiPageEditorPart

    † Les méthodes utiles de la classe MultiPageEditorPart † int addPage(IEditorPart editor, IEditorInput input) : ajoute un éditeur à la dernière page et retourne l’index de la page ajoutée † int addPage(Control p) : ajoute un composant SWT à la dernière page et retourne l’index de la page ajoutée † void setPageText(int index, String title) : modifie le titre de la page en fonction de l’index donné † Les méthodes à implémenter et à redéfinir † void createPages() : appeler pour construire les pages † void doSave(IProgressMonitor p) : sauvegarde de l’éditeur multiple † void doSaveAs() : sauvegarde de type « Sauvegarder Comme » † boolean isSaveAsAllowed() : autoriser le « Sauvegarder Comme » † void pageChange(int new) : à redéfinir si besoin d’être notifié du changement de page
  64. 64 Editors - M. Baron - Page mickael-baron.fr mickaelbaron MultiPageEditorPart

    † Exemple : Un éditeur multiple avec trois pages Une page contenant un éditeur de texte affichant le contenu d’un fichier Une page contenant un composite avec un bouton permettant de choisir une police de caractères Une dernière page contenant un composite avec un composant StyledText permettant d’afficher chaque mot (donné par le texte de la page 1) avec une police définie par la page 2 Exemple basé sur le template fourni par la plateforme Eclipse utilisé pour illustrer la construction d’éditeurs
  65. 65 Editors - M. Baron - Page mickael-baron.fr mickaelbaron MultiPageEditorPart

    † Exemple (suite) : Un éditeur multiple avec trois pages public class MultiPageEditor extends MultiPageEditorPart implements IResourceChangeListener { public MultiPageEditor() { super(); ResourcesPlugin.getWorkspace().addResourceChangeListener(this); } protected void createPages() { createPage0(); createPage1(); createPage2(); } private void createPage0() { try { editor = new TextEditor(); int index = addPage(editor, getEditorInput()); setPageText(index, editor.getTitle()); } catch (PartInitException e) { ErrorDialog.openError(getSite().getShell(), "Error creating nested text editor", null, e.getStatus()); } } private void createPage2() { ... text = new StyledText(composite, SWT.H_SCROLL | SWT.V_SCROLL); int index = addPage(composite); setPageText(index, "Preview"); } protected void pageChange(int newPageIndex) { super.pageChange(newPageIndex); if (newPageIndex == 2) sortWords(); } // Suite dans le prochain transparent } MultiPageEditor.java du projet MultiPageEditorExample Ajoute un écouteur sur les changements des ressources Demande la création de chaque page Ajoute un éditeur dans la page 0 Ajoute un composite dans la page 2 A chaque changement de page une action est réalisée
  66. 66 Editors - M. Baron - Page mickael-baron.fr mickaelbaron MultiPageEditorPart

    † Exemple (suite) : Un éditeur multiple avec trois pages public class MultiPageEditor extends MultiPageEditorPart implements IResourceChangeListener { public void doSave(IProgressMonitor monitor) { getEditor(0).doSave(monitor); } public void doSaveAs() { IEditorPart editor = getEditor(0); editor.doSaveAs(); setPageText(0, editor.getTitle()); setInput(editor.getEditorInput()); } public void resourceChanged(final IResourceChangeEvent event) { if(event.getType() == IResourceChangeEvent.PRE_CLOSE){ Display.getDefault().asyncExec(new Runnable(){ public void run(){ IWorkbenchPage[] pages = getSite().getWorkbenchWindow().getPages(); for (int i = 0; i<pages.length; i++){ if(((FileEditorInput)editor.getEditorInput()).getFile() .getProject().equals(event.getResource())) { IEditorPart editorPart = pages[i].findEditor(editor.getEditorInput()); pages[i].closeEditor(editorPart,true); } } } }); } } public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); super.dispose(); } } Sauvegarde le contenu si des modifications ont été apportées Si le projet est fermé, les éditeurs associés sont fermés MultiPageEditor.java du projet MultiPageEditorExample
  67. 67 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Diviser

    un éditeur † Dans une page la zone de l’éditeur est unique et plusieurs instances d’éditeurs peuvent cohabiter dans la zone † L’affichage des éditeurs peut se faire † Soit en empilant les différents éditeurs † Soit en divisant (split) les éditeurs † Soit de manière mixte (empiler + diviser) † Pour diviser des éditeurs, l’utilisateur peut faire glisser un éditeur dans la zone † Toutefois, il n’existe pas d’API « publics » pour réaliser cette opération de manière programmatique, il faut penser par l’implémentation interne
  68. 68 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Diviser

    un éditeur † Exemple : Diviser programmatiquement la zone de l’éditeur Exemple issu du blog … http://swarmy.free.fr/wordpress/?p=17 La zone de l’éditeur est séparée en deux
  69. 69 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Diviser

    un éditeur † Exemple (suite) : Diviser programmatiquement la zone de l’éditeur public class CreateSplitEditorHandler extends AbstractHandler { public Object execute(ExecutionEvent event) throws ExecutionException { IWorkbenchPage workbenchPage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IWorkbenchPart part = workbenchPage.getActivePart(); PartPane partPane = ((PartSite) part.getSite()).getPane(); LayoutPart layoutPart = partPane.getPart(); IEditorReference[] editorReferences = workbenchPage.getEditorReferences(); if (editorReferences.length > 1) { PartPane currentEditorPartPane = ((PartSite) workbenchPage.getActiveEditor().getSite()).getPane(); EditorSashContainer editorSashContainer = null; ILayoutContainer rootLayoutContainer = layoutPart.getContainer(); if (rootLayoutContainer instanceof LayoutPart) { ILayoutContainer editorSashLayoutContainer = ((LayoutPart) rootLayoutContainer).getContainer(); if (editorSashLayoutContainer instanceof EditorSashContainer) { editorSashContainer = ((EditorSashContainer) editorSashLayoutContainer); } } PartStack newPart = createStack(editorSashContainer); editorSashContainer.stack(currentEditorPartPane, newPart); if (rootLayoutContainer instanceof LayoutPart) { ILayoutContainer cont = ((LayoutPart) rootLayoutContainer).getContainer(); if (cont instanceof PartSashContainer) { // "Split" the editor area by adding the new part ((PartSashContainer) cont).add(newPart); } } } return null; } // Suite dans le prochain transparent ... } CreateSplitEditorHandler.java du projet SplitEditorExample
  70. 70 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Diviser

    un éditeur † Exemple (suite) : Diviser programmatiquement la zone de l’éditeur public class CreateSplitEditorHandler extends AbstractHandler { ... private PartStack createStack(EditorSashContainer editorSashContainer) { WorkbenchPage workPage = (WorkbenchPage)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); EditorStack newWorkbook = EditorStack.newEditorWorkbook(editorSashContainer, workbenchPage); return newWorkbook; } } CreateSplitEditorHandler.java du projet SplitEditorExample Une grande partie des APIs utilisées sont définies comme Internal
  71. 71 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Ecouteurs

    (Part Listeners) † Les modifications opérées sur un éditeur (activation, visibilité ou l’ouverture) peuvent être écoutées (principe identique pour les vues) † L’activation et la création des éditeurs sont écoutables via l’interface IPartService implémentées par IWorkbenchPage † void addPartListener(IPartListener pl) : écouteur « 1ère » version † void addPartListener(IPartListener2 pl) : écouteur « 2nd » version † IWorkbenchPart getActivePart() : Part active † IWorkbenchPartReference getActivePartReference() : Part référence active † L’écouteur IPartListener est à remplacer par l’interface IPartListener2
  72. 72 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Ecouteurs

    (Part Listeners) † IPartListener2 fournit les méthodes suivantes pour les réponses aux événements sur un éditeur † void partActivated(IWorkbenchPartReference pr) : part activée † void partBroughtToTop(IWorkbench… pr) : part au premier plan † void partClosed(IWorkbench… pr) : part fermée † void partDeactivated(IWorkbench… pr) : part désactivée † void partHidden(IWorkbench… pr) : part cachée † void partInputChanged(IWorkbench… pr) : saisie de l’utilisateur † void partOpened(IWorkbench… pr) : part ouverte † void partVisible(IWorkbench… pr) : part visible † L’écoute de certaines modifications propres aux éditeurs n’est pas pris en compte : show/hide, instances multiples, division d’un éditeur, …
  73. 73 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Ecouteurs

    (Part Listeners) † Exemple : Ecouter les changements d’un EditorPart public class LifeCycleEditor extends EditorPart { public void createPartControl(Composite parent) { ... IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); activePage.addPartListener(new IPartListener2() { @Override public void partActivated(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (LifeCycleEditor.this == part) { System.out.println(".partActivated()"); } } @Override public void partBroughtToTop(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (LifeCycleEditor.this == part) { System.out.println(".partBroughtToTop()"); } } @Override public void partClosed(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (LifeCycleEditor.this == part) { System.out.println(".partClosed()"); } } ... } } } LifeCycleEditor.java du projet EditorExample
  74. 74 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et les commandes † Lors de l’activation d’un éditeur il est possible de rendre accessible des actions spécifiques à ce contexte † Si l’éditeur n’est plus actif, les actions spécifiques sont automatiquement cachées † Exemple : ouverture d’un éditeur de texte † Rendre accessible des actions comme copier/coller/couper … † Deux solutions sont à envisager † Utilisation des restrictions via les commandes (afficher une commande selon si un éditeur est actif ou pas) † Utilisation d’un objet IEditorActionBarContributor (à renseigner lors de la définition de l’extension d’un éditeur) † Dans la suite, nous utiliserons la seconde solution basée sur l’utilisation de commandes
  75. 75 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et les commandes † L’interface IEditorActionBarContributor permet d’ajouter des actions dans un menu, une barre d’outils et la barre d’états † void contributeToMenu(IMenuManager mmanager) : pour contribuer à l’enrichissement de la barre de menu † void contributeToStatusLine(IStatusLineManager smanager) : pour contribuer à l’enrichissement de la barre d’états † void contributeToToolBar(IToolBarManager tmanager) : pour contribuer à l’enrichissement de la barre d’outils † Dans les trois cas, possibilité d’ajouter des éléments de type IContributionItem (CommandContributionItem)
  76. 76 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et les commandes † Exemple : Contribuer au menu, à la barre d’outils et à la barre d’états Lors de l’activation de l’éditeur, des actions sont disponibles dans la barre d’outils (1), la barre d’état (2) et le menu File (3) 1 2 3
  77. 77 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et les commandes † Exemple (suite) : Contribuer au menu, à la barre d’outils et à la barre d’états plugin.xml du projet EditorActionBarContributor Objet de type EditorWithCommandActionBarContributor
  78. 78 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et les commandes † Exemple (suite) : Contribuer au menu, à la barre d’outils et à la barre d’états public class EditorWithCommandActionBarContributor extends EditorActionBarContributor { public void contributeToMenu(IMenuManager menuManager) { super.contributeToMenu(menuManager); CommandContributionItemParameter commandParameter = new CommandContributionItemParameter( PlatformUI.getWorkbench(), "contributionitem", "eclipse.workbench.editorexample.SimpleCommandId", CommandContributionItem.STYLE_PUSH); IContributionItem ref = new CommandContributionItem(commandParameter); IMenuManager menu = new MenuManager("Simple Editor"); menuManager.prependToGroup(IWorkbenchActionConstants.MB_ADDITIONS, menu); menuManager.findMenuUsingPath("File").add(ref); } public void contributeToStatusLine(IStatusLineManager statusLineManager) { CommandContributionItemParameter commandParameter = new CommandContributionItemParameter( PlatformUI.getWorkbench(), "contributionitem", "eclipse.workbench.editoractionbarcontributorexample.samplecommandid", CommandContributionItem.STYLE_PUSH); IContributionItem ref = new CommandContributionItem(commandParameter); statusLineManager.add(ref); } public void contributeToToolBar(IToolBarManager toolBarManager) { CommandContributionItemParameter commandParameter = new CommandContributionItemParameter( PlatformUI.getWorkbench(), "contributionitem", "eclipse.workbench.editoractionbarcontributorexample.samplecommandid", CommandContributionItem.STYLE_PUSH); IContributionItem ref = new CommandContributionItem(commandParameter); toolBarManager.add(ref); } } EditorWithCommandActionBarContributor.java du projet EditorActionBarContributor
  79. 79 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Généralités † La plateforme Eclipse s’appuie sur le Workspace pour stocker les fichiers qu’il manipule † Le Workspace est un répertoire hiérarchique physique qui contient une collection de ressources † Projets, répertoires et fichiers † Le Workspace sert également à stocker les paramètres des plugins (répertoire .metadata) † Paramètres définis par les préférences de l’utilisateur † Agencement de l’espace de travail (perspectives, projets ouverts, …) † Pourquoi discuter du Workspace ici ? † L’éditeur manipulent essentiellement des ressources † Besoin de notifications sur les changements des ressources
  80. 80 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Généralités † Structure d’un Workspace Eclipse Répertoire contenant des informations liées aux plugins Ressources de type projet Fichier .log utilisé pour stocker les messages Informations liées aux plugins Ressources de type dossier et fichier
  81. 81 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Généralités † Exemple : Localiser l’emplacement du Workspace public class WorkspaceViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button location = new Button(parent, SWT.FLAT); location.setText("Workspace location ..."); location.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Location instanceLoc = Platform.getInstanceLocation(); System.out.println(instanceLoc.getURL().toString()); } }); ... } } WorkspaceViewPart.java du projet WorkspaceExample
  82. 82 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Généralités † Exemple : Forcer la sélection du Workspace au démarrage d’une application Eclipse Avec l’option -data @noDefault, la plateforme Eclipse demandera à chaque démarrage l’emplacement du Workspace WorkspaceExampleConfiguration.launch du projet WorkspaceExample
  83. 83 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource † La plateforme Eclipse fournit des APIs pour la création, la navigation et la manipulation de ressources dans un Workspace † Le plugin org.eclipse.core.resources fournit ces APIs (dépendance obligatoire) † Des méta-données sont associées aux ressources † Properties (session et persistent) † Preferences † Content types † Linked resources (attaché un fichier à un projet qui n’est pas localisé dans le Workspace) † Markers (task, problem, text, bookmark)
  84. 84 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource † Cartes des principales interfaces concernant les ressources IResource getName() getParent() getWorkspace() getFullPath() … IContainer members() exists(IPath) … IProject open(int, IProgressMonitor) create(IProgressMonitor) close(IProgressMonitor) … IFolder getFile(String) create(boolean, boolean, IProgressMonitor) createLink(URI, int, IProgressMonitor) delete(boolean, boolean, IProgressMonitor) … IWorkspaceRoot getProjects() getProject(String) delete(boolean, boolean, IProgressMonitor) … IWorkspace addResourceChangeListener(IResourceChangeListener) build(int, IProgressMonitor) isAutoBuilding() getRoot() … IFile create(InputStream, int, IProgressMonitor) createLink(URI, int, IProgressMonitor) delete(boolean, boolean, IProgressMonitor) … IPath toFile() … Dépendance Héritage
  85. 85 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IWorkspaceRoot † Le Workspace est défini par l’interface IWorkspace et il n’y a qu’un seul workspace par application Eclipse † Pour récupérer le workspace de l’application Eclipse en cours † IWorkspace permet l’écoute des changements sur les res- sources d’un workspace (détaillée plus tard) † Il permet également de récupérer un objet IWorkspaceRoot autorisant l’accès à l’ensemble des projets † Principales méthodes de IWorkspaceRoot † IProject getProject(String) : récupère un projet par son nom † IProject[] getProjects() : récupère tous les projets IWorkspace current = ResourcesPlugin.getWorkspace(); IWorkspaceRoot currentRoot = ResourcesPlugin.getWorkspace().getRoot();
  86. 86 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IWorkspaceRoot † Exemple : Lister l’ensemble des projets d’un Workspace public class WorkspaceViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button projects = new Button(parent, SWT.FLAT); projects.setText("Workspace location ..."); projects.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject[] projects = root.getProjects(); for (IProject iProject : projects) { System.out.println(iProject.getName()); } } }); ... } } WorkspaceViewPart.java du projet WorkspaceExample
  87. 87 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IProject † L’interface IProject définit le concept de projet † Principales caractéristiques d’un projet Eclipse † Un projet est un conteneur et contient des dossiers et des fichiers † Un projet peut construire des ressources † Un projet peut avoir des références vers d’autres projets † Un projet peut avoir une ou plusieurs natures (voir plus tard) † Un projet contient des méta-données (IProjectDescription) † Différentes méthodes … † void create(IProgressMonitor) : crée du projet † void open(IProgessMonitor) : ouvre un projet † IProjectDescription getDescription() : récupère les méta-datas † IFolder getFolder(String) : retourne un dossier † IFile getFile(String) : retourne un fichier † + méthodes fournies par IContainer
  88. 88 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IProject † Exemple : Manipulation de ressource IProject public class WorkspaceViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button createProject = new Button(parent, SWT.FLAT); createProject.setText("Create new Project ..."); createProject.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject project4 = root.getProject("Project 4"); try { project4.create(null); project4.open(null); final IProjectDescription description = project4.getDescription(); description.setComment("This is a simple project"); final IProject project1 = root.getProject("Project 1"); final IProject project2 = root.getProject("Project 2"); IProject[] tabProjects = {project1, project2}; description.setReferencedProjects(tabProjects); project4.setDescription(description, null); } catch (CoreException e1) { e1.printStackTrace(); } } }); ... } } WorkspaceViewPart.java du projet WorkspaceExample
  89. 89 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IProject † Les informations décrit dans IProjectDescription sont stockées dans le fichier .project <?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>Project 4</name> <comment>This is a simple project</comment> <projects> <project>Project 1</project> <project>Project 2</project> </projects> <buildSpec> </buildSpec> <natures> </natures> </projectDescription> Nom du projet Commentaire du projet Liste des projets référencés Liste des Builders Liste des Natures Fichier .project du répertoire Project 4
  90. 90 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IFolder † L’interface IFolder décrit un dossier localisé dans un projet † Un objet IFolder est un conteneur et contient des fichiers et des dossiers † Différentes méthodes … † void create(boolean, boolean, IProgressMonitor) : création du dossier † IFile getFile(String) : retourne un fichier † IFolder getFolder(String) : retour un dossier † + méthodes fournies par IContainer † L’interface IFile décrit un fichier † void create(InputStream, boolean, IProgressMonitor) : création du fichier † InputStream getContents() : retourne le contenu † IPath getFullPath() : retourne le chemin † + méthodes fournis par IResource
  91. 91 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / IFolder † Exemple : Manipulation de ressources IFolder et IFile public class WorkspaceViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button createFolder = new Button(parent, SWT.FLAT); createFolder.setText("Create new Folder and File ..."); createFolder.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject project4 = root.getProject("Project 4"); try { final IFolder folderA = project4.getFolder("Folder A"); folderA.create(true, true, null); IFile fileA = project4.getFile("File A.txt"); fileA.create(new ByteArrayInputStream("HelloWorld from File A".getBytes()), true, null); IFile fileB = folderA.getFile("File B.txt"); fileB.create(new ByteArrayInputStream("HelloWorld from File B".getBytes()), true, null); System.out.println(fileA.getFullPath()); } catch (CoreException e1) { e1.printStackTrace(); } } }); ... } } WorkspaceViewPart.java du projet WorkspaceExample
  92. 92 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Properties † Les propriétés (Properties) sont utilisées pour attacher des informations (méta-données) aux ressources † Tous les objets de type IResource sont concernés (IProject, IFolder, IFile et IWorkspaceRoot) † Quand une ressource est supprimée, ses propriétés sont effacées également † Deux types de propriétés sont à distinguer † Propriétés de session † Propriétés persistantes † L’API de manipulation est fournie par l’interface IResource
  93. 93 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Properties † Propriétés de session † Permet de sauvegarder des informations dans un couple clé / valeur † Les valeurs peuvent être des objets † Les propriétés sont maintenues en mémoire † Les propriétés sont supprimées quand une ressource est supprimée ou quand un projet ou un workspace est fermé † Map IResource#getSessionProperties() : propriétés en session † Propriétés de persistances † Sauvegarde les informations physiquement sur le disque † Les valeurs sont obligatoirement des chaînes de caractères † Les chaînes de caractères sont prévues pour être courtes † Les propriétés sont maintenues même au redémarrage de la plateforme † Map IResource#getPersistentProperties() : propriétés persistantes
  94. 94 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Properties † Exemple : Manipulation des propriétés de session public class PropertiesViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button saveSessionPropertiesButton = new Button(parent, SWT.FLAT); saveSessionPropertiesButton.setText("Save Session Properties ..."); saveSessionPropertiesButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject project5 = root.getProject(projectName); try { if (!project5.exists()) { project5.create(null); project5.open(null); } project5.setSessionProperty(new QualifiedName("session_test", "en"), "test"); } catch (Exception e1) { e1.printStackTrace(); } } }); ... } } PropertiesViewPart.java du projet WorkspaceExample Enregistrement d’une propriété « session_test » dans la session d’un projet Si le projet est fermé, workspace rechargé ou application redémarrée, la propriété est perdue
  95. 95 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Properties † Exemple (suite) : Manipulation des propriétés de session public class WorkspaceViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button loadSessionPropertiesButton = new Button(parent, SWT.FLAT); loadSessionPropertiesButton.setText("Load Session Properties ..."); loadSessionPropertiesButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject project5 = root.getProject(projectName); try { final Map sessionProperties = project5.getSessionProperties(); System.out.println(sessionProperties.get(new QualifiedName("session_test", "en"))); } catch (CoreException e1) { e1.printStackTrace(); } } }); ... } } PropertiesViewPart.java du projet WorkspaceExample Chargement de la propriété « session_test »
  96. 96 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Properties † Exemple : Manipulation des propriétés persistances public class PropertiesViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button savePersistentPropertiesButton = new Button(parent, SWT.FLAT); savePersistentPropertiesButton.setText("Save Persistence Properties ..."); savePersistentPropertiesButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject project5 = root.getProject(projectName); try { if (!project5.exists()) { project5.create(null); } project5.setPersistentProperty(new QualifiedName("persistent_test", "en"), "test"); } catch (CoreException e1) { e1.printStackTrace(); } } }); } } PropertiesViewPart.java du projet WorkspaceExample Enregistrement d’une propriété « persistent_test » dans un projet en mode persistant Si le projet est fermé, workspace rechargé ou application redémarrée, la propriété reste présente
  97. 97 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Properties † Exemple (suite) : Manipulation des propriétés persistances public class PropertiesViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button loadPersistentPropertiesButton = new Button(parent, SWT.FLAT); loadPersistentPropertiesButton.setText("Load Persistence Properties ..."); loadPersistentPropertiesButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject project5 = root.getProject(projectName); try { final Map persistentProperties = project5.getPersistentProperties(); System.out.println(persistentProperties.get(new QualifiedName("persistent_test", "en"))); } catch (CoreException e1) { e1.printStackTrace(); } } }); } } PropertiesViewPart.java du projet WorkspaceExample Chargement de la propriété « persistent_test »
  98. 98 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Les markers sont un mécanisme permettant d’annoter une ressource † Les markers peuvent s’appliquer autant sur les projets que les dossiers et les fichiers † La plateforme Eclipse définit cinq types de markers † Marker : annotation de base † Task : annotation de type tâche † Problem : annotation de type problème † Text : annotation de type texte † Bookmark : annotation de type favori † Possibilité de définir ses propres markers en utilisant le point d’extension org.eclipse.core.resources.markers † Les types de markers sont hiérarchiques et des niveaux d’héritage peuvent être définis
  99. 99 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers Markers de type Problem avec deux niveaux de sévérité : Error et Warning † Exemple : Des Markers de types Problem et Task Markers de type Task
  100. 100 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Un marker est défini par l’interface IMarker † La création d’un marker est réalisé à partir d’un objet de type IResource † Opérations basiques (méthodes de IResource) † IMarker createMarker(String) : création d’un marker à partir d’un identifiant (Task, Bookmark, …) † void deleteMarkers(String t, boolean, int) : supprime tous les markers définis par le type t † Principales méthodes de IMarker † void delete() : supprimer un marker † Une série setAttribute(String, boolean || int || Object) : permettant d’affecter des valeurs à des attributs prédéfinis
  101. 101 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † L’interface IMarker définit un ensemble d’attributs et de valeurs d’attributs pour les types de marker prédéfinis † SEVERITY : niveau de sévérité (#SEVERITY_ERROR, #SEVERITY_WARNING, #SEVERITY_INFO) † MESSAGE : description du marker (nom d’une tâche) † LOCATION : information pour distinguer deux markers † PRIORITY : niveau de priorité (#PRIORITY_HIGH, #PRIORITY_NORMAL, #PRIORITY_LOW) † DONE : marker est considéré terminé † CHAR_START et CHAR_END : intervalle impacté † LINE_NUMBER : numéro de line impacté † USER_EDITABLE : modification autorisé ou pas
  102. 102 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Exemple : Création d’une annotation de type Task Les tâches sont visibles dans la vue Tasks Depuis l’éditeur, une annotation précise qu’une tâche est associée à la ligne 2 Tâche annotée à une ressource de type fichier
  103. 103 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Exemple (suite) : Création d’une annotation de type Task public class MarkersViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button createTasks = new Button(parent, SWT.FLAT); createTasks.setText("Create Tasks ..."); createTasks.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject projectMarkers= root.getProject("Project Markers"); try { if (!projectMarkers.exists()) { projectMarkers.create(null); projectMarkers.open(null); } final IFile file = projectMarkers.getFile("file"); if (!file.exists()) { file.create(new ByteArrayInputStream("HelloWorld\n This is a text for Markers example".getBytes()), IResource.NONE, null); } IMarker createTaskMarker = file.createMarker(IMarker.TASK); createTaskMarker.setAttribute(IMarker.MESSAGE, "Sample Task message"); createTaskMarker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_LOW); createTaskMarker.setAttribute(IMarker.USER_EDITABLE, false); createTaskMarker.setAttribute(IMarker.LINE_NUMBER, 2); } catch (Exception e1) { e1.printStackTrace(); } } }); } } MarkersViewPart.java du projet WorkspaceExample
  104. 104 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Exemple : Création d’une annotation de type Problem Les problèmes sont visibles dans la vue Problems Depuis l’éditeur, une annotation précise que des problèmes sont associés à la ligne 2 Problèmes annotés à une ressource de type fichier
  105. 105 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Exemple (suite) : Création d’une annotation de type Problem public class MarkersViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button createProblems = new Button(parent, SWT.FLAT); createProblems.createTasks.setText("Create Tasks ..."); createProblems.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { ... try { IMarker createErrorProblemMarker = file.createMarker(IMarker.PROBLEM); createErrorProblemMarker.setAttribute(IMarker.MESSAGE, "Sample Error Problem message"); createErrorProblemMarker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); createErrorProblemMarker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_LOW); createErrorProblemMarker.setAttribute(IMarker.LINE_NUMBER, 2); IMarker createWarningProblemMarker = file.createMarker(IMarker.PROBLEM); createWarningProblemMarker.setAttribute(IMarker.MESSAGE, "Sample Warning Problem message"); createWarningProblemMarker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING); createWarningProblemMarker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH); createWarningProblemMarker.setAttribute(IMarker.LINE_NUMBER, 2); } catch (Exception e1) { e1.printStackTrace(); } } }); } } MarkersViewPart.java du projet WorkspaceExample
  106. 106 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers † Exemple : Création d’une annotation de type Bookmark Depuis l’éditeur, une annotation précise qu’un favori est associé à la ligne 2 Les favoris sont visibles dans la vue Bookmarks
  107. 107 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Resource / Markers public class MarkersViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button createBookmarks = new Button(parent, SWT.FLAT); createBookmarks.createTasks.setText("Create Tasks ..."); createBookmarks.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { ... try { IMarker createErrorProblemMarker = file.createMarker(IMarker.BOOKMARK); createErrorProblemMarker.setAttribute(IMarker.MESSAGE, "Sample Bookmark message"); createErrorProblemMarker.setAttribute(IMarker.LINE_NUMBER, 2); } catch (Exception e1) { e1.printStackTrace(); } } }); } } MarkersViewPart.java du projet WorkspaceExample † Exemple (suite) : Création d’une annotation de type Bookmark
  108. 108 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder † Un Builder (constructeur) est un objet qui manipule les ressources d’un projet pour produire d’autres ressources † Cette opération s’appelle un Build ou une construction † La plateforme définit deux types de construction † Une construction complète (full build) : manipule toutes les ressources même si aucune modification n’a été apportée sur ces ressources † Une construction incrémentale (incremental build) : s’appuie sur les précédentes constructions pour traiter les ressources modifiées † Les constructions incrémentales s’appuient sur des objets de type IResourceDelta pour distinguer les changements d’une ressource † Le nettoyage d’un projet (Clean) force un builder à procéder à une construction complète
  109. 109 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder † Une construction (complète ou incrémentale) se déclenche † De manière automatique (si l’option Build Automatically est activée) à chaque modification opérée sur le workspace † A la demande explicite du développeur (Build Project, Build All) † Lors du démarrage d’un builder des artefacts peuvent être générés † Des ressources (projet, dossier ou des fichiers) † Des markers (tâches, problèmes, …) † Une ressource générée est appelée ressource dérivée † Une construction est encapsulée dans un processus extérieur de manière à † éviter le blocage † assurer l’annulation de la construction
  110. 110 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Exemple JDT † Le projet JDT (Java Development Tool) utilise un builder pour compiler les classes Java d’un projet † Construction complète via JDT † Toutes les classes Java (ressources) sont compilées † Les fichiers .class (ressources dérivées) sont stockés dans le réper- toire bin † Les problèmes de compilation sont ajoutées comme des markers de type Problem † Construction incrémentale via JDT † Seules les classes modifiées sont recompilées (utilisation des IResourceDelta) † Mise à jour de la liste des markers de type Problem
  111. 111 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder † Un builder peut être déclenché explicitement via les méthodes suivantes † IProject#build(…) : demande un démarrage d’une construction avec possibilité de choisir son builder (voir paramètres) † IWorkspace#build(…) : demande un démarrage d’une construction de tous les projets du workspace † Les commandes utilisées dans le Workbench permettent d’appeler directement ces méthodes † Lors de l’appel de ces méthodes, des builders sont déclen- chés. Si aucun builder n’a été défini aucune construction ne sera réalisée
  112. 112 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Création d’une extension à org.eclipse.core.resources.builders Utilisation du point d’extension org.eclipse.core.resources.builders Template existant pour les constructeurs
  113. 113 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Edition des attributs de l’extension builders Préciser l’identifiant de l’extension de manière à manipuler le builder par la suite plugin.xml du projet BuilderExample
  114. 114 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Edition des attributs de l’extension builders (suite) Précise si le constructeur est associé à une nature (voir plus tard) Si à vraie, déclenche une construction incrémentale même s’il n’y a aucun changement plugin.xml du projet BuilderExample
  115. 115 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Création de la classe du builder Définition de la classe où sera codée le traitement du builder plugin.xml du projet BuilderExample Classe de type IncrementalProjectBuilder
  116. 116 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Associer des paramètres au builder Deux paramètres sont associés au builder plugin.xml du projet BuilderExample
  117. 117 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Classe du builder personnalisée public class CustomIncrementalProjectBuilder extends IncrementalProjectBuilder { protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { if (kind == IncrementalProjectBuilder.FULL_BUILD) { fullBuild(monitor); } else { IResourceDelta delta = getDelta(getProject()); if (delta == null) { fullBuild(monitor); } else { incrementalBuild(delta, monitor); } } return null; } protected void fullBuild(final IProgressMonitor monitor) throws CoreException { try { final IFolder folder = this.getProject().getFolder("My Bin"); if (!folder.exists()) { folder.create(true, true, null); } getProject().accept(new MyBuildVisitor()); } catch (CoreException e) { } } protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { delta.accept(new MyBuildDeltaVisitor()); } ... CustomIncrementalProjectBuilder.java du projet BuilderExample Selon le type de builder des traitements particuliers sont réalisés Au niveau du full build un répertoire My Bin est créé Un Visiteur est utilisé pour parcourir chaque ressource de la construction et réaliser les opérations adéquates
  118. 118 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Classe du builder personnalisée (suite) public class CustomIncrementalProjectBuilder extends IncrementalProjectBuilder { class MyBuildVisitor implements IResourceVisitor { public boolean visit(IResource res) { System.out.println("Full " + res.getName()); return true; } } class MyBuildDeltaVisitor implements IResourceDeltaVisitor { public boolean visit(IResourceDelta res) { System.out.println("Incremental " + res.getKind() + " " + res.getResource().getName()); return true; } } protected void clean(IProgressMonitor monitor) throws CoreException { final IFolder folder = this.getProject().getFolder("My Bin"); if (folder.exists()) { folder.delete(true, null); } } } CustomIncrementalProjectBuilder.java du projet BuilderExample Ce visiteur donne accès à chaque IResource « balayée » par le full build Ce visiteur donne accès à chaque IResourceDelta « balayée » par l’incremental build La catégorie permet de savoir quelle modification a été apportée à la ressource (voir constante dans IResourceDelta) L’opération de nettoyage efface le répertoire créé dans le full build
  119. 119 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Associer un builder personnalisé à un projet public class BuilderViewPart extends ViewPart { public void createPartControl(Composite parent) { parent.setLayout(new GridLayout()); Button createTasks = new Button(parent, SWT.FLAT); createTasks.setText("Associate Builder ..."); createTasks.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject projectBuilder = root.getProject("Project Builder"); try { if (!projectBuilder.exists()) { projectBuilder.create(null); projectBuilder.open(null); } final IFile file = projectBuilder.getFile("file 1"); if (!file.exists()) { file.create(new ByteArrayInputStream("HelloWorld\n This is text for Builder examples".getBytes()), IResource.NONE, null); } ... BuilderViewPart.java du projet BuilderExample Création d’un projet et d’un fichier
  120. 120 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Associer un builder personnalisé à un projet (suite) public class BuilderViewPart extends ViewPart { public void createPartControl(Composite parent) { ... final String BUILDER_ID = "eclipse.workbench.BuilderExample.samplebuilderid"; IProjectDescription desc = projectBuilder.getDescription(); ICommand[] commands = desc.getBuildSpec(); // Add builder to project ICommand command = desc.newCommand(); command.setBuilderName(BUILDER_ID); ICommand[] newCommands = new ICommand[commands.length + 1]; // Add it before other builders. System.arraycopy(commands, 0, newCommands, 1, commands.length); newCommands[0] = command; desc.setBuildSpec(newCommands); projectBuilder.setDescription(desc, null); } catch (Exception e1) { e1.printStackTrace(); } } }); } } BuilderViewPart.java du projet BuilderExample Identifiant du builder = identifiant plugin + identifiant extension du builder
  121. 121 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Associer un builder personnalisé à un projet (suite) Un dossier a été ajouté lors du full build Toutes les ressources sont « visitées »
  122. 122 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Associer un builder personnalisé à un projet (suite) Le contenu du fichier file1 a été mis à jour Deux ressources ont été modifiées
  123. 123 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Builder / Définir son Builder † Associer un builder personnalisé à un projet (suite) Le fichier file1 a été déplacé Des ressources ont été modifiées, ajoutée (nouveau file 1) et supprimée (ancien file 1)
  124. 124 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature † Associer un builder à un projet doit être réalisé une seule fois, habituellement quand le projet est créé † La plateforme Eclipse préconise d’associer un builder à un projet en passant par la notion de nature † De manière plus générale une nature est utilisée également pour créer une association entre un projet et des plugins † En ajoutant une nature à un projet, vous précisez que des plugins sont configurés à utiliser ce projet † Ce mécanisme de nature peut être ainsi utilisé pour ajouter un comportement spécifique à des projets
  125. 125 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Exemple JDT † Le projet JDT (Java Development Tool) utilise une nature pour définir des comportements spécifiques pour les projets Java † Fonctionnalités de la nature Java † Tous les plugins JDT sont ainsi associés aux projets Java † Un classpath a été configuré † Un builder propre au langage Java est défini † Les icônes des ressources contenant du Java sont modifiés pour les distinguer des autres ressources † Des actions sur les différents menus peuvent être visibles ou masquées
  126. 126 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Création d’une extension à org.eclipse.core.resources.natures Utilisation du point d’extension org.eclipse.core.resources.natures
  127. 127 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Edition des attributs de l’extension nature Préciser l’identifiant de l’extension de manière à manipuler le builder par la suite plugin.xml du projet NatureExample
  128. 128 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Associer une nature à un projet public class NatureViewPart extends ViewPart { public void createPartControl(Composite parent) { parent.setLayout(new GridLayout()); Button createNature = new Button(parent, SWT.FLAT); createNature.setText("Associate Nature (add Builder by code ..."); createNature.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject projectNature = root.getProject("Project Nature"); try { if (!projectNature.exists()) { projectNature.create(null); projectNature.open(null); } final IFile file = projectNature.getFile("file 1"); if (!file.exists()) { file.create(new ByteArrayInputStream("HelloWorld\n This is text for Nature examples".getBytes()), IResource.NONE, null); } ... } } NatureViewPart.java du projet NatureExample Création d’un projet et d’un fichier
  129. 129 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Associer une nature à un projet (suite) public class NatureViewPart extends ViewPart { public void createPartControl(Composite parent) { ... IProjectDescription description = projectNature.getDescription(); String[] natures = description.getNatureIds(); String[] newNatures = new String[natures.length + 1]; System.arraycopy(natures, 0, newNatures, 0, natures.length); newNatures[natures.length] = "eclipse.workbench.NatureExample.samplenatureid"; description.setNatureIds(newNatures); projectNature.setDescription(description, null); } catch (Exception e1) { e1.printStackTrace(); } } }); } } NatureViewPart.java du projet NatureExample Identifiant de la nature = identifiant plugin + identifiant extension de la nature
  130. 130 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Création de la classe de la nature Définition de la classe où sera codé le traitement de la configuration de la nature vis-à-vis des projets plugin.xml du projet NatureExample Classe de type IProjectNature
  131. 131 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Un objet de type IProjectNature fournit des méthodes pour l’installation et la désinstallation d’une nature † void configure() : configure une nature pour un projet † void unconfigure() : dé-configure une nature † IProject getProject() : retourne le projet associé à la nature † void setProject(IProject project) : modifie le projet associé à la nature † Que faire dans configure/unconfigure ? † Installation/désinstallation d’un builder † Création/suppression d’attributs supplémentaires † Plusieurs projets peuvent être associés à un même type de nature (IProjectNature)
  132. 132 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Exemple : Associer un builder à une nature public class CustomProjectNature implements IProjectNature { private IProject current; public void configure() throws CoreException { final String BUILDER_ID = "eclipse.workbench.BuilderExample.samplebuilderid"; IProjectDescription desc = current.getDescription(); ICommand[] commands = desc.getBuildSpec(); // Add builder to project ICommand command = desc.newCommand(); command.setBuilderName(BUILDER_ID); ICommand[] newCommands = new ICommand[commands.length + 1]; // Add it before other builders. System.arraycopy(commands, 0, newCommands, 1, commands.length); newCommands[0] = command; desc.setBuildSpec(newCommands); current.setDescription(desc, null); } public void deconfigure() throws CoreException { // Code permettant la suppression du builder ... } public IProject getProject() { return current; } public void setProject(IProject project) { this.current = project; } } CustomProjectNature.java du projet NatureExample IProjectDescription sert à préciser les natures à utiliser sur un projet
  133. 133 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Définir sa nature † Exemple : Définir l’icône d’un projet en fonction de la nature plugin.xml du projet NatureExample Création de l’extension org.eclipse.ui.ide.projectNatureImages Préciser l’identifiant de la nature puis l’image à afficher Nécessite que le plugin org.eclipse.ui.ide soit ajouté comme dépendance
  134. 134 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Contraintes † Des natures définies sur un projet peuvent être activées sous certaines conditions (suivant des contraintes) † Deux types de contraintes sont disponibles † one-of-nature : spécifie qu’il ne peut y avoir qu’une seule nature pour un projet † requires-nature : spécifie qu’une nature dépend d’une autre nature et ne peut être ajoutée à un projet que si l’autre nature est associée également au projet † Exemple via la contrainte requires-nature † Un plugin ou outils ne peut être associé aux projets dont la nature JDT est associée † Les contraintes sont définies directement au niveau des extensions des natures
  135. 135 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Contraintes † Exemple : Définir une contrainte requires-nature plugin.xml du projet NatureExample Identifiant de la nature requise (Ici il s’agit de la nature pour gérer les projets Java) Ajout d’un élément de type requires-nature à la définition d’un nature
  136. 136 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Contraintes † Exemple (suite) : Définir une contrainte requires-nature public class NatureViewPart extends ViewPart { public void createPartControl(Composite parent) { parent.setLayout(new GridLayout()); Button createRequiresNature = new Button(parent, SWT.FLAT); createRequiresNature.setText("Associate Nature (add Builder by code ..."); createRequiresNature.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); final IProject projectNature = root.getProject("Project Requires Nature"); try { if (!projectNature.exists()) { projectNature.create(null); projectNature.open(null); } final IFile file = projectNature.getFile("file 1"); if (!file.exists()) { file.create(new ByteArrayInputStream("HelloWorld\n This is text for Nature examples".getBytes()), IResource.NONE, null); } ... } } NatureViewPart.java du projet NatureExample
  137. 137 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Workspace

    : Nature / Contraintes public class NatureViewPart extends ViewPart { public void createPartControl(Composite parent) { ... IProjectDescription description = projectNature.getDescription(); String[] natures = description.getNatureIds(); String[] newNatures = new String[natures.length + 1]; System.arraycopy(natures, 0, newNatures, 0, natures.length); newNatures[natures.length] = "eclipse.workbench.NatureExample.samplerequiresnaturesid"; IStatus status = ResourcesPlugin.getWorkspace().validateNatureSet(newNatures); if (status.getCode() == IStatus.OK) { description.setNatureIds(newNatures); projectNature.setDescription(description, null); } else { System.out.println("Cannot enable this nature, nature is required."); } } catch (Exception e1) { e1.printStackTrace(); } } }); } } † Exemple (suite) : Définir une contrainte requires-nature NatureViewPart.java du projet NatureExample Vérifie si les natures sont valides avant de les imposer aux projets Comme la nature Java n’est pas associée au projet, une erreur est retournée
  138. 138 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte † Dans la suite, nous allons étudier en détail un éditeur qui manipule du texte via la classe TextEditor † Nous allons montrer comment enrichir un éditeur de texte en ajoutant † une coloration syntaxique † un assistant de contenu (content assist) † utilisation de patron † vérification lexicale (spell checking) † plan du contenu (outline) † hiérarchiser du texte (folding) † Par ailleurs, nous allons capitaliser la plupart des éléments étudiés précédemment pour développer un exemple complet
  139. 139 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : exemple † Définition d’un éditeur pour la sauvegarde de mot de passe Les mots clés sont en gras La vue Outline est utilisée pour visualiser le contenu de l’éditeur Utilisation de l’assistant de contenu pour insérer des mots clés et des templates
  140. 140 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : TextEditor par extension † Définir un extension basée sur le point d’extension org.eclipse.ui.editors † Définir les attributs de l’extension † Implémenter une classe qui hérite de TextEditor † Fournir une icône pour dissocier l’éditeur † Connecter le menu et la barre de menu à l’implémentation par défaut † TextEditorActionContributor † Ajouter les dépendances suivantes dans Require-Bundle † org.eclipse.jface.text † org.eclipse.ui.workbench.texteditor † org.eclipse.ui.editors
  141. 141 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : hiérarchie du TextEditor † Classe AbstractTextEditor † Chercher / Remplacer, navigateur hypertexte † Classe StatusTextEditor † Afficher une barre d’état † Classe AbstractDecoratedTextEditor † Numéro de lignes, change ruler, overview ruler, surbrillance de la sélection, … † Classe TextEditor † L’éditeur de texte utilisé par défaut † Toutes ces classes sont définies dans la plateforme Eclipse via les packages org.eclipse.ui.editors… † Nous verrons par la suite des APIs issues de la boîte à outils JFace
  142. 142 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : SourceViewerConfiguration † La configuration de l’affichage d’un éditeur de texte passe par l’utilisation d’un objet de type SourceViewerConfiguration † Coloration syntaxique † Formatage † Complétion automatique, … † La classe SourceViewConfiguration réalise que très peu de traitement, il faut passer par TextSourceViewerConfiguration † Pour associer un SourceViewerConfiguration à un éditeur de type texte † setSourceViewerConfiguration(SourceViewerConfiguration s) : appeler cette méthode à la création de l’éditeur † Au cours des fonctionnalités de notre éditeur nous découv- rirons certaines méthodes de SourceViewerConfiguration
  143. 143 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : SourceViewerConfiguration † Exemple : Associer un SourceViewerConfiguration à un éditeur de texte public class PassMemEditorPart extends AbstractDecoratedTextEditor { public PassMemEditorPart() { super(); this.setSourceViewerConfiguration( new PassMemSourceViewerConfiguration(getSharedColors(), getPreferenceStore())); } } Un SourceViewerConfiguration ne s’utilise que pour les éditeurs de type texte Associe un SourceViewerConfiguration avec l’éditeur PassMemEditorPart.java des projets PassMemEditorStep…
  144. 144 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † La coloration syntaxique permet de mettre en forme des éléments du contenu d’un texte † Caractère, mot, phrase, paragraphe, … † Possibilité de modifier les caractéristiques suivantes † Couleur de fond † Couleur de texte † Style du texte (italique, gras, police, taille, …) † Exemple
  145. 145 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † La coloration syntaxique est mise en place en utilisant un objet de type IPresentationReconciler † Un IPresentationReconciler permet de mettre à jour la mise en forme du document lors de la saisie d’un texte † Un objet PresentationReconciler s’appuie sur deux objets † Damager : détermine quelle portion du texte à été modifiée et qui doit être mise à jour (IPresentationDamager) † Repairer : permet de savoir les styles à appliquer sur la portion de texte qui a été modifiée (IPresentationRepairer) † La classe DefaultDamagerRepairer implémente les interfaces de Damager et Repairer (majorité des cas) † Petite explication …
  146. 146 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † L’objet IPresentationReconciler coordonne le Damager et le Repairer † Le Damager indique au IPresentationReconciler qu’une portion du texte a été modifiée † La portion du texte est transférée au Repairer pour que des styles y soient appliqués † L’utilisation de la classe DefaultDamagerRepairer traite à la fois les demandes de Damager et les besoins de Repairer † Le SourceViewerConfiguration fournit la méthode suivante † IPresentationReconciler getPresentationReconciler() : retourne un IPresentationReconciler pour un éditeur de texte donné
  147. 147 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique IPresentationReconciler install(…) getDamager(…) getRepairer(…) † Cartes des classes concernant un PresentationReconciler IPresentationDamager getDamageRegion(…) IPresentationRepairer createPresentation(…) Dépendance Héritage PresentationReconciler setDamager(…) setRepairer(…) DefaultDamageRepairer … ITokenScanner nextToken(…) Etudiée dans la suite
  148. 148 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † Exemple : Définir un IPresentationReconciler dans un SourceViewerConfiguration public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private ISharedTextColors refSharedTextColors; public PassMemSourceViewerConfiguration(ISharedTextColors pSharedTextColors, IPreferenceStore preferenceStore) { super(preferenceStore); this.refSharedTextColors = pSharedTextColors; } public IPresentationReconciler getPresentationReconciler( ISourceViewer sourceViewer) { DefaultDamagerRepairer commentDamagerRepairer= new DefaultDamagerRepairer(getRuleBasedScanner()); PresentationReconciler reconciler= new PresentationReconciler(); reconciler.setDamager(commentDamagerRepairer, IDocument.DEFAULT_CONTENT_TYPE); reconciler.setRepairer(commentDamagerRepairer, IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } ... // La suite dans les prochains transparents } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep1 Scanner étudié par la suite
  149. 149 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † Un DefaultDamagerRepairer s’appuie sur un scanner (ITokenScanner) pour déterminer les styles à appliquer † Un objet ITokenScanner décompose un texte sur des intervalles donnés et retourne des Tokens † Un token contient le style à appliquer sur la portion du texte † Pour déterminer quel token doit être retourné, l’objet ITokenScanner s’appuie sur des règles Rules † Résumé † Un objet ITokenScanner est associé à un ensemble de Rules † Chaque règle est associée à un Token † Si la règle est respectée, le Token est retourné
  150. 150 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † Cartes des classes concernant Scanner et Rules IToken getData() Token ITokenScanner nextToken() getTokenOffset() getTokenLength() ICharacterScanner read() unread() IRule evaluate() RuleBasedScanner setRules() setDefaultReturnToken() RuleBasedPartitionScanner setRules() IPredicatRule getSuccessToken() WhitespaceRule NumberRule WordRule SingleLineRule MultiLineRule WordPatternRule IWordDetector isWordStart() isWordPart() IWhitespaceDetector isWhitespace() Dépendance Héritage
  151. 151 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † Pour chaque règle (voir dans la suite) définie, un Token est retourné † Un Token est défini par l’interface IToken et une implémen- tation par défaut est fournie par Token † Un objet Token contient un type et un objet † Différentes valeurs de types (indéfini, espace, fin de fichier ou autre) † L’objet contenu peut de type arbitraire † String : pour donner des valeurs de partitions (voir plus tard) † TextAttribute(Color for, Color back, int style, Font font) : défini le style d’un texte
  152. 152 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique † Exemple : Construction d’objets de type Token public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private ITokenScanner getRuleBasedScanner() { Token tokenDefault = new Token(new TextAttribute( refSharedTextColors.getColor(new RGB(0, 0, 255)), null, SWT.ITALIC | SWT.BOLD)); Token keywordToken = new Token(new TextAttribute( refSharedTextColors.getColor(new RGB(0, 0, 0)), null, SWT.BOLD)); Token tokenComment1 = new Token(new TextAttribute( refSharedTextColors.getColor(new RGB(192, 192, 192)), refSharedTextColors.getColor(new RGB(20, 20, 20)), SWT.ITALIC)); Token tokenComment2 = new Token(new TextAttribute( refSharedTextColors.getColor(new RGB(140, 140, 140)), refharedTextColors.getColor(new RGB(200, 200, 200)), SWT.ITALIC)); ... } } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep1 Plusieurs Token ont été définis
  153. 153 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique / WordRule † Dans la plupart des cas, la mise en place de la coloration syntaxique consiste à lister un ensemble de mots clés † L’objet WordRule est utilisé dans les cas suivants † Colorer des mots complets † Colorer des mots qui débutent par un caractère donné † Un objet WordRule est construit à partir † IWordDetector : vérifie les caractères valides en début de mot et les caractères faisant parti d’un mot (détailler dans la suite) † Un Token par défaut utilisé par les mots non reconnus † Un objet WordRule permet d’ajouter des mots clés via † void addWord(String word, Token currentToken) : ajoute un mot et un Token qui doit être retourné si le mot est découvert
  154. 154 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique / WordRule † L’objet IWordDetector permet de vérifier les caractères valides en début de mot et tout au long du mot à l’aide des méthodes † boolean isWordStart(char c) : true s’il s’agit d’un caractère valide en début de mot † boolean isWordPart(char c) : true s’il s’agit d’un caractère valide en cours de mot † A chaque saisi d’un caractère WordRule demande à son objet IWordDetector si le mot en cours est valide † Si le caractère de début est découvert deux tokens peuvent être retournés † Le Token par défaut, puisque le mot n’est pas encore identifié † Le Token associé au mot clé
  155. 155 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique / WordRule † Exemple : Définition de règles à partir de WordRule public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private static final class WordDetector implements IWordDetector { public boolean isWordPart(char c) { return Character.isLetter(c); } public boolean isWordStart(char c) { return c == '@'; } } private ITokenScanner getRuleBasedScanner() { ... // Keywords. String[] keywords = {"@entry", "@login", "@password", "@url", "@note", "@creationDate", "@expirationDate"}; WordRule keywordRule= new WordRule(new WordDetector(), tokenDefault); for(String current : keywords) { keywordRule.addWord(current, keywordToken); } ... } } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep1 Utilisation du Token par défaut La liste des mots clés à découvrir Un mot clé contient uniquement des lettres et commence par @
  156. 156 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Coloration Syntaxique / WordRule † Exemple (suite) : Définition de règles à partir de WordRule Les mots clés ont été découverts Mot clé en cours d’être découvert (utilisation du Token par défaut) Ce mot n’a pas été découvert
  157. 157 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Col. Synt. / (Single|Multi)LineRule † La règle SingleLineRule définit une règle qui est satisfaite † Débute avec une chaîne de caractères donnée † Se termine avec une chaîne de caractères donnée † Se termine avec la ligne † Un caractère d’échappement est autorisé pour exprimer que la ligne suivante est considérée comme le prolongement de la ligne en cours † Constructeur de SingleLineRule † SingleLineRule(String startSeq, String endSeq, IToken tok, char esc) † EndOfLineRule est une variante de SingleLineRule à l’exception que la règle se termine avec la ligne † MultiLineRule propose les mêmes fonctionnalités que SingleLineRule sur plusieurs lignes
  158. 158 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Col. Synt. / (Single|Multi)LineRule † Exemple : Définition de règles à partir de SingleLineRule et MultiLineRule public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private ITokenScanner getRuleBasedScanner() { ... // Comment. EndOfLineRule commentRule1 = new EndOfLineRule("//", tokenComment,'c'); MultiLineRule commentRule2 = new MultiLineRule("/*", "*/", tokenComment, (char)0, true); ... } } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep1 Les commentaires sont identifiés
  159. 159 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Scanner † Un Scanner s’appuie sur les règles pour déterminer quel Token doit être retourné † Un Scanner est défini par l’interface ITokenScanner † La classe RuleBasedScanner est une implémentation par défaut et permet de retourner des règles † void setRules(IRule[] rules) : permet d’ajouter une liste de règles † Dans la suite nous utiliserons un scanner pour gérer les partitions via la classe RuleBasedPartitionScanner
  160. 160 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Scanner † Exemple : Mise en place d’un scanner public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private ITokenScanner getRuleBasedScanner() { ... RuleBasedScanner passMemScanner= new RuleBasedScanner(); // Keywords. String[] keywords = {"@entry", "@login", "@password", "@url", "@note", "@creationDate", "@expirationDate"}; WordRule keywordRule= new WordRule(new WordDetector(), tokenDefault); for(String current : keywords) { keywordRule.addWord(current, keywordToken); } // Comment. EndOfLineRule commentRule1 = new EndOfLineRule("//", tokenComment1,'c'); MultiLineRule commentRule2 = new MultiLineRule("/*", "*/", tokenComment2, (char)0, true); IRule[] rules= {keywordRule, commentRule1, commentRule2}; passMemScanner.setRules(rules); return passMemScanner; } } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep1 Trois règles ont été définies et associées au scanner
  161. 161 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Modèle de texte † Un texte est défini par un modèle de présentation IDocument et est affiché via un ITextViewer † Un modèle de présentation est associé à un type de fichier (défini par IEditorInput) par un objet IDocumentProvider † Un IDocumentProvider effectue donc la relation entre un objet IEditorInput et un objet IDocumentProvider † Responsabilités d’un IDocumentProvider † Création du modèle d’annotation IAnnotationModel (markers, …) † Création et gestion du modèle de présentation IDocument † …
  162. 162 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Modèle de texte † Exemple : Associer un IDocumentProvider par programme public class PassMemEditorPart extends AbstractDecoratedTextEditor { public PassMemEditorPart() { super(); this.setSourceViewerConfiguration( new PassMemSourceViewerConfiguration(getSharedColors(), getPreferenceStore())); this.setDocumentProvider(new PassDocumentProvider()); } } PassMemEditorPart.java du projet PassMemEditorStep2 Possibilité également d’associer un IDocumentProvider par déclaration. Deux solutions différentes ne sert à rien de les combiner
  163. 163 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Modèle de texte † Exemple : Associer un IDocumentProvider par extension plugin.xml du projet PassMemEditorStep2 Pour tous les fichiers dont l’extension est .pass le IDocumentProvider est associé au modèle de présentation
  164. 164 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Modèle de texte † Cartes des classes concernant le modèle de présentation IDocument get() getLength() getLine…() getDocumentPartitioner() getLine…() addDocumentListener() AbstractDocument getStore() getTracker() Dépendance Héritage IDocumentExtension3 getPartition() getContentType() IDocumentPartitioner connect() getPartition() getContentType() Document IDocumentListener documentAboutToBeChanged() documentChanged() Position getOffset() getLength() isDeleted() IDocumentProvider getAnnotationModel(Object element) getDocument(Object element) connect(Object element) IAnnotationModel addAnnotation(Annotation, Position) getAnnotationIterator() IEditorInput Modèle de Présentation Modèle du domaine
  165. 165 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Partition † Les partitions (décrites par ITypedRegion) permettent de diviser un document en plusieurs régions † Chaque région est associée à un type de contenu † Un partitionneur (décrit par IDocumentPartitioner) est responsable de la division du document en plusieurs régions † L’avantage des partitions est de pouvoir découper le document selon les différents langages exploités † Exemple d’un fichier Java † Code Java † Commentaire † Annotation, …
  166. 166 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Partition † Le partitionnement assure une mise à jour permanente de chaque partition lorsque le texte est modifié † Le IDocumentProvider assure que le partitionnement est installé † void setupDocument(Object ele, IDocument doc) : méthode à utiliser pour configurer le partitionnement † Le SourceViewerConfiguration a besoin de connaître le partitionnement et les types de contenu définis † String getConfiguredDocumentPartitioning(ISourceViewer sv) : retourne le partitionnement pour cette configuration † String[] getConfiguredContentTypes(ISourceViewer sv) : retourne les types de contenu définis dans le partitionnement
  167. 167 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Partition † Exemple : Ajouter un partitionnement Type par défaut Type commentaire Deux types de contenu sont utilisés un type par défaut et un type commentaire 1 2 1 2 1 2
  168. 168 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Partition † Exemple (suite) : Ajouter un partitionnement public class PassDocumentProvider extends FileDocumentProvider { public static final String PASS_PARTITIONING = "eclipse.workbench.passmemeditor.passpartitioning"; public static final String PASS_CODE = IDocument.DEFAULT_CONTENT_TYPE; public static final String PASS_COMMENT= "PASS_COMMENT"; private static final String[] CONTENT_TYPES= { PASS_CODE, PASS_COMMENT }; protected void setupDocument(Object element, IDocument document) { if (document instanceof IDocumentExtension3) { IDocumentExtension3 ext = (IDocumentExtension3)document; IDocumentPartitioner partitioner = createPassPartitioner(); ext.setDocumentPartitioner(PASS_PARTITIONING, partitioner); partitioner.connect(document); } } private IDocumentPartitioner createPassPartitioner() { EndOfLineRule commentRule1 = new EndOfLineRule("//", new Token(PASS_COMMENT),'c'); MultiLineRule commentRule2 = new MultiLineRule("/*", "*/", new Token(PASS_COMMENT), (char)0, true); IPredicateRule[] rules= { commentRule1, commentRule2 }; RuleBasedPartitionScanner scanner= new RuleBasedPartitionScanner(); scanner.setPredicateRules(rules); return new FastPartitioner(scanner, CONTENT_TYPES); } } PassDocumentProvider.java du projet PassMemEditorStep2 Identifiant du partitionnement Associer au document le partitionneur Connecter le partitionneur au document Utilisation d’un scanneur spécifique pour les partitions
  169. 169 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Partition † Exemple (suite) : Ajouter un partitionnement public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { public String getConfiguredDocumentPartitioning(ISourceViewer sourceViewer) { return PassDocumentProvider.PASS_PARTITIONING; } public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) { return new String[] { PassDocumentProvider.PASS_CODE, PassDocumentProvider.PASS_COMMENT }; } public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { PresentationReconciler reconciler = new PresentationReconciler(); reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); // Partition for other. DefaultDamagerRepairer defaultDamagerRepairer = new DefaultDamagerRepairer(getDefaultRuleBasedScanner()); reconciler.setDamager(defaultDamagerRepairer, PassDocumentProvider.PASS_CODE); reconciler.setRepairer(defaultDamagerRepairer, PassDocumentProvider.PASS_CODE); // Partition for Comment. DefaultDamagerRepairer commentDamagerRepairer = new DefaultDamagerRepairer(getCommentRuleBasedScanner()); reconciler.setDamager(commentDamagerRepairer, PassDocumentProvider.PASS_COMMENT); reconciler.setRepairer(commentDamagerRepairer, PassDocumentProvider.PASS_COMMENT); return reconciler; } ... // Suite dans le prochain transparent } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep2 Utilisation de deux scanneurs distinctes
  170. 170 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Partition † Exemple (suite) : Ajouter un partitionnement public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private ITokenScanner getCommentRuleBasedScanner() { Token tokenComment2 = new Token(new TextAttribute(refSharedTextColors .getColor(new RGB(140, 140, 140)), null, SWT.ITALIC)); RuleBasedScanner commentScanner = new RuleBasedScanner(); commentScanner.setDefaultReturnToken(tokenComment2); return commentScanner; } private ITokenScanner getDefaultRuleBasedScanner() { Token tokenDefault = new Token(new TextAttribute(refSharedTextColors .getColor(new RGB(0, 0, 255)), null, SWT.ITALIC | SWT.BOLD)); Token keywordToken = new Token(new TextAttribute(refSharedTextColors .getColor(new RGB(0, 0, 0)), null, SWT.BOLD)); RuleBasedScanner passMemScanner = new RuleBasedScanner(); // Keywords. String[] keywords = { "@entry", "@login", "@password", "@url", "@note", "@creationDate", "@expirationDate" }; WordRule keywordRule = new WordRule(new WordDetector(), tokenDefault); for (String current : keywords) { keywordRule.addWord(current, keywordToken); } IRule[] rules = { keywordRule }; passMemScanner.setRules(rules); return passMemScanner; } } Utilisation des règles définies pour le type PASS_COMMENT Principe déjà évoqué sans l’utilisation de partitionnement PassMemSourceViewerConfiguration.java du projet PassMemEditorStep2
  171. 171 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † L’assistant de contenu (Content Assist) permet de fournir une aide contextuelle à l’utilisateur † Selon ce contexte (texte en sélection, fraction de mot, …) l’assistant peut proposer des solutions adaptées † Fournir des patrons de texte (voir plus tard) † Compléter un mot qui était en cours de saisie † Enrichir une portion de texte sélectionné † … † L’aide contextuelle est disponible via la combinaison de touche Ctrl + Espace ou en fonction d’un certain délai d’attente
  172. 172 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple : Intégration d’un assistant de contenu Complétion de mots clés (à définir l’algorithme qui liste les propositions de la complétion) Pour chaque mot clé sélectionné, une information peut être donnée L’assistant de contenu est ouvert
  173. 173 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † L’objet SourceViewerConfiguration est utilisé pour déclarer l’assistant de contenu † IContentAssistant getContentAssistant(ISourceViewer p) : définit l’objet IContentAssistant employé pour activer l’assistant de contenu † Un objet IContentAssistant est utilisé pour configurer l’assistant de contenu † Pour chaque type de contenu un objet IContentAssistProcessor doit être défini † Paramètres liés à l’affichage (couleur, délai d’apparition, …) † Des écouteurs liés au déroulement de l’assistant (début, fin, sélection de l’élément proposé par l’assistant)
  174. 174 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple (suite) : Intégration d’un assistant de contenu public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { ... public IContentAssistant getContentAssistant(final ISourceViewer sourceViewer) { ContentAssistant assistant= new ContentAssistant(); assistant.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); assistant.setContentAssistProcessor(new HippieProposalProcessor(), PassDocumentProvider.PASS_COMMENT); PassMemCompletionProcessor processor = new PassMemCompletionProcessor(); assistant.setContentAssistProcessor(processor, PassDocumentProvider.PASS_CODE); assistant.setContentAssistProcessor(processor,IDocument.DEFAULT_CONTENT_TYPE); // Paramètres du ContentAssistant étudiés plus tard return assistant; } } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep3 Un IContentAssistProcessor prédéfini dont le fonctionnement est d’identifier des mots qui existent déjà dans les éditeurs ouverts Défini notre propre IContentAssistProcessor (voir dans la suite)
  175. 175 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Pour que l’assistant de contenu puisse démarrer lors de l’utilisation du raccourci CTRL + Espace il faut créer une action de type ContentAssistAction † Exemple : activer l’assistant de contenu via CTRL + Espace public class PassMemEditorPart extends AbstractDecoratedTextEditor { public PassMemEditorPart() { super(); this.setSourceViewerConfiguration( new PassMemSourceViewerConfiguration(getSharedColors(), getPreferenceStore())); this.setDocumentProvider(new PassDocumentProvider()); } protected void createActions() { super.createActions(); ContentAssistAction action= new ContentAssistAction(PassMemEditorMessages.getBundle(), "contentAssist.", this); action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); setAction("ContentAssist", action); } } PassMemEditorPart.java du projet PassMemEditorStep3
  176. 176 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple : Ajouter les actions d’assistant de contenu dans le menu principal public class PassMemEditorActionContributor extends TextEditorActionContributor { private RetargetTextEditorAction fContentAssist; public PassMemEditorActionContributor() { fContentAssist = new RetargetTextEditorAction(PassMemEditorMessages.getBundle(), "contentAssist."); fContentAssist.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); } public void contributeToMenu(IMenuManager menu) { super.contributeToMenu(menu); IMenuManager editMenu = menu .findMenuUsingPath(IWorkbenchActionConstants.M_EDIT); if (editMenu != null) { editMenu.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, fContentAssist); } } public void setActiveEditor(IEditorPart part) { super.setActiveEditor(part); if (part instanceof ITextEditor) { fContentAssist.setAction(getAction((ITextEditor) part, "ContentAssist")); } } } PassMemEditorActionContributor.java du projet PassMemEditorStep3
  177. 177 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Un objet IContentAsssistProcessor fournit un mécanisme pour gérer la complétion de texte (ICompletionProposal) † Différentes méthodes pour la gérer cette complétion † ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) : pour définir les propositions de complétion † char[] getCompletionProposalAutoActivationCharacters() : les caractères qui déclenchent la complétion † Dans la méthode computeCompletionProposals, il faut fournir un algorithme qui établie une liste de propositions suivant le contexte courant † @e fournira les propositions @entry et @expirationDate
  178. 178 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple (suite) : Intégration d’un assistant de contenu public class PassMemCompletionProcessor implements IContentAssistProcessor { public char[] getCompletionProposalAutoActivationCharacters() { return new char[] {'@'}; } public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { IDocument document = viewer.getDocument(); int currOffset = offset - 1; String currWord = ""; try { char currChar; while (currOffset > 0 && !Character.isWhitespace(currChar = document.getChar(currOffset))) { currWord = currChar + currWord; currOffset--; } List<String> currentSuggests = buildSuggests(currWord); ICompletionProposal[] proposals = null; if (currentSuggests.size() > 0) { proposals = buildProposals(currentSuggests, currWord, offset - currWord.length()); } return proposals; } catch (BadLocationException e) { e.printStackTrace(); return null; } } PassMemCompletionProcessor.java du projet PassMemEditorStep3 Extrait à partir du mot courant la liste des propositions Identifie le mot en cours de saisie Construit à partir des propositions des objets ICompletionProposal Le caractère @ qui permet de déclencher l’assistant de contenu
  179. 179 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple (suite) : Intégration d’un assistant de contenu public class PassMemCompletionProcessor implements IContentAssistProcessor { private List<String> buildSuggests(String word) { List<String> current = new ArrayList<String>(); for (int i = 0 ; i < PassMemSourceViewerConfiguration.KEYWORDS.length ; i++) { if (PassMemSourceViewerConfiguration.KEYWORDS[i].startsWith(word)) { current.add(PassMemSourceViewerConfiguration.KEYWORDS[i]); } } return current; } private ICompletionProposal[] buildProposals(List<String> suggestions, String replacedWord, int offset) { ICompletionProposal[] proposals = new ICompletionProposal[suggestions.size()]; int index = 0; for (Iterator<String> i = suggestions.iterator(); i.hasNext();) { String currSuggestion = (String) i.next(); IContextInformation contextInfo = new ContextInformation(null, currSuggestion); proposals[index] = new CompletionProposal(currSuggestion, offset, replacedWord.length(), currSuggestion.length(), null, currSuggestion, contextInfo,currSuggestion); index++; } return proposals; } ... } PassMemCompletionProcessor.java du projet PassMemEditorStep3 Retourne une information contextuelle par rapport à la proposition en cours de sélection
  180. 180 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Pour afficher l’information contextuelle (partie droite) il faut configurer l’objet IContentAssistant de cette façon † Optionnellement si vous souhaitez paramétrer l’affichage des informations contextuelles † Redéfinir la méthode getInformationControlCreator() qui indiquera la présentation à utiliser † Fournir un IInformationPresenter qui précisera comment afficher les informations contextuelles assistant.setInformationControlCreator(getInformationControlCreator(sourceViewer)); public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, presenter); } }; }
  181. 181 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple : Modifier l’apparence de l’information contextuelle public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { private DefaultInformationControl.IInformationPresenter presenter; public PassMemSourceViewerConfiguration(ISharedTextColors pSharedTextColors, IPreferenceStore preferenceStore) { super(preferenceStore); this.refSharedTextColors = pSharedTextColors; presenter = new DefaultInformationControl.IInformationPresenter() { public String updatePresentation(Display display, String infoText, TextPresentation presentation, int maxWidth, int maxHeight) { StyleRange range = new StyleRange(0, 1, null, refSharedTextColors.getColor(new RGB(220,220,220)), SWT.BOLD | SWT.ITALIC); presentation.addStyleRange(range); return infoText; } }; } public IContentAssistant getContentAssistant(final ISourceViewer sourceViewer) { ... assistant.setInformationControlCreator(getInformationControlCreator(sourceViewer)); } public IInformationControlCreator getInformationControlCreator(ISourceViewer sourceViewer) { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, presenter); } }; } ... } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep3 Le premier caractère est un gras et sa couleur de fond est modifiée
  182. 182 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Un objet IContentAsssistProcessor fournit également un traitement pour afficher de l’information contextuelle † L’information peut être affichée à différents cas † A la suite d’un caractère donné (principe étudié précédemment) † Une fois une proposition de complétion insérée dans le document † Différentes méthodes liées à la gestion de l’information † IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) : pour définir les informations susceptibles d’être affichées † char[] getContextInformationAutoActivationCharacters() : les caractères qui déclenchent l’affichage des informations † IContextInformationValidator getContextInformationValidator() : défini un objet qui établie si une information doit être affichée ou pas à la suite de la complétion 1 2
  183. 183 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple : Afficher de l’information contextuelle à la suite d’un caractère donné public class PassMemCompletionProcessor implements IContentAssistProcessor { public char[] getContextInformationAutoActivationCharacters() { return new char[] {'?'}; } public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { ContextInformation[] contextInfos = new ContextInformation[PassMemSourceViewerConfiguration.KEYWORDS.length]; for (int i = 0; i < contextInfos.length; i++) { contextInfos[i] = new ContextInformation(null, PassMemSourceViewerConfiguration.KEYWORDS[i]); } return contextInfos; } ... } PassMemCompletionProcessor.java du projet PassMemEditorStep3 Affichage de l’information à la suite du caractère -
  184. 184 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Assistant de contenu † Exemple : Afficher de l’information contextuelle une fois une proposition de complétion insérée dans le document public class PassMemCompletionProcessor implements IContentAssistProcessor { ... private ICompletionProposal[] buildProposals(List<String> suggestions, String replacedWord, int offset) { ... for (Iterator<String> i = suggestions.iterator(); i.hasNext();) { ... IContextInformation contextInfo = new ContextInformation(null, currSuggestion); ... } return proposals; } public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { ContextInformation[] contextInfos = new ContextInformation[PassMemSourceViewerConfiguration.KEYWORDS.length]; for (int i = 0; i < contextInfos.length; i++) { contextInfos[i] = new ContextInformation(null, PassMemSourceViewerConfiguration.KEYWORDS[i]); } return contextInfos; } public IContextInformationValidator getContextInformationValidator() { return new ContextInformationValidator(this); } } Le validator compare que l’information utilisée par la complétion est disponible dans computeContextInformation PassMemCompletionProcessor.java du projet PassMemEditorStep3
  185. 185 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Un template permet à partir de l’assistant de contenu d’insérer un squelette de texte † Le contenu du texte inséré peut être complété par des propositions de valeurs de mots † Un template s’utilise suivant un contexte † Une boucle while ne peut être insérée que dans du code Java † La définition d’un template est réalisée soit † au travers du point d’extension org.eclipse.ui.editors.template † programmatiquement via Template et TemplateContexteType † Privilégier la solution via extension permet † de s’assurer d’une meilleure portabilité du code † d’enrichir les templates existants
  186. 186 Editors - M. Baron - Page mickael-baron.fr mickaelbaron †

    Processus d’utilisation des templates depuis l’assistant de contenu † En fonction de la position sur le document, identification du type de contexte (ContextType) † En fonction de l’identification de contexte, récupération des templates disponibles (TemplateStore) † Proposition des templates à l’utilisateur † Insertion du template dans le document † Proposition des valeurs pour les différentes variables (TemplateContexte) Editeur et le texte : Template 1 2 3 4 5
  187. 187 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple : Définir un template par extension Création d’une extension à partir du point d’extension org.eclipse.ui.editors.templates
  188. 188 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple (suite) : Définir un template par extension plugin.xml du projet PassMemEditorStep4 Définir un élément template contextTypeId permet de préciser l’identifiant du contexte (définir plus tard)
  189. 189 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple (suite) : Définir un template par extension plugin.xml du projet PassMemEditorStep4 Définition de l’identifiant contextTypeId en utilisant une classe prédéfinie de la plateforme
  190. 190 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple (suite) : Définir un template par extension plugin.xml du projet PassMemEditorStep4 Pour le template proposé est défini le texte à insérer. Les textes ${…} désignent des variables
  191. 191 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple (suite) : Définir un template par extension plugin.xml du projet PassMemEditorStep4 Pour la variable entry ces valeurs seront déterminés par un objet de type TemplateVariableResolver L’élément resolver nommé entryValues permet d’obtenir la liste des valeurs à proposer pour la variable entry
  192. 192 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Les classes de type TemplateVariableResolver permettent d’énumérer les valeurs possibles pour une variable † Ces valeurs sont proposées après l’insertion du template dans le document courant † Pour retourner ces valeurs possibilité de redéfinir certaines méthodes † String[] resolveAll(TemplateContext context) : un ensemble de valeurs à proposer † String resolve(TemplateContext context) : une seule valeur à proposer † Un objet de type TemplateContext donne accès à l’ensemble des variables et aux valeurs associées
  193. 193 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple (suite) : Définir un template par extension public class TemplateEntryVariableResolver extends TemplateVariableResolver { protected String[] resolveAll(TemplateContext context) { return new String[] {"twitter", "gmail"}; } } TemplateEntryVariableResolver.java du projet PassMemEditorStep4
  194. 194 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † La classe TemplateCompletionProcessor est un objet IContentAssistProcessor dédié à la gestion de l’insertion de template via l’assistant de contenu † Différentes méthodes qui permettent de respecter le processus d’insertion de template † TemplateContextType getContextType(ITextViewer viewer, IRegion region) : retourne le type de contexte en fonction de la position sur le document † Template[] getTemplates(String contextTypeId) : retourne une liste de templates en fonction de l’identifiant de type de contexte † Image getImage(Template template) : retourne une image associé à un template
  195. 195 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple : Intégrer un template défini par extension dans un assistant de contenu public class PassMemTemplateProcessor extends TemplateCompletionProcessor { protected TemplateContextType getContextType(ITextViewer viewer, IRegion region) { return Activator.getDefault().getContextTypeRegistry().getContextType(Activator.PASS_MEM_TEMPLATE_CONTEXT); } protected Image getImage(Template template) { return Activator.getImage("template"); } protected Template[] getTemplates(String contextTypeId) { ContributionTemplateStore templateStore= Activator.getDefault().getTemplateStore(); return templateStore.getTemplates(contextTypeId); } } PassMemTemplateProcessor.java du projet PassMemEditorStep4
  196. 196 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template public class Activator extends AbstractUIPlugin { private ContributionTemplateStore fTemplateStore; private ContributionContextTypeRegistry fRegistry; public static final String PASS_MEM_TEMPLATE_CONTEXT= "eclipse.workbench.passmemeditorstep4.templatecontextid"; private static final String TEMPLATES_PREFERENCE_KEY= "templates"; public ContributionTemplateStore getTemplateStore() { if (fTemplateStore == null) { fTemplateStore= new ContributionTemplateStore(getContextTypeRegistry(), getPreferenceStore(), TEMPLATES_PREFERENCE_KEY); try { fTemplateStore.load(); } catch (IOException x) { x.printStackTrace(); } } return fTemplateStore; } public ContributionContextTypeRegistry getContextTypeRegistry() { if (fRegistry == null) { fRegistry= new ContributionContextTypeRegistry(); fRegistry.addContextType(PASS_MEM_TEMPLATE_CONTEXT); } return fRegistry; } ... } † Exemple (suite) : Intégrer un template défini par extension dans un assistant de contenu Activator.java du projet PassMemEditorStep4
  197. 197 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple : Combiner une intégration d’un template défini par extension et une proposition de mots clés public class PassMemTemplateProcessorBis extends TemplateCompletionProcessor { public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { final ICompletionProposal[] computeCompletionProposals = super.computeCompletionProposals(viewer, offset); IDocument document = viewer.getDocument(); int currOffset = offset - 1; String currWord = ""; try { char currChar; while (currOffset > 0 && !Character.isWhitespace(currChar = document.getChar(currOffset))) { currWord = currChar + currWord; currOffset--; } List<String> currentSuggests = buildSuggests(currWord); ICompletionProposal[] proposals = null; if (currentSuggests.size() > 0) { proposals = buildProposals(currentSuggests, currWord, offset - currWord.length()); } ICompletionProposal[] copyOf = Arrays.copyOf(computeCompletionProposals, computeCompletionProposals.length + proposals.length); for (int i = 0 ; i < proposals.length ; i++) { copyOf[i+computeCompletionProposals.length] = proposals[i]; } return copyOf; } catch (BadLocationException e) { return null; } }} PassMemTemplateProcessorBis.java du projet PassMemEditorStep4 Récupère les objets ICompletionProposal liés aux templates
  198. 198 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Template † Exemple : Intégrer un template défini programmatiquement dans un assistant de contenu public class PassMemTemplateProcessorBisBis implements IContentAssistProcessor { private static final String CONTEXT_ID= "eclipse.workbench.passmemeditorstep4.templatecontextid"; private TemplateContextType fContextType= new TemplateContextType( CONTEXT_ID, "Default Template Context"); private Template fTemplate= new Template( "PassTemplate", // name "This Template inserts all keywords", // description CONTEXT_ID, "@entry\n${entry}\n@login\n@password\n@url\n@note\n@creationDate\n@expirationDate", // pattern false); public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { ... final ICompletionProposal[] tabTemplates = new ICompletionProposal[1]; TemplateContext context= new DocumentTemplateContext(fContextType, document, offset, 0); Region region= new Region(offset, 0); TemplateProposal proposal= new TemplateProposal(fTemplate, context, region, Activator.getImage("template")); tabTemplates[0] = proposal; ... final ICompletionProposal[] copyOf = Arrays.copyOf(tabTemplates, tabTemplates.length + proposals.length); for (int i = 0 ; i < proposals.length ; i++) { copyOf[i+tabTemplates.length] = proposals[i]; } return copyOf; } catch (BadLocationException e) { e.printStackTrace(); return null; } } ... } PassMemTemplateProcessorBisBis.java du projet PassMemEditorStep4
  199. 199 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Mise à jour † Lors de la modification du document d’un éditeur il peut être intéressant † D’identifier des erreurs (orthographiques) et de les ajouter dans le modèle d’annotation † De construire un modèle (utilisé pour l’affichage dans Outline) † L’écoute des changements ne peut être réalisé dans le Thread-UI car la mise à jour est trop couteuse † Le Reconciler fournit un Thread pour ce type de tâche † Traitement déclenché quand l’utilisateur a fini de saisir du texte † Interruptible quand l’utilisateur re-saisit du texte † Autorise la synchonisation † Implémentation par défaut : MonoReconciler † La stratégie de traitement est définit dans IReconcilingStrategy
  200. 200 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Mise à jour † Un objet IReconcilingStrategy est utilisé pour définir les traitements de mise à jour d’un document † Différentes méthodes à implémenter † void setDocument(IDocument document) : pour connaître le document sur lequel la stratégie s’applique † void reconcile(IRegion partition) : déclenché à chaque modification du document et permet de connaître la région impactée † Dans la méthode reconcile, différents traitements peuvent être réalisés † Utilisation d’un moteur de vérification orthographique (Spelling Checking) et création d’annotation SpellingAnnotation † Utilisation d’un parser pour identifier les éléments saisis dans le texte puis utiliser le résultat du parser pour l’affichage dans Outline
  201. 201 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Outline † Exemple : Création d’une vue Outline personnalisée Le contenu de l’éditeur est analysé puis affiché dans la vue Outline La vue Outline affiche une liste d’éléments. Un élément sélectionné permet de mettre en surbrillance la portion sur l’éditeur
  202. 202 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Outline † Exemple (suite) : Création d’une vue Outline personnalisée public class PassMemSourceViewerConfiguration extends TextSourceViewerConfiguration { public PassMemSourceViewerConfiguration(ISharedTextColors pSharedTextColors, IPreferenceStore preferenceStore, PassMemEditorPart pEditor) { super(preferenceStore); this.refSharedTextColors = pSharedTextColors; this.refEditor = pEditor; } public IReconciler getReconciler(ISourceViewer sourceViewer) { IReconcilingStrategy strategy = new PassMemReconcileStrategy(sourceViewer, refEditor); return new MonoReconciler(strategy, false); } ... } PassMemSourceViewerConfiguration.java du projet PassMemEditorStep5
  203. 203 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Outline † Exemple (suite) : Création d’une vue Outline personnalisée public class PassMemReconcileStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { private IDocument fDocument; private final PassMemEditorPart fEditor; public PassMemReconcileStrategy(ISourceViewer pSourceViewer, PassMemEditorPart refEditorPart) { fEditor = refEditorPart; } public void reconcile(IRegion partition) { PassMemModel value = new PassMemParser().parse(fDocument); this.fEditor.setPassMemModel(value); } public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { } public void setDocument(IDocument document) { fDocument = document; } public void initialReconcile() { reconcile(new Region(0, fDocument.getLength())); } public void setProgressMonitor(IProgressMonitor monitor) { } } PassMemReconcileStrategy.java du projet PassMemEditorStep5 Utilisation d’un parser qui analyse le document de l’éditeur
  204. 204 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Outline † Exemple (suite) : Création d’une vue Outline personnalisée public final class PassMemParser { private IDocument fDocument; private int fLine; private int fLineCount; public PassMemModel parse(IDocument document) { try { fDocument= document; fLine= 0; fLineCount= fDocument.getNumberOfLines(); return parsePassMem(); } catch (BadLocationException e) { return null; } } private PassMemModel parsePassMem() throws BadLocationException { PassMemModel refModel = new PassMemModel(); while (fLine < fLineCount) { IRegion region= fDocument.getLineInformation(fLine); String text= fDocument.get(region.getOffset(), region.getLength()); for (int i = 0 ; i < PassMemSourceViewerConfiguration.KEYWORDS.length ; i++) { if (text.startsWith(PassMemSourceViewerConfiguration.KEYWORDS[i])) { refModel.addKeywordPosition(buildKeywordPosition(text, PassMemSourceViewerConfiguration.KEYWORDS[i], region.getOffset(), refModel)); } } fLine++; } return refModel; } private KeywordPosition buildKeywordPosition(String text, String keyword, int offset, PassMemModel pModel) { KeywordPosition refKeywordPosition = new KeywordPosition(keyword, offset, text.length()); return refKeywordPosition; } } PassMemParser.java du projet PassMemEditorStep5 A chaque découverte d’un mot clé, ajout dans un modèle
  205. 205 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Outline † Exemple (suite) : Création d’une vue Outline personnalisée public class PassMemModel { private List<KeywordPosition> keywordPositions; public PassMemModel() { keywordPositions = new ArrayList<KeywordPosition>(); } public void addKeywordPosition(KeywordPosition current) { this.keywordPositions.add(current); } public List<KeywordPosition> getKeywordPositions() { return this.keywordPositions; } } public class KeywordPosition { private String key; private int offset; private int length; public KeywordPosition(String key, int offset, int length) { this.key = key; this.offset = offset; this.length = length; } public String getKey() { return key; } public int getOffSetPosition() { return offset; } public int getLength() { return length; } } PassMemModel.java du projet PassMemEditorStep5 KeywordPosition.java du projet PassMemEditorStep5
  206. 206 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : Outline † Exemple (suite) : Création d’une vue Outline personnalisée public class PassMemEditorPart extends AbstractDecoratedTextEditor { private PassMemOutlinePage fOutlinePage; private PassMemModel fPassMemModel; ... public Object getAdapter(Class required) { if (IContentOutlinePage.class.equals(required)) { if (fOutlinePage == null) createOutlinePage(); return fOutlinePage; } return super.getAdapter(required); } private void createOutlinePage() { fOutlinePage = new PassMemOutlinePage(this); fOutlinePage.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection= event.getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection ss= (IStructuredSelection) selection; Object element= ss.getFirstElement(); if (element instanceof KeywordPosition) { KeywordPosition re= (KeywordPosition) element; selectAndReveal(re.getOffSetPosition(), re.getLength()); } } } }); } public void setPassMemModel(PassMemModel passMemModel) { fPassMemModel = passMemModel; if (fOutlinePage != null) fOutlinePage.updatePassMemModel(passMemModel); } public PassMemModel getPassMemModel() { return fPassMemModel; } } PassMemEditorPart.java du projet PassMemEditorStep5
  207. 207 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Editeur

    et le texte : il reste … † De nombreuses améliorations peuvent encore être apportées à un composant TextEditor † Folding : plier / déplier une zone de texte † Spell Checking : vérification orthographique en exploitant un moteur de vérification et un dictionnaire de mots † Hyperlinks : ajouter des liens hypertextes dans du texte Des informations complémentaires sur la mise en place de ces améliorations peuvent être trouvées www.eclipse.org/eclipse/platform- text/development/dev.html
  208. 208 Editors - M. Baron - Page mickael-baron.fr mickaelbaron Bilan

    † Ce support de cours a exploré les différentes APIs de la plateforme Eclipse pour la mise en place d’éditeur † Deux types d’éditeurs sont à distinguer † Ceux qui fournissent un composant TextEditor pour la manipulation de texte † Ceux qui fournissent des IHMs formulaires qui facilitent l’édition des éditeurs à base de texte (complémentaires aux premiers) † Les éditeurs permettent donc de manipuler des DSL (Domain Specific Language) † Des projets du domaine de la modélisation tentent de faire abstraction de l’apprentissage des APIs et focalisent essentiellement sur la modélisation (Xtext) † Dans le chapitre lié à la modélisation, nous reviendrons sur ces technologies