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

Eclipse : développement de vues

Eclipse : développement de vues

Ce support de cours s'intéresse à détailler la construction de vues 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, étude des classes ViewPart StickyView et Category, vues multiples, registre des vues, cycle de vie, communication entre vues (en direct, le service de sélection et IAdaptable).

Mickael BARON

August 19, 2009
Tweet

More Decks by Mickael BARON

Other Decks in Programming

Transcript

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

    2009 (Rev. Juillet 2010) mailto:[email protected] ou mailto:[email protected] mickael-baron.fr mickaelbaron Chapitre 3 : Conception de plug-ins Workbench : Views
  2. 2 Views - 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 Views - 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 Views - M. Baron - Page mickael-baron.fr mickaelbaron Organisation

    du cours sur le Workbench : Views Tous les exemples du cours sont disponibles directement à l’adresse mickael-baron.fr/eclipse/views † Construction déclarative † ViewPart, StickyView et Category † Vues multiples † Registre des vues † Cycle de vie † Communication entre vues
  5. 5 Views - M. Baron - Page mickael-baron.fr mickaelbaron Déroulement

    du cours † Pédagogie du cours † Illustration avec de nombreux exemples qui sont disponibles à l’adresse mbaron.developpez.com/eclipse/views † Des bulles d’aide tout au long du cours † Logiciels utilisés † Eclipse 3.4.2 Ganymede † Pré-requis † Connaître la boite à outils SWT, JFace † Connaître la structure d’un plug-ins et savoir créer une extension † Remerciements † Developpez.com : djo.mos (Jawher MOUSSA) † … Ceci est une alerte Ceci est une astuce
  6. 6 Views - M. Baron - Page mickael-baron.fr mickaelbaron Ressources

    … † Des articles sur la construction des vues † www.eclipse.org/articles/viewArticle/ViewArticle2.html † www.vogella.de/articles/RichClientPlatform/article.html † eclipsesource.com/blogs/2010/06/23/tip-how-to-detect-that-a-view-was-detached/ † Des articles sur la communication entre vues † www.eclipse.org/articles/Article-WorkbenchSelections/article.html † www.eclipse.org/articles/Article-Properties-View/properties-view.html † wiki.eclipse.org/FAQ_How_do_I_find_out_what_object_is_selected%3F † wiki.eclipse.org/FAQ_How_do_I_use_IAdaptable_and_IAdapterFactory%3F † Des livres † Eclipse – Building Commercial-Quality Plug-ins, 2004 - ISBN : 0-321-22847-2 † Eclipse – Rich Client Platform, 2005 – ISBN : 0-321-33461-2
  7. 7 Views - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    † Dans la partie précédente nous avons étudié les perspectives qui permettent d’agencer vues et éditeurs † Une vue est utilisée pour naviguer dans une hiérarchie d’information, ouvrir un éditeur ou afficher des propriétés en relation directe avec l’éditeur actif † Cette partie sera l’occasion d’étudier en détail les construc- tions des vues et les possibilités d’interaction † construction d’une vue par extension † construction de vues multiples † construction de vues de type Sticky Views † registre des vues † mécanismes de communication entre les vues
  8. 8 Views - M. Baron - Page mickael-baron.fr mickaelbaron Généralités

    IWorkbenchPage activate(IWorkbenchPart) bringToTop(IWorkbenchPart) findView(String) getViewReferences() hideView(IViewPart) showView(String) IWorkbenchPartReference getId() getPage() getPart(boolean) IViewReference getView(boolean) isFastView() WorkbenchPart dispose() getConfigurationElement() getSite() setPartName(String) ViewPart getViewSite() init(IViewSite) init(IViewSite, IMemento) saveState(IMemento) IWorkbenchPart createPartControl(Composite) dispose() getSite() IViewPart getViewSite() init(IViewSite) init(IViewSite, IMemento) saveState(IMemento) IWorkbenchSite getPage() getSelectionProvider() getShell() getWorkbenchWindow() setSelectionProvider(ISelectionProvider) IWorkbenchPartSite getKeyBindingService() registerContextMenu(MenuManager, ISelectionProvider) registerContextMenu(String, MenuManager, ISelectionProvider) IViewSite getActionBars() † Principales interfaces et classes pour la gestion des vues Dépendance Héritage
  9. 9 Views - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d'une vue par extension † Sélectionner le point d’extension org.eclipse.ui.views Création d’une vue à partir du template Sample View Création d’une extension à partir du point d’extension org.eclipse.ui.views
  10. 10 Views - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d'une vue par extension † Définir les attributs de l’extension Identifiant de la vue Nom de la vue Objet de type ViewPart décrivant le contenu d’une vue Ratio pour l’ouverture de la vue en mode FastView Possibilité d’avoir plusieurs instances de cette vue Possibilité de retrouver la vue après un redémarrage du Workbench
  11. 11 Views - M. Baron - Page mickael-baron.fr mickaelbaron Construction

    d'une vue par extension † Etendre une vue de type ViewPart package eclipse.workbench.viewexample.views public class SimpleView extends ViewPart { public SampleView() { } public void createPartControl(Composite parent) { Label myLabel = new Label(parent, SWT.NONE); myLabel.setText("Simple View"); } public void setFocus() { } } SimpleView.java du Projet ViewExample Création du contenu en utilisant des composants SWT
  12. 12 Views - M. Baron - Page mickael-baron.fr mickaelbaron ViewPart

    † La définition d’une nouvelle vue est obtenue en héritant de la classe ViewPart † Des méthodes abstraites doivent être implémentées † void createPartControl(Composite parent) : création de l’IHM par des composants SWT où parent est le conteneur de la vue † void setFocus() : précise le composant SWT qui aura le focus à chaque activation de la vue † Des méthodes qui peuvent être redéfinies (à voir plus tard) † setInitializationData(…) : appelée lors de la création de l’extension † init(IWorkbenchPartSite p) : appelée pour initialiser le contexte de la vue (quelle est la Shell parente, la page active, …) † Des méthodes à exploiter † IViewSite getViewSite() : site de la vue (étudier par la suite) † void setPartName(String ti) : modifie le titre
  13. 13 Views - M. Baron - Page mickael-baron.fr mickaelbaron ViewPart

    † L’implémentation de la méthode createPartControl suit un processus assez similaire † Modifier l’agent de placement du parent si nécessaire † Créer un ou plusieurs composants SWT / JFace à l’intérieur du parent † Déclarer des nouvelles actions avec la barre d’actions du site † Déclarer un nouveau menu contextuel dans le Workbench avec le site † Déclarer l’écoute de la sélection d’un composant avec le site Les déclarations d’actions, d’un menu contextuel et de la sélection seront étudiés en détail dans la suite
  14. 14 Views - M. Baron - Page mickael-baron.fr mickaelbaron ViewPart

    et IViewSite † Le site d’une vue est une sorte de pont entre la vue (définit par ViewPart) et le Workbench † Il faut voir le site comme un objet qui permet de lier la vue au contexte de l’application † L’interface IWorkbenchSite est une abstraction d’un site † IWorkbenchPage getPage() : la page dans laquelle la vue est stockée † Shell getShell() : la fenêtre physique d’où est contenue la vue † IWorkbenchWindow getWorkbenchWindow() : la fenêtre déclarative contenant la vue † 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
  15. 15 Views - M. Baron - Page mickael-baron.fr mickaelbaron ViewPart

    et IViewSite † L’interface IViewSite (sous type de IWorkbenchSite) fournit des méthodes spécifiques † IActionBars getActionBars() : la barre d’action de la vue † String getSecondaryId() : identifiant secondaire utilisé pour gérer des instances multiples de la vue Le contenu de la vue est défini par la méthode createPartControl L’ajout d’actions se fait via le site de la vue Sera utilisée dans la partie Vues Multiples
  16. 16 Views - M. Baron - Page mickael-baron.fr mickaelbaron ViewPart

    et IViewSite † Exemple : accéder aux éléments du site d’une vue package eclipse.workbench.viewexample.views public class SiteView extends ViewPart { public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(2,true)); Button buttonLeft = new Button(parent, SWT.FLAT); buttonLeft.setText("LEFT_Button"); Button buttonRight = new Button(parent, SWT.FLAT); buttonRight.setText("RIGHT_Button"); GridData myGridData = new GridData(GridData.FILL_BOTH); myGridData.horizontalSpan = 2; viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setContentProvider(new ViewContentProvider()); viewer.setLabelProvider(new ViewLabelProvider()); viewer.getControl().setLayoutData(myGridData); viewer.setInput(this.getViewSite()); this.createActions(); this.createToolBar(); this.createContextMenu(); } } SiteView.java du Projet ViewExample Modification de l’agent de placement du parent Construction de l’interface à partir de composants SWT Création des actions, de la barre d’outils et du menu contextuel
  17. 17 Views - M. Baron - Page mickael-baron.fr mickaelbaron ViewPart

    et IViewSite † Exemple (suite) : accéder aux éléments du site d’une vue ... private void createActions() { action1 = new Action("Action 1") { public void run() { System.out.println("Action 1 Performed"); } }; action2 = new Action("Action 2") { public void run() { System.out.println("Action 2 Performed"); } }; } private void createToolBar() { IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager(); mgr.add(this.action1); mgr.add(this.action2); } private void createContextMenu() { MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager mgr) { mgr.add(action1); mgr.add(new Separator()); mgr.add(action2); } }); Menu menu = menuMgr.createContextMenu(viewer.getControl()); Viewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, viewer); } SiteView.java du Projet ViewExample Ce menu est déclaré dans le Workbench. L’extension du menu devient possible
  18. 18 Views - M. Baron - Page mickael-baron.fr mickaelbaron Sticky

    View † Une Sticky View est une vue partagée à toutes les perspec- tives d’une application Eclipse † Une Sticky View n’est pas associée explicitement à une perspective, la déclaration est implicite † Lors du chargement d’une perspective, une Sticky View déjà ouverte le reste et conserve sa position † Un « reset » sur une perspective ne fermera pas une Sticky View † La fermeture d’une Sticky View ne peut se faire que par une demande explicite (fermeture de la vue associée) † Pour quelle occasion ? † Cheat Sheets (aide contextualisée)
  19. 19 Views - M. Baron - Page mickael-baron.fr mickaelbaron Sticky

    View † Création d’une vue classique plugin.xml du projet ViewExample Onglet Extensions
  20. 20 Views - M. Baron - Page mickael-baron.fr mickaelbaron Sticky

    View † Création d’une vue classique (suite) package eclipse.workbench.viewexample.views public class StickyViewPart extends ViewPart { public StickyViewPart() { } public void createPartControl(Composite parent) { Label myCurrentLabel = new Label(parent, SWT.NONE); myCurrentLabel.setText("View used for the StickyView ViewExample series"); } public void setFocus() { } } StickyViewPart.java du projet ViewExample plugin.xml du projet ViewExample
  21. 21 Views - M. Baron - Page mickael-baron.fr mickaelbaron Sticky

    View † Création d’une Sticky View Onglet Extensions plugin.xml du projet ViewExample
  22. 22 Views - M. Baron - Page mickael-baron.fr mickaelbaron Sticky

    View † Association d’une vue avec une Sticky View Vue qui sera transformée en Sticky View Identifiant d’une vue existante Position de la Sticky View dans les perspectives (BOTTOM, RIGHT, LEFT, TOP) Précise si la Sticky View peut elle être fermée ? La Sticky View peut elle être déplacée plugin.xml du projet ViewExample
  23. 23 Views - M. Baron - Page mickael-baron.fr mickaelbaron Sticky

    View † Exemple : une vue transformée en Sticky View La Sticky View est visible et conserve son emplacement lors des changements des perspectives Projet ViewExample
  24. 24 Views - M. Baron - Page mickael-baron.fr mickaelbaron Category

    † Une Category est un regroupement de vues utilisée pour organiser la liste des vues dans le menu Window -> Show View -> Other † Possibilité d’associer une vue à une nouvelle catégorie ou à une catégorie déjà existante † Les catégories sont hiérarchiques, possibilité de spécifier à une catégorie une catégorie parente existante † Les catégories sont identifiées par un identifiant unique Plusieurs catégories existantes : General, Debug, Help, Java, …
  25. 25 Views - M. Baron - Page mickael-baron.fr mickaelbaron Category

    † Création d’une Category plugin.xml du projet ViewExample Création d’une nouvelle catégorie Identifiant de la nouvelle catégorie Nom de la nouvelle catégorie Parent de la catégorie (optionnelle) Onglet Extensions
  26. 26 Views - M. Baron - Page mickael-baron.fr mickaelbaron Category

    † Associer la vue à la catégorie plugin.xml du projet ViewExample Onglet Extensions La vue ViewCategory est associée à la catégorie *.ViewExample.Category La liste des vues est accessible via le menu Window -> Show View -> Other La vue ViewCategory est contenu dans la catégorie Category Example
  27. 27 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vue

    Détachée † Une vue détachée est une vue affichée dans une fenêtre autre que celle de la WorkbenchWindow † Une vue détachée appartient toujours à sa perspective † Pour détacher une vue, afficher le menu contextuel (bouton droit) puis option Detached † Au niveau des APIs de manipulation des vues détachées c’est le grand vide … † Savoir si une vue est détachée ? † Pas d’écouteur pour savoir si une vue est détachée † Solution : titre de la fenêtre est vide, quand la vue est détachée les événements de redimensionnement sont déclenchés † Détacher une vue † Utilisation des APIs internes
  28. 28 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vue

    Détachée † Exemple : Détecter qu’une vue est détachée La vue Detach View est détachée
  29. 29 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vue

    Détachée † Exemple (suite) : Détecter qu’une vue est détachée public class DetachViewPart extends ViewPart { private Composite parent; private Label label; protected boolean isDetached; public DetachViewPart() { } @Override public void createPartControl(Composite parent) { this.parent = parent; parent.setLayout(new RowLayout()); label = new Label(parent, SWT.NONE); parent.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { updateDetached(); } }); updateDetached(); } private void updateDetached() { isDetached = parent.getShell().getText().length() == 0; label.setText("View isDetached? " + isDetached); } ... } DetachViewPart.java du Projet ViewExample
  30. 30 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vue

    Détachée † Exemple : Attacher et Détacher une vue par code Attache ou Détache une vue selon son état Détaché ou pas
  31. 31 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vue

    Détachée † Exemple (suite) : Attacher et Détacher une vue par code public class OpenDetachViewPart extends DetachViewPart { public OpenDetachViewPart() { } @Override public void createPartControl(Composite parent) { super.createPartControl(parent); Button detachViewButton = new Button(parent, SWT.NONE); detachViewButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkbenchPage page = getSite().getPage(); final IViewReference findViewReference = page.findViewReference( "eclipse.workbench.ViewExample.openDetachViewId"); if (!isDetached) { ((WorkbenchPage) page).detachView(findViewReference); } else { ((WorkbenchPage) page).attachView(findViewReference); } } }); detachViewButton.setText("Attach/Detach"); } ... } AttachOrDetachViewPart.java du Projet ViewExample Réutilise la vue DetachViewPart pour connaître l’état de la vue
  32. 32 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples † Jusque là nous avons vu qu’une vue est identifiée par un identifiant unique définie lors de la création d’une extension (attribut id) † Le conteneur de la plateforme Eclipse s’appuie sur cet identifiant pour la création de l’instance de la vue † Par conséquent, une seule instance de la vue ne peut être créée par le conteneur de la plateforme Eclipse † Or, il peut être intéressant d’afficher plusieurs instances d’une même vue † Un second identifiant est utilisé pour la création d’instances multiples
  33. 33 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples † Une vue est donc identifiée par un identifiant primaire et un identifiant secondaire (par défaut est vide) † Une instance d’une vue est identifiée de la manière suivante † Pour activer la gestion des instances multiples d’une vue, il faut modifier le paramètre allowMultiple à true † Différentes manières de construire une instance multiple d’une vue Identifiant_Primaire:Identifiant_Secondaire Identifiant primaire déclaré lors de la définition de la vue Identifiant secondaire déclaré pour chaque nouvelle instance de la vue
  34. 34 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples Identifiant primaire de la vue Multiple Instances View 1 Ne pas ajouter un identifiant secondaire lors de la définition de la vue † Activation de la gestion multiple d’une vue La gestion multiple de cette vue est activée
  35. 35 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples † Exemple 1 : Vues multiples à partir de IPageLayout package eclipse.workbench.viewexample.views public class MultipleInstancesViewFactory implements IPerspectiveFactory { public void createInitialLayout(IPageLayout layout) { String editorAreaId = layout.getEditorArea(); layout.addView("eclipse.workbench.ViewExample.MultipleInstanceView1:1", IPageLayout.LEFT, 0.25f, editorAreaId); layout.addView("eclipse.workbench.ViewExample.MultipleInstanceView1:2", IPageLayout.BOTTOM, 0.25f, editorAreaId); } } MultipleInstancesViewFactory.java du projet ViewExample Création de deux instances de la même vue Création statique d’instances multiples de la vue
  36. 36 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples MultipleInstancesViewFactory du projet ViewExample La vue Multiple Instances View 1 est instanciée deux fois † Exemple 1 (suite) : Vues multiples à partir de IPageLayout
  37. 37 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples † Exemple 2 : Vues multiples à partir de IWorkbenchPage package eclipse.workbench.viewexample.views public class AddSecondaryViewAction implements IWorkbenchWindowActionDelegate { private int secondaryIdCount = 0; public AddSecondaryViewAction() { } public void run(IAction action) { try { PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView( "eclipse.workbench.ViewExample.MultipleInstanceView2", Integer.toString(secondaryIdCount), IWorkbenchPage.VIEW_ACTIVATE); secondaryIdCount++; } catch (PartInitException e) { e.printStackTrace(); } } public void selectionChanged(IAction action, ISelection selection) { } public void dispose() { } public void init(IWorkbenchWindow window) { } } AddSecondaryViewAction.java du projet ViewExample La méthode showView permet d’ajouter une vue en donnant un identifiant secondaire Création dynamique d’instances multiples de la vue
  38. 38 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples † Exemple 2 (suite) : Vues multiples à partir de IWorkbench… AddSecondaryViewAction du projet ViewExample Action permettant la création de nouvelles instances de vue Plusieurs instances de vue
  39. 39 Views - M. Baron - Page mickael-baron.fr mickaelbaron †

    La création d’une vue via la méthode showView(…) varie selon le mode de fonctionnement utilisé † IViewPart showView(String id, String secId, int mode) throws PartInitException : crée une vue à partir d’un identifiant primaire id, d’un identifiant secondaire secId et d’un mode † Différentes valeurs pour mode † VIEW_ACTIVATE : vue est visible et a le focus † VIEW_VISIBLE : vue visible mais n’a pas le focus † VIEW_CREATE : vue créée mais n’a pas forcément le focus Vues multiples
  40. 40 Views - M. Baron - Page mickael-baron.fr mickaelbaron Vues

    multiples † A partir du code d’une ViewPart il est possible de connaître l’identifiant secondaire au travers du site de la vue via l’interface IViewSite † String getSecondaryId() : identifiant secondaire utilisé pour gérer des instances multiples de la vue † Exemple : afficher l’identifiant secondaire d’une vue package eclipse.workbench.viewexample.views public class MultipleInstanceViewPart2 extends ViewPart { public MultipleInstanceViewPart2() { } public void createPartControl(Composite parent) { Button myButton = new Button(parent, SWT.FLAT); myButton.setText("Give me the Secondary ID"); myButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { String secondaryId = getViewSite().getSecondaryId(); System.out.println(secondaryId); } }); } public void setFocus() { } } MultipleInstanceViewPart2.java du projet ViewExample Identifiant secondaire
  41. 41 Views - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des vues † Le registre des vues est utilisé par le conteneur Eclipse pour stocker l’intégralité des vues créées (incluant également les Sticky Views et les Categories) † Il n’existe qu’un seul registre de vue géré par le Workbench † A partir de ce registre il est possible de † Chercher une vue par son identifiant † Récupérer l’ensemble des Categories, des Sticky Views ou des Views IViewRegistry viewRegistry = PlatformUI.getWorkbench().getViewRegistry(); L’interface IViewRegistry est détaillée dans la suite
  42. 42 Views - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des vues † L’interface IViewRegistry dispose des méthodes suivantes † IViewDescriptor find(String id) : recherche une vue par son id † IViewCategory[] getCategories() : récupère l’intégralité des catégories † IStickyViewDescriptor[] getStickyViews() : retourne l’intégralité des Sticky Views † IViewDescriptor[] getViews() : retourne l’intégralité des vues issues du registre des vues † L’interface IViewCategory représente une catégorie de vues † String getId() : id de la catégorie † String getLabel() : nom de la catégorie † IPath getPath() : chemin de la catégorie (parent) † IViewDescriptor getViews() : liste des vues
  43. 43 Views - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des vues † L’interface IViewDescriptor décrit le contenu d’une vue † String getId() : identifiant de la vue † String getLabel() : nom † String getDescription() : description † float getFastViewWithRatio() : ratio † boolean getAllowMultiple() : instances multiples ou pas † String getCategoryPath() : chemin des catégories † isRestorable() : vue reconstruite au redémarrage de l’application † IViewPart createView() : création d’une instance (peu utilisé car réalisé par le conteneur) † Le registre des vues ne gère pas les instances IViewPart, elles le sont au niveau de la page
  44. 44 Views - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des vues † L’interface IStickyViewDescriptor représente une Sticky View † String getId() : identifiant de la vue † int getLocation() : position de la vue dans la WorkbenchWindow † boolean isCloseable() : la vue peut-elle être fermée † isMoveable() : la vue peut-elle être déplacée
  45. 45 Views - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des vues † Exemple : interroger le registre des vues package eclipse.workbench.viewexample.views public class ViewRegistryViewPart extends ViewPart { public void createPartControl(Composite parent) { ... Button findView = new Button(parent, SWT.FLAT); findView.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("- Find View -"); IViewRegistry viewRegistry = PlatformUI.getWorkbench().getViewRegistry(); ViewDescriptor find = viewRegistry.find("eclipse.workbench.ViewExample.views.ViewRegistryId"); System.out.println(find.getLabel()); System.out.println(find.getAllowMultiple()); } }); findView.setText("Find : ViewRegistryId"); Button getCategories = new Button(parent, SWT.FLAT); getCategories.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("- Get Categories -"); IViewRegistry viewRegistry = PlatformUI.getWorkbench().getViewRegistry(); IViewCategory[] categories = viewRegistry.getCategories(); for (IViewCategory viewCategory : categories) { System.out.println(viewCategory.getLabel()); } } }); getCategories.setText("Get Categories"); ... } } ViewRegistryViewPart.java du projet ViewExample
  46. 46 Views - M. Baron - Page mickael-baron.fr mickaelbaron Registre

    des vues † Exemple (suite) : interroger le registre des vues ... Button getStickyViews = new Button(parent, SWT.FLAT); getStickyViews.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("- Get Sticky Views -"); IViewRegistry viewRegistry = PlatformUI.getWorkbench().getViewRegistry(); IStickyViewDescriptor[] stickyViews = viewRegistry.getStickyViews(); for (IStickyViewDescriptor stickyViewDescriptor : stickyViews) { System.out.println(stickyViewDescriptor.getId() + stickyViewDescriptor.getLocation()); } } }); getStickyViews.setText("Get Sticky Views"); Button getViews = new Button(parent, SWT.FLAT); getViews.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { System.out.println("- Get Views -"); IViewRegistry viewRegistry = PlatformUI.getWorkbench().getViewRegistry(); IViewDescriptor[] views = viewRegistry.getViews(); for (IViewDescriptor viewDescriptor : views) { System.out.println(viewDescriptor.getId()); } } }); getViews.setText("Get Views"); } } ViewRegistryViewPart.java du projet ViewExample
  47. 47 Views - 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 ViewPart (celle utilisée pour programmer l’interface d’une vue) † Pour rappel l’agencement des objets ViewPart d’une page est réalisé par les perspectives † L’instance d’une ViewPart est associée à son identifiant (id) † Il peut exister plusieurs instances d’une ViewPart dans le cas ou dans la description de la vue, la gestion des instances multiples est activée (id:idSecondaire)
  48. 48 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie † L’interface IViewReference décrit la relation entre identifiant et instance de ViewPart † String getId() : identifiant primaire de la vue † String getSecondaryId() : identifiant secondaire de la vue † IViewPart getView(boolean restore) : référence à la ViewPart, restore à TRUE pour tenter de la restaurer si elle n’existe pas † boolean isFastView() : cette vue est de type FastView † IWorkbenchPage getPage() : Workbench Page dans laquelle cette vue est référencée † String getPartName() : le nom à afficher † … † L’accès à un objet IViewReference se fait par l’intermédiaire du Workbench Page actif
  49. 49 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – ouverture d’une vue † Ouverture d’une vue procède à la création d’une instance ViewPart dans le cas où il n’existe aucune instance de cette ViewPart dans le Workbench Page actif † L’ouverture d’une vue déjà présente dans une perspective rend active cette vue. Aucune nouvelle instance est créée † L’ouverture d’une vue dans une autre perspective affiche la vue sans re-créér une nouvelle instance (les ViewPart sont partagées dans les perspectives) † Dans le cas des vues avec instances multiples, la stratégie d’ouverture est identique sauf qu’il peut exister plusieurs instances d’une même ViewPart
  50. 50 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – ouverture d’une vue Ouverture de la vue SimpleView dans la perspective Resource Création d’une instance de la classe SimpleView.java du projet ViewExample stockée dans la page courante La vue SimpleView n’a jamais encore été ouverte † Démonstration …
  51. 51 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – ouverture d’une vue La vue SimpleView est déjà ouverte dans la perspective Resource La vue SimpleView est ajoutée à la perspective SimplePerspective Comme une instance à SimpleView existe, aucune autre instance n’est créée † Démonstration (suite) …
  52. 52 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – ouverture d’une vue † Lors de l’analyse de la classe ViewPart seules les méthodes createPartControl(…) et setFocus() avaient été étudiées † D’autres méthodes sont fournies et appelées par le conte- neur (suivant un ordre donné) pour instancier la ViewPart † Constructeur de la classe † void setInitializationData(IConfigurationElement, String, Object) : les informations de l’extension sont passées en paramètre permettant de connaître le titre, le nom, l’image, … † void init(IViewSite) : initialise l’instance avec le site † void init(IViewSite, IMemento) : idem sauf que l’objet IMemento permet de restaurer un état (voir fermeture d’une vue) † void createPartControl(Composite) : pour programmer l’IHM † Méthodes appelées uniquement à la création de l’instance 1 2 3 4 5
  53. 53 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – ouverture d’une vue † Exemple : accès aux informations de l’extension d’une vue public class OpenLifeCycle extends ViewPart { public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) { super.setInitializationData(cfig, propertyName, data); System.out.println(cfig.getAttribute("name")); } public OpenLifeCycle() { } public void createPartControl(Composite parent) { } public void setFocus() { } } OpenLifeCycle du projet ViewExample Récupération des informations de l’extension utilisée par la création de cette vue
  54. 54 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – ouverture d’une vue † A l’ouverture, la vue passe par différents états † Opened : lors de la création de l’instance d’une ViewPart † Visible : la vue est visible † Activated : la vue a le focus La vue SimpleView a été ajoutée par le menu Show View Les différents états de la vue : Opened -> Visible -> Activated
  55. 55 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – fermeture d’une vue † Lors de la fermeture d’une vue dans une perspective le Workbench Page vérifie si cette vue est également présente dans d’autres perspectives † Présente : la vue est cachée † Non présente : la référence ViewPart est disposée † Quand une référence ViewPart est disposée, elle est suppri- mée de la liste des IViewReference du Workbench Page † Une nouvelle référence doit être créée pour ré-ouvrir la vue
  56. 56 Views - M. Baron - Page mickael-baron.fr mickaelbaron Cycle

    de vie – fermeture d’une vue † D’autres méthodes sont appelées par le conteneur (suivant un ordre donné) lors de la fermeture d’une vue † void saveState(IMemento memento) : appelée uniquement quand l’application Eclipse doit être fermée. Cette méthode n’est pas appelée s’il n’existe pas de vue ouverte (référence n’existe pas) † void dispose() : suppression de la référence quand la vue doit être disposée † A la fermeture, la vue passe par différents états † Deactivated : la vue n’a plus le focus † Hidden : la vue est cachée † Closed : l’instance de la vue est disposée
  57. 57 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues † Dans tous les exemples que nous avons étudiés nous n’avons encore jamais échangé d’information entre vues † Informations à partager † Des attributs d’une classe ViewPart † Sélection courante d’une TableViewer † Et pourtant, la plateforme Eclipse fournit un ensemble de mécanismes pour faciliter la communication entre vues † Nous étudions dans cette partie ces mécanismes † Communication directe entre des ViewParts † Communication à partir des écouteurs de vues † Service de sélection † Le patron Adaptateur via IAdaptable
  58. 58 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (en direct …) † Le premier mécanisme consiste à récupérer l’instance d’une ViewPart à partir de l’identifiant de sa vue † Une classe ViewPart spécialisée peut éventuellement disposer d’attributs ou d’un modèle de données à partager † Le registre des vues ne permet pas de récupérer des instances de ViewPart puisque ces instances sont gérées au niveau de la page (voir cycle de vie) † Il faut passer par la page active pour récupérer cette instance de ViewPart en fonction de l’identifiant de la vue IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IViewPart viewPart = page.findViewReference(String id).getView(true); Récupération de la page active Récupération de l’instance ViewPart d’une vue en fonction de son identifiant par rapport à la page active
  59. 59 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (en direct …) † Exemple : communication entre deux instances ViewPart package eclipse.workbench.linkviewexample.views public class LinkView2 extends ViewPart { private String familyName; private String firstName; public LinkView2() { } public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(2, false)); Label nameLabel = new Label(parent, SWT.NONE); nameLabel.setText("Family Name"); Label fnameLabel = new Label(parent, SWT.NONE); fnameLabel.setText("First Name"); final Text nameText = new Text(parent, SWT.BORDER); nameText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { familyName = nameText.getText(); } }); final Text fnameText = new Text(parent, SWT.BORDER); fnameText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { firstName = fnameText.getText(); } }); } ... } LinkView2.java du projet LinkViewExample Attribut spécifique à la classe LinkView2.java Perspective Link Direct
  60. 60 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (en direct …) † Exemple (suite) : communication entre deux instances … package eclipse.workbench.linkviewexample.views public class LinkView1 extends ViewPart { public void createPartControl(Composite parent) { Button getLinkView2DataButton = new Button(parent, SWT.FLAT); getLinkView2DataButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IViewPart findView = activePage.findViewReference("eclipse.workbench.LinkViewExample.views.linkviewid2"); if (findView != null) { String familyName = ((LinkView2)findView).getFamilyName(); String firstName = ((LinkView2)findView).getFirstName(); System.out.println(familyName + " " + firstName); } else { System.out.println("Instance de vue non créée"); } } }); getLinkView2DataButton.setText("Retrieve information from LinkView2"); } ... } LinkView1.java du projet LinkViewExample
  61. 61 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (en direct …) † Comme les instances ViewPart sont gérées au niveau de la page, il se peut que des instances d’une vue ne soient pas encore créées † Par conséquent il existe un couplage fort entre ViewPart † Le scénario suivant peut se produire † Une vue identifiée par id1 (dans le contenu de sa ViewPart) souhaite accéder à une instance ViewPart d’une vue identifiée par id2 † Si la vue id2 n’a pas été créée (par exemple par encore visible dans une perspective) l’instance de sa ViewPart sera null † La vue id1 ne pourra pas accéder aux informations de la ViewPart de id2 tant que la création n’a pas été réalisée † Le problème se complexifie si les vues gère les instances multiples
  62. 62 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (en direct …) † Exemple : communication entre deux instances ViewPart package eclipse.workbench.linkviewexample.views public class MultipleLinkView extends ViewPart { public void createPartControl(Composite parent) { Button getLinkView2DataButton = new Button(parent, SWT.FLAT); getLinkView2DataButton.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IViewPart findView = activePage.findViewReference("eclipse.workbench.LinkViewExample.views.linkviewid2","1") .getView(true); if (findView != null) { String familyName = ((LinkView2)findView).getFamilyName(); String firstName = ((LinkView2)findView).getFirstName(); System.out.println(familyName + " " + firstName); } else { System.out.println("Instance de vue non créée"); } } }); getLinkView2DataButton.setText("Retrieve information from LinkView2"); } ... } MultipleLinkView.java du projet LinkViewExample Récupération de l’instance d’une ViewPart par rapport à l’identifiant primaire et secondaire
  63. 63 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Part Listeners) † Une vue peut subir des modifications comme l’activation, l’ouverture ou la visibilité et permet d’effectuer des changements à la réponse des événements † L’activation et la création des vues peuvent être écoutées 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
  64. 64 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Part Listeners) † IPartListener2 fournit les méthodes suivantes pour les réponses aux événements sur la vue ( † 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 † Malheureusement certaines méthodes sont manquantes comme : vue détachée, vue transformée en Fast View, vue dockée …
  65. 65 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Part Listeners) † Exemple : écouter les changements d’une ViewPart package eclipse.workbench.ipartserviceexample.views public class PartListenerViewPart extends ViewPart { public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(1, false)); Label myLabel = new Label(parent, SWT.NONE); myLabel.setText("Listened View"); IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); activePage.addPartListener(new IPartListener2() { @Override public void partActivated(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (PartListenerViewPart.this == part) { System.out.println(".partActivated()"); } } @Override public void partBroughtToTop(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (PartListenerViewPart.this == part) { System.out.println(".partBroughtToTop()"); } } @Override public void partClosed(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (PartListenerViewPart.this == part) { System.out.println(".partClosed()"); } } } ... } PartListenerViewPart.java du projet IPartServiceExample Liste des méthodes déclenchées lors de l’écoute de la ViewPart
  66. 66 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Le service de sélection permet à des vues d’avertir la plate- forme Eclipse qu’une sélection sur des objets a été réalisée † Exemple : vues Package Explorer et Properties Le changement de sélection d’un fichier provoque la modification de la vue Properties
  67. 67 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Toutes modifications de sélection réalisées par les fournis- seurs (Provider) sont transmises aux écouteurs (Listener) † Le service de sélection est unique pour chaque Workbench Window † Il fonctionne à la manière d’un Bus où tout fournisseur peut se connecter et où tout écouteur peut s’abonner Instance unique par Workbench Window Des fournisseurs existants de la plateforme Des écouteurs existants de la plateforme Des fournisseurs spécifiques
  68. 68 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † L’écouteur reçoit des informations liées à la sélection † L’origine de la sélection (le fournisseur) † Les informations de la sélection (les objets) † Les informations de la sélection sont de deux natures † Une liste d’objets (une ligne d’un composant TableViewer) † Une zone de texte (un bloc de texte d’un éditeur) † La structure de données est définie par l’interface ISelection (vue dans la partie Composants Additionnels avec Java)
  69. 69 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Un fournisseur doit implémenter l’interface ISelectionProvider † Tous les composants Viewer de JFace sont des fournisseurs de sélection † void addSelectionChangedListener(ISelectionChangedListener) : ajout d’un écouteur sur la sélection courante † ISelection getSelection() : récupère la sélection courante † void setSelection(ISelection) : modifie la sélection courante † Toutefois, cette sélection est locale au composant Viewer † Pour la diffuser au service de sélection il est nécessaire de connecter un objet ISelectionProvider au Workbench getSite().setSelectionProvider(tableViewer); Site de la ViewPart contenant le Viewer Objet ISelectionProvider à connecter au service de sélection
  70. 70 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Différents types de données de sélections en fonction du type de composant Viewer † ComboViewer † ListViewer † TreeViewer † CheckboxTreeViewer † TableViewer † CheckboxTableViewer † TextViewer † SourceViewer † ProjectionViewer IStructuredSelection IStructuredSelection IStructuredSelection, ITreeSelection IStructuredSelection, ITreeSelection IStructuredSelection IStructuredSelection ITextSelection, IMarkSelection ITextSelection, IMarkSelection ITextSelection, IMarkSelection
  71. 71 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Chaque Workbench Window fournit un service de sélection de type ISelectionService † Le service de sélection fournit les méthodes suivantes † ISelection getSelection() : récupère la dernière sélection active † ISelection getSelection(String partId) : récupère la sélection de la ViewPart définie par partId † void addSelectionListener(ISelectionListener listener) : ajoute un écouteur sur les changements de sélection (voir transparent suivant) † void addSelectionListener(String partId, ISelectionListener listener) : ajoute un écouteur sur les changements de la sélection d’une vue † void addPostSelectionListener(ISelectionListener listener) : ajoute un écouteur sur des changements finaux † Exemple ListViewer : l’écouteur ne sera averti qu’après un certain délai de manière à éviter les sélections multiples ISelectionService selService = getSite().getWorkbenchWindow().getSelectionService()
  72. 72 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † L’interface ISelectionListener fournit une seule méthode † public void selectionChanged(IWorkbenchPart part, ISelection s) : déclenchée lors du changement de la sélection courante † L’interface INullSelectionListener (étend ISelectionListener) ne fournit aucune méthode (Interface de marquage), elle est utilisée dans les cas d’une non sélection † Activation d’une ViewPart non connectée au service de sélection † Toutes les vues et éditeurs du Workbench Window sont fermées
  73. 73 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Exemple : 1 vue productrice, 1 vue consommatrice package eclipse.workbench.linkviewexample.views public class ProviderViewPart extends ViewPart { private TableViewer viewer; public void createPartControl(Composite parent) { viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer.setContentProvider(new ViewContentProvider()); viewer.setLabelProvider(new ViewLabelProvider()); viewer.setInput(getViewSite()); this.getSite().setSelectionProvider(viewer); } ... } ProviderViewPart.java du projet LinkViewExample Connexion du SelectionProvider (viewer) au service de sélection
  74. 74 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Exemple (suite) : 1 vue productrice, 1 vue consommatrice package eclipse.workbench.linkviewexample.views public class ListenerViewPart extends ViewPart { private Label listenerLabel; public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(1, false)); listenerLabel = new Label(parent, SWT.NONE); this.getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(" eclipse.workbench.LinkViewExample.views.ProviderViewPartId", new ISelectionListener() { public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (selection == null) { return; } if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)selection; Object firstElement = structuredSelection.getFirstElement(); if (firstElement != null) { listenerLabel.setText(firstElement.toString()); listenerLabel.getParent().pack(); } } } }); } ... } ListenerViewPart.java du projet LinkViewExample Ecouteur sur la sélection de la vue ProviderViewPartId Utile pour redimensionner le parent Récupération du service de sélection du Workbench Window
  75. 75 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Exemple : plusieurs TableViewers d’une ViewPart connectés au service de sélection Une vue contient deux composants TableViewer Une vue utilisée pour écouter les changements de sélection de la vue Multiple Providers Utilisation de la classe SelectionProviderIntermediate permettant de déléguer un SelectionProvider d’une ViewPart au service de sélection www.eclipse.org/articles/Article-WorkbenchSelections/article.html
  76. 76 Views - M. Baron - Page mickael-baron.fr mickaelbaron †

    Exemple (suite) : plusieurs TableViewers d’une ViewPart connectés au service de sélection Communication entre vues (Selection Service) package eclipse.workbench.multipleprovidersexample public class MultipleProvidersViewPart extends ViewPart { private Label listenerLabel; public void createPartControl(Composite parent) { final SelectionProviderIntermediate selectionProvider = new SelectionProviderIntermediate(); viewer1 = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer1.setContentProvider(new ViewContentProvider()); viewer1.setLabelProvider(new ViewLabelProvider()); viewer1.setInput(getViewSite()); viewer2 = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); viewer2.setContentProvider(new ViewContentProvider()); viewer2.setLabelProvider(new ViewLabelProvider()); viewer2.setInput(getViewSite()); this.getSite().setSelectionProvider(selectionProvider); ... } ... } MultipleProvidersViewPart.java du projet MultipleProvidersExample SelectionProvider « maison » utilisé pour connecter dynamiquement le Viewer de la ViewPart au service de sélection Le reste du code est présenté dans la suite
  77. 77 Views - M. Baron - Page mickael-baron.fr mickaelbaron †

    Exemple (suite) : plusieurs TableViewers d’une ViewPart connectés au service de sélection Communication entre vues (Selection Service) package eclipse.workbench.multipleprovidersexample public class SelectionProviderIntermediate implements IPostSelectionProvider { private final ListenerList selectionListeners = new ListenerList(); private final ListenerList postSelectionListeners = new ListenerList(); private ISelectionProvider delegate; public void setSelectionProviderDelegate(ISelectionProvider newDelegate) { if (delegate == newDelegate) { return; } if (delegate != null) { delegate.removeSelectionChangedListener(selectionListener); if (delegate instanceof IPostSelectionProvider) { ((IPostSelectionProvider)delegate).removePostSelectionChangedListener(postSelectionListener); } } delegate = newDelegate; if (newDelegate != null) { newDelegate.addSelectionChangedListener(selectionListener); if (newDelegate instanceof IPostSelectionProvider) { ((IPostSelectionProvider)newDelegate).addPostSelectionChangedListener(postSelectionListener); } fireSelectionChanged(newDelegate.getSelection()); firePostSelectionChanged(newDelegate.getSelection()); } } ... } SelectionProviderIntermediate.java du projet MultipleProvidersExample inspirée de www.eclipse.org/articles/Article-WorkbenchSelections/article.html Chaque SelectionProvider délégué est écouté
  78. 78 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (Selection Service) † Exemple (suite) : plusieurs TableViewers d’une ViewPart connectés au service de sélection package eclipse.workbench.multipleprovidersexample public class MultipleProvidersViewPart extends ViewPart { private Label listenerLabel; public void createPartControl(Composite parent) { ... // Voir précédent transparent viewer1.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { selectionProvider.setSelectionProviderDelegate(viewer1); } }); viewer2.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { selectionProvider.setSelectionProviderDelegate(viewer2); } }); ... } ... } Suite de MultipleProvidersViewPart.java du projet MultipleProvidersExample A chaque changement de sélection d’un TableViewer modification du SelectionProvider via la classe de délégation
  79. 79 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Précédemment nous avons étudié le service de sélection qui permet de récupérer la sélection courante ISelection † Pour rappel un objet de type ISelection « véhicule » l’objet modèle d’un composant viewer † Un écouteur du service de sélection (généralement une vue) doit interpréter l’objet véhiculé pour le traduire en un objet exploitable † Inconvénients : couplage fort † L’écouteur (la vue) doit connaître le type de l’objet véhiculé † Nécessite une adaptation de l’écouteur à chaque nouveau type d’objet véhiculé † Dans certains, il n’est pas possible de modifier le code de l’écouteur car il peut s’agir d’une vue de la plateforme : vue Properties
  80. 80 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) La vue Properties n’écoutent que les objets de type IPropertySource La vue Package Explorer manipule des objets d’un certains type La vue Multiple Providers fournit des objets de type String Service Sélection Les objets de la vue Package Explorer sont adaptés en type IPropertySource Les objets de la vue Multiple Providers ne sont pas consommés car la vue Properties ne traite que les objets de type IPropertySource Objets consommés par la vue Properties
  81. 81 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † L’écouteur ne doit manipuler que des objets dont il a connaissance † Exemple : la vue Properties ne peut manipuler que des objets de type IPropertySource † La transformation des objets du producteur en objet écouteur utilise la patron de conception : adaptateur † Nous allons montrer dans la suite comment exploiter le service de sélection de manière à réduire le couplage entre l’écouteur et le producteur † Pour ce faire trois approches seront étudiées appliqué à un exemple commun
  82. 82 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Exemple : carnet d’adresse avec informations de création et de mise à jour Un composant ListViewer affiche la liste des noms du carnet d’adresse La vue Properties affiche des informations complémentaires lorsque un contact est sélectionné
  83. 83 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Exemple (suite) : carnet d’adresse avec informations de création et de mise à jour public class ContactView extends ViewPart { public void createPartControl(Composite parent) { ... final ListViewer contactsList = new ListViewer(container, SWT.BORDER); contactsList.setLabelProvider(new ListLabelProvider()); contactsList.setContentProvider(new ContentProvider()); contactsList.setInput(Activator.getDefault().getContactManager()); getViewSite().setSelectionProvider(contactsList); } class ContentProvider implements IStructuredContentProvider { public Object[] getElements(Object inputElement) { Manager contactManager = (Manager) inputElement; return contactManager.contacts(); } ... } class ListLabelProvider extends LabelProvider { public String getText(Object element) { Contact contact = (Contact) element; return contact.getName(); } ... } } Vue définie par ContactView.java commune aux trois approches
  84. 84 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Exemple (suite) : carnet d’adresse avec informations de création et de mise à jour public class ContactManager { List<Contact> contacts; public ContactManager() { this.contacts = new ArrayList<Contact>(); Contact contact = new Contact("Mickael BARON", "1 rue des Eclipses"); contact.setProperty("Created", "2008/04/05 11:44"); contact.setProperty("Updated", "2008/04/05 11:53"); this.contacts.add(contact); contact = new Contact("Tom STORY", "11 rue du Pixar"); contact.setProperty("Created", "2008/04/05 15:21"); contact.setProperty("Updated", "2008/04/07 09:48"); this.contacts.add(contact); contact = new Contact("Sarah PRINCESSE", "32 rue des Goonies"); contact.setProperty("Created", "2008/04/06 10:34"); this.contacts.add(contact); } public Contact[] contacts() { return (Contact[])this.contacts.toArray(new Contact[0]); } } Fabrique des contacts définie par ContactManager.java commune aux trois approches
  85. 85 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Exemple (suite) : carnet d’adresse avec informations de création et de mise à jour public class Contact { private String address; private String name; private Map<String, Object> properties; public Contact(String name, String address) { this.name = name; this.address = address; } public String getAddress() { return this.address; } public String getName() { return this.name; } public synchronized Map<String, Object> getProperties() { if (this.properties == null) { return Collections.emptyMap(); } return new HashMap<String, Object>(this.properties); } public synchronized void setProperty(String key, Serializable value) { if (this.properties == null) { this.properties = new HashMap<String, Object>(); } this.properties.put(key, value); } ... // Le reste est spécifique à chaque approche, à voir dans la suite } Classe Contact.java qui décrit l’objet envoyé lors d’une sélection
  86. 86 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † La première approche consiste à typer l’objet du producteur du type de l’objet manipulé par l’écouteur † Avantage † Très simple à mettre en œuvre † Inconvénients † Le producteur connaît le type de l’objet du producteur † Si plusieurs écouteurs avec des types différents nécessite plusieurs implémentations (à condition que l’écouteur fournit une interface) † Si nouvelle adaptation, nécessite de modifier le code du producteur
  87. 87 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 1 : typer l’objet du producteur du type de l’objet manipulé par l’écouteur package eclipse.workbench.iadaptableexample1; public class Contact implements IPropertySource { ... // Voir code commun public Object getEditableValue() { return null; } public IPropertyDescriptor[] getPropertyDescriptors() { List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>(); Map<String, Object> properties = this.getProperties(); for (String key : properties.keySet()) { PropertyDescriptor descriptor = new PropertyDescriptor(key, key); descriptor.setAlwaysIncompatible(true); descriptors.add(descriptor); } return descriptors.toArray(new IPropertyDescriptor[0]); } public Object getPropertyValue(Object id) { Map<String, Object> properties = this.getProperties(); return properties.get(id); } public boolean isPropertySet(Object id) { return false; } public void resetPropertyValue(Object id) { } public void setPropertyValue(Object id, Object value) { } } Classe Contact.java du projet IAdaptableExample1 La vue Properties ne consomme que des objets de type IPropertySource
  88. 88 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † La première approche consiste à typer l’objet du producteur du type de l’objet manipulé par l’écouteur † Avantage † Très simple à mettre en œuvre † Inconvénients † Le producteur connaît le type de l’objet du producteur † Si plusieurs écouteurs avec des types différents nécessite plusieurs implémentations (à condition que l’écouteur fournit une interface) † Si nouvelle adaptation, nécessite de modifier le code du producteur
  89. 89 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † La deuxième approche consiste à implémenter l’interface IAdaptable † Object getAdapter(Class adapter) : une adaptation est demandée du type défini par adapter. Le retour est un objet adapté du type du producteur vers le type adapter (écouteur) † Démarche de développement : en fonction du type du paramètre adapter déléguer l’adaptation à un objet de type adapter † Avantage † La gestion de plusieurs types objets est facilitée car il ne s’agit plus d’héritage mais de délégation † Inconvénients † Le producteur connaît le type de l’objet du producteur † Si nouvelle adaptation, nécessite de modifier le code du producteur
  90. 90 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 2 : utilisation de IAdaptable avec couplage avec le type de l’écouteur package eclipse.workbench.iadaptableexample2; public class Contact implements IAdaptable { ... // Voir code commun public Object getAdapter(Class adapter) { if (adapter == IPropertySource.class) { return new ContactPropertySourceAdapter(this); } else { return null; } } } Classe Contact.java du projet IAdaptableExample2 La vue Properties exploite le patron de conception adaptateur pour adapter un objet Contact en IPropertySource La classe ContactPropertySourceAdapter est de type IPropertySource
  91. 91 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 2 (suite) : utilisation de IAdaptable avec couplage avec le type de l’écouteur package eclipse.workbench.iadaptableexample2; public class ContactPropertySourceAdapter implements IPropertySource { private Contact contact; public ContactPropertySourceAdapter(Contact contact) { this.contact = contact; } public Object getEditableValue() { return this; } public IPropertyDescriptor[] getPropertyDescriptors() { List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>(); Map<String, Object> properties = this.contact.getProperties(); for (String key : properties.keySet()) { PropertyDescriptor descriptor = new PropertyDescriptor(key, key); descriptor.setAlwaysIncompatible(true); descriptors.add(descriptor); } return descriptors.toArray(new IPropertyDescriptor[0]); } public Object getPropertyValue(Object id) { Map<String, Object> properties = this.contact.getProperties(); return properties.get(id); } public boolean isPropertySet(Object id) { return false; } public void resetPropertyValue(Object id) {} public void setPropertyValue(Object id, Object value) {} } Classe ContactPropertySourceAdapter.java du projet IAdaptableExample2 Code spécifique à IPropertySource découplé de l’objet Contact
  92. 92 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † La troisième approche consiste à passer par une extension pour réaliser l’adaptateur † L’extension permet de définir † Le type à adapter (l’objet du producteur) † La fabrique des objets utilisée pour convertir le type du producteur dans les types des écouteurs † La liste des types des écouteurs que la fabrique peut adapter † Avantage † L’adaptation en un type donné devient en partie déclarative † L’adaptation peut être réalisée en dehors du code du producteur † Inconvénient † Pas simple à première vue, mais nous allons y remédier …
  93. 93 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 3 : utilisation de IAdaptable sans couplage avec le type de l’écouteur package eclipse.workbench.iadaptableexample3; public class Contact implements IAdaptable { ... // Voir code commun public Object getAdapter(Class adapter) { return Platform.getAdapterManager().getAdapter(this, adapter); } } Classe Contact.java du projet IAdaptableExample3 La demande d’adaptation est déléguée à Platform L’objet du producteur n’est plus couplé aux objets des écouteurs L’implémentation à l’interface IAdaptable n’est pas obligatoire
  94. 94 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 3 (suite) : utilisation de IAdaptable sans couplage avec le type de l’écouteur Utilisation du point d’extension org.eclipse.core.runtime.adapters
  95. 95 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 3 (suite) : utilisation de IAdaptable sans couplage avec le type de l’écouteur Onglet Extensions Défini le type à adapter La fabrique des objets utilisée pour convertir le type du producteur dans les types des écouteurs Liste des types des écouteurs
  96. 96 Views - M. Baron - Page mickael-baron.fr mickaelbaron Communication

    entre vues (IAdaptable) † Approche 3 (suite) : utilisation de IAdaptable sans couplage avec le type de l’écouteur package eclipse.workbench.iadaptableexample3; public class ContactAdapterFactory implements IAdapterFactory { private static final Class[] TYPES = { IPropertySource.class }; public Object getAdapter(Object adaptableObject, Class adapterType) { if (adapterType == IPropertySource.class) { if (adaptableObject instanceof Contact) { return new ContactPropertySourceAdapter((Contact) adaptableObject); } } return null; } public Class[] getAdapterList() { return TYPES; } } Classe ContactAdapterFactory.java du projet IAdaptableExample3 La fabrique des objets utilisée pour convertir le type du producteur dans les types des écouteurs Classe équivalente à celle du projet IAdaptableExample2