La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

JAVA Avancé DESS ISIDIS Année 2003/2004 Jean-Christophe Soulié

Présentations similaires


Présentation au sujet: "JAVA Avancé DESS ISIDIS Année 2003/2004 Jean-Christophe Soulié"— Transcription de la présentation:

1 JAVA Avancé DESS ISIDIS Année 2003/2004 Jean-Christophe Soulié

2 DESS ISIDIS /20042 Plan Gestion des évènements MVC Les observeurs Le réflexion Swing Le squelette dun éditeur de texte

3 DESS ISIDIS /20043 Gestion des évènements Un composant enregistre des auditeurs dévènements (Listeners) Lorsquun évènement se produit dans un composant, il est envoyé aux auditeurs enregistrés Chaque auditeur définit les actions à entreprendre, dans des méthodes aux noms prédéfinis Exemple : Un Bouton enregistre des ActionListener Lors dun clic, un ActionEvent est envoyé aux ActionListener enregistrés Ceci provoque lexécution de la méthode actionPerformed de chaque ActionListener

4 DESS ISIDIS /20044 Exemple Une classe de boutons class MonBouton extends Button { int incr; MonBouton(String title, int incr) { super(title); this.incr = incr; } int getIncr() { return incr} } Une classe détiquettes auditrices class ListenerLabel extends Label implements ActionListener{ ListenerLabel() { super("0", Label.CENTER);} public void actionPerformed(ActionEvent evt) { MonBouton b = (MonBouton) evt.getSource(); int c = Integer.parseInt(getText()); c += b.getIncr(); setText(Integer.toString(c)); }

5 DESS ISIDIS /20045 Exemple (fin) Le cadre class PlusOuMoins extends Frame { PlusOuMoins() { super("Plus ou moins"); Bouton oui = new MonBouton("Plus", 1); Bouton non = new MonBouton("Moins", -1); Label diff = new ListenerLabel(); oui.addActionListener((ActionListener) diff); non.addActionListener((ActionListener) diff); } public static void main(String args[]) { Frame r = new PlusOuMoins(); r.pack(); r.setVisible(true); r.addWindowListener(new WindowCloser()); } Et pour fermer class WindowCloser extends WindowAdapter { public void windowClosing(WindowEvent evt) { System.exit(0); }

6 DESS ISIDIS /20046 MVC (Model-View-Controller) Le modèle gère les données La vue affiche les données Le contrôleur gère la communication et les mise-à-jour Origine : Smalltalk (Xerox Park, Palo Alto – Milieu des années 70)

7 DESS ISIDIS /20047 Exemple (1) class PlusouMoinsMVC { Model model; View view; Controller control; public PlusouMoinsMVC() { model = new Model(); control = new Controller(); view = new View( control); control. setModel( model); control. setView( view); view. setVisible( true); view. addWindowListener( new WindowCloser()); } public static void main (String[] argv) { PlusouMoinsMVC r = new PlusouMoinsMVC(); }

8 DESS ISIDIS /20048 Exemple (2) Le modèle contient la donnée La mise-à-jour par update() Le renseignement par getValue() class Model { int compteur; Model() { compteur = 0; } void update( int incr) { compteur += incr; } int getValue() { return compteur; }

9 DESS ISIDIS /20049 Exemple (3) La vue affiche les composants et les données La mise- à- jour du texte de létiquette est faite par update(). La notification de modifications au contrôleur. Cest le contrôleur qui écoute ! Les renseignements sont pris par getValue(). La vue se débrouille pour les obtenir. class View extends Frame { Button oui = new Button("Up !"); Button non = new Button("Down !"); Label diff = new Label("0", Label.CENTER); public View(Controller c) { super("Plus ou moins"); add(oui, BorderLayout.NORTH); add(non, BorderLayout.SOUTH); add(diff, BorderLayout.CENTER); oui.addActionListener(c); non.addActionListener(c); pack(); } void update(int compte) { diff.setText(Integer.toString(compte)); } int getValue(ActionEvent e) { Button b = (Button) e.getSource(); return (b == oui) ? 1 : -1; }

10 DESS ISIDIS / Exemple (4) Le contrôleur : Réveillé par les actions produites dans la vue Récupère des information dans la vue Met à jour dans le modèle Récupère la nouvelle valeur dans le modèle La transmet pour affichage à la vue class Controller implements ActionListener { private Model m; private View v; public void actionPerformed(ActionEvent e) { m.update(v.getValue(e)); v.update(m.getValue()); } public void setModel (Model m) { this.m = m; } public void setView (View v) { this.v = v; }

11 DESS ISIDIS / Observeurs Une classe : Observable Un objet observable issu de cette classe dispose, entre autres, des méthodes suivantes : addObserver(Observer o) permet de rajouter un observeur setChanged() indique un changement de létat de lobjet notifyObservers(Object arg) si l'état de l'objet a été changé, l'appel à cette méthode permet de notifier les observateurs potentiels de ce changement, par l'appel de la méthode update avec pour arguments l'objet observé et l'argument arg

12 DESS ISIDIS / Observeurs (suite) Une interface : Observer Permet de décrire un objet qui souhaite être informé du changement détat dobjets issus de la classe Observable Introduit une seule méthode : public void update(Observable o, Object arg) qui est appelée lorsque o change détat

13 DESS ISIDIS / Observeurs : Exemple (1) Lobservé class TableObservable extends Observable { Hashtable table = new Hashtable(); public synchronized Object put(Object clé, Object valeur) { setChanged(); notifyObservers(clé); return table.put(clé, valeur); } public synchronized Object get(Object clé) { return table.get(clé); } public synchronized boolean containsKey(Object clé) { return table.containsKey(clé); } public synchronized Enumeration keys() { return table.keys(); } public int size() { return table.size(); }

14 DESS ISIDIS / Observeurs : Exemple (2) Lobserveur class ObservateurDeTable implements Observer { public ObservateurDeTable(Observable o) { o.addObserver(this); } public void update(Observable o, Object arg) { System.out.println("J'observe l'objet : "+o+"\nqui m'envoie :"+arg); }

15 DESS ISIDIS / Observeurs : Exemple (3) class TestObserver{ public static void main (String[] args) { StringTokenizer lstMots = new StringTokenizer(args[0], ",."); TableObservable table = new TableObservable(); ObservateurDeTable observateur = new ObservateurDeTable(table); while (lstMots.hasMoreTokens()) { String mot = lstMots.nextToken(); if (!table.containsKey(mot)) table.put(mot, new Integer(1)); else { Integer nbre = (Integer) table.get(mot); Integer i = new Integer(1+nbre.intValue()); table.put(mot, i); } System.out.print("==> Dans la phrase \""); System.out.print(args[0]); System.out.print("\",\n il y a "); System.out.print(table.size()); System.out.println(" mots différents qui sont:"); for (Enumeration e = table.keys(); e.hasMoreElements(); ) { String mot = (String) e.nextElement(); System.out.println("==> "+mot+" ("+table.get(mot)+" fois)"); }

16 DESS ISIDIS / Observeurs : Exemple (4) Trace de lexécution $ java TestObserver "Voila ma phrase, ma courte phrase." J'observe l'objet : qui m'envoie : Voila J'observe l'objet : qui m'envoie : ma J'observe l'objet : qui m'envoie : phrase J'observe l'objet : qui m'envoie : ma J'observe l'objet : qui m'envoie : courte J'observe l'objet : qui m'envoie : phrase ==> Dans la phrase "Voila ma phrase, ma courte phrase.", il y a 4 mots différents qui sont: ==> phrase (2 fois) ==> ma (2 fois) ==> courte (1 fois) ==> Voila (1 fois) $

17 DESS ISIDIS / Réflexion L'objet Class, qui contient toutes les informations relative à la classe (on l'appelle parfois meta-class). En fait, l'objet Class est utilisé pour créer tous les objets « habituels » d'une classe. Il y a un objet Class pour chacune des classes d'un programme. A chaque fois qu'une classe est écrite et compilée, un unique objet de type Class est aussi créé

18 DESS ISIDIS / Réflexion Durant l'exécution, lorsqu'un nouvel objet de cette classe doit être créé, la JVM qui exécute le programme vérifie d'abord si l'objet Class associé est déjà chargé. Si non, la JVM le charge en cherchant un fichier.class du même nom. Ainsi un programme Java n'est pas totalement chargé en mémoire lorsqu'il démarre, contrairement à beaucoup de langages classiques. Une fois que l'objet Class est en mémoire, il est utilisé pour créer tous les objets de ce type.

19 DESS ISIDIS / Réflexion Class.forName("NomClasse"); Cette méthode est une méthode static de Class (qui appartient à tous les objets Class ). Un objet Class est comme tous les autres objets, il est donc possible d'obtenir sa référence et de la manipuler (c'est ce que fait le chargeur de classes). Un des moyens d'obtenir une référence sur un objet Class est la méthode forName(), qui prend en paramètre une chaîne de caractères contenant le nom de la classe dont vous voulez la référence. Elle retourne une référence sur un objet Class.

20 DESS ISIDIS / Réflexion La classe Class (décrite précédemment dans ce chapitre) supporte le concept de réflexion, et une bibliothèque additionnelle, java.lang.reflect, contenant les classes Field, Method, et Constructor (chacune implémentant l'interface Member ). Les objets de ce type sont créés dynamiquement par la JVM pour représenter les membres correspondants d'une classe inconnue.

21 DESS ISIDIS / Réflexion On peut alors utiliser les constructeurs pour créer de nouveaux objets, les méthodes get() et set() pour lire et modifier les champs associés à des objets Field, et la méthode invoke() pour appeler une méthode associée à un objet Method. De plus, on peut utiliser les méthodes très pratiques getFields(), getMethods(), getConstructors(), etc. retournant un tableau représentant respectivement des champs, méthodes et constructeurs (pour en savoir plus, jetez un oeil à la documentation en ligne de la classe Class ). Ainsi, l'information sur la classe d'objets inconnus peut être totalement déterminée dynamiquement, sans rien en savoir à la compilation.

22 DESS ISIDIS / Réflexion : un exemple Un extracteur de méthodes public class ShowMethods { public static void main(String[] args) { try { Class c = Class.forName(args[0]); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); if(args.length == 1) { for (int i = 0; i < m.length; i++) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) System.out.println(ctor[i]); } else { for (int i = 0; i < m.length; i++) if(m[i].toString().indexOf(args[1])!= -1) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) if(ctor[i].toString().indexOf(args[1])!= -1) System.out.println(ctor[i]); } } catch(ClassNotFoundException e) { System.err.println("Classe non trouvée : " + e); } } }

23 DESS ISIDIS / Swing Swing est une extension des AWT nombreux nouveaux composants nombreuses facilités séparation entre : modèle (données) aspect visuel (UI) contrôle Les composants sont légers, sauf JApplet, JWindow, JFrame, JDialog Ces derniers ont une structure spéciale

24 DESS ISIDIS / Swing – larbre dhéritage

25 DESS ISIDIS / JFrame Une JFrame contient une fille unique, de la classe JRootPane. Cette fille contient deux filles, glassPane ( JPanel ) et layeredPane ( JLayeredPane ). La layeredPane a deux filles, contentPane (un Container ) et menuBar (null JMenuBar ). On travaille dans contentPane. JApplet, JWindow et JDialog sont semblables. class Tout extends JFrame { Tout() { JPanel panel; getContentPane().add(panel);... }... }

26 DESS ISIDIS / Modèles et vues Les composants (sauf les conteneurs) ont un modèle qui contient les données associées ButtonModel pour les boutons ListModel pour les données dun JList TableModel pour les JTable TreeModel pour les JTree Document pour tous les composants de texte La vue dun composant sont agrégés en un délégué UI (User Interface) détermine le look-and-feel du composant (bord, couleur, ombre, forme des coches) peut- être changé est parfois spécifié par un dessinateur (renderer) à un niveau plus élevé un changement global, pour tous les composants, est le pluggable look and feel (plaf) trois implémentations (quatre ave le Mac) existent : Windows, CDE/ Motif, Metal (défaut). Le contrôle est assuré par le modèle.

27 DESS ISIDIS / Look and Feel Trois look and feel existent, de noms "com. sun. java. swing. plaf. windows.WindowsLookAndFeel" "com. sun. java. swing. plaf. motif.MotifLookAndFeel" "javax. swing. plaf. metal. MetalLookAndFeel On essaye de lutiliser par UIManager.setLookAndFeel(lf); et de lappliquer à la racine de larbre par SwingUtilities. updateComponentTreeUI (SwingUtilities.getRoot(this));

28 DESS ISIDIS / Actions Moyen commode pour définir une entrée (dans un menu) et simultanément y attacher un auditeur Entrée simultanée dans Toolbar possible Tout changement dans lun se reflète sur lautre (grisé etc.)

29 DESS ISIDIS / AbstractAction AbstractAction est une classe abstraite elle implémente linterface Action Action étend ActionListener la seule méthode à écrire est actionPerformed() Les conteneurs JMenu, JPopupMenu et JToolBar honorent les actions : un même objet dune classe implémentant AbstractAction peut être « ajouté » à plusieurs de ces conteneurs. les diverses instances opèrent de concert. par exemple, un objet ajouté à un menu et à une barre doutils est activé ou désactivé simultanément dans les deux. Les classes dérivées de AbstractAction sont utiles quand une même action peut être déclenchée de plusieurs manières.

30 DESS ISIDIS / Emploi dAbstractAction Création dune classe qui étend AbstractAction Utilisation comme ActionListener Utilisation dans un menu et dans une barre doutils class MonAction extends AbstractAction { public void actionPerformed(ActionEvent e) {... } Action monAction = new MonAction(); JButton b = new JButton("Hello"); b. addActionListener(monAction); Action copyAction = new MonAction("Copy"); JMenu menu = new JMenu("Edit"); JToolBar tools = new JToolBar(); JMenuItem copyItem = menu.add(copyAction); JButton copyBtn = tools.add(copyAction);

31 DESS ISIDIS / JTabbedPane Groupe une liste de conteneurs repérés par des onglets. Création: Ajout de conteneurs à un tabbedPane : JTabbedPane() JTabbedPane( int cotéOnglets) addTab(String texteOnglet, Component composant) addTab(String texteOnglet, Icon icone, Component composant) addTab(String texteOnglet, Icon icone, Component composant, String toolTipText)

32 DESS ISIDIS / JTabbedPane Feuille initiale Récupérer le choix Et la feuille elle-même Nombre total de feuilles tabbedPane.setSelectedIndex(int numero) int tabbedPane. getSelectedIndex() Component tabbedPane.getComponentAt(int numero); int tabbedPane.getTabCount();

33 DESS ISIDIS / JTabbedPane : exemple (1) Pour naviguer ajouter, enlever les feuilles choisir la position des onglets De plus un message affiche le numéro de longlet, à chaque changement

34 DESS ISIDIS / JTabbedPane : exemple (2) Acteurs principaux class Panneau extends JPanel implements ActionListener { String [] imageNames = {"arques","berstel","crochemore","desarmenien",...}; ImageIcon[] images = new ImageIcon[imageNames.length]; //les images montrées ImageIcon tabimage; //licône dans les onglets JTabbedPane tabbedPane; //le panneau à feuilles String[] boutonNames = {"TOP","BOTTOM","LEFT","RIGHT","add","remove"}; JButton[] boutons = new JButton[boutonNames.length]; //les boutons de gestion JLabel statut; //le message détat AudioClip layoutson, tabson; //les sons des actions Panneau() {} //création de la scène void createTab() {} //ajoute une feuille et son onglet void killTab() {} //supprime une feuille void setStatus(int index) {...} //gestion du message public void actionPerformed(ActionEvent e) {...} //les actions des boutons }

35 DESS ISIDIS / JTabbedPane : exemple (3) Création/Suppression de feuilles public void createTab() { JLabel feuille = null; int ong = tabbedPane.getTabCount(); feuille = new JLabel(imageNames[ong % images.length], images[ong % images.length], SwingConstants.CENTER); feuille.setOpaque(true); feuille.setBackground(Color. green); tabbedPane.addTab("Feuille No " + ong, tabimage, feuille); tabbedPane.setSelectedIndex(ong); setStatus(ong); } public void killTab() { // dernière if (tabbedPane.getTabCount()> 0) { tabbedPane.removeTabAt(tabbedPane.getTabCount() - 1); setStatus(tabbedPane.getSelectedIndex()); } public void setStatus( int index) { if (index > -1) statut.setText("Feuille choisie: " + index); else statut.setText("Pas de feuille choisie"); }

36 DESS ISIDIS / JTabbedPane : exemple (4) Les actions des boutons La classe SwingConstants contient les constantes de placement public void actionPerformed(ActionEvent e) { String lib = ((JButton) e.getSource()).getActionCommand(); if (lib.equals(boutonNames[0])) { tabbedPane.setTabPlacement(SwingConstants.TOP); layoutson.play(); } else if (lib.equals(boutonNames[1])) { tabbedPane.setTabPlacement(SwingConstants.BOTTOM); layoutson.play(); } else if (lib.equals(boutonNames[2])) { tabbedPane.setTabPlacement(SwingConstants.LEFT); layoutson.play(); } else if (lib.equals(boutonNames[3])) { tabbedPane.setTabPlacement(SwingConstants.RIGHT); layoutson.play(); } else if (lib.equals(boutonNames[4])) createTab(); else if(lib.equals(boutonNames[5])) killTab(); }

37 DESS ISIDIS / JTabbedPane : exemple (5) Panneau() { tabimage = new ImageIcon("gifs/tabimage.gif"); for (int i = 0 ; i < images.length; i++) images[i] = new ImageIcon("gifs/" + imageNames[i] +".jpg"); for (int i = 0; i < boutons.length; i++) boutons[i] = new JButton(boutonNames[i]); statut = new JLabel(); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(0,1)); for (int i = 0; i < boutons.length ; i++){ boutons[i].addActionListener(this); buttonPanel.add(boutons[i]); } JPanel leftPanel = new JPanel(); leftPanel.add(buttonPanel); tabbedPane = new JTabbedPane(SwingConstants.TOP); createTab(); createTab(); tabbedPane.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e) { setStatus(((JTabbedPane) e.getSource()).getSelectedIndex()); tabson.play(); } }); setLayout(new BorderLayout()); add(leftPanel, BorderLayout.WEST); add(statut, BorderLayout.SOUTH); add(tabbedPane, BorderLayout.CENTER); }

38 DESS ISIDIS / JTabbedPane : exemple (6) On modifie le contenu après le chargement La méthode revalidate sert à « forcer » le réaffichage. public void init() { JLabel loading = new JLabel("Initialisation en cours...", JLabel.CENTER); setContentPane(loading); setVisible(true); getRootPane().revalidate(); try { Thread. sleep( 1000); } catch (InterruptedException e) {}; layoutson = getAudioClip(getCodeBase(), "switch.wav"); tabson = getAudioClip(getCodeBase(), "tab.wav"); Panneau panneau = new Panneau(); panneau.addAudioClips(layoutson, tabson); setContentPane(panneau); }

39 DESS ISIDIS / JScrollPane Gère automatiquement des ascenseurs autour de son composant central qui est un JViewPort. Constructeurs principaux Une « vue » sajoute au JViewPort, si elle ne lest dans le constructeur, par JScrollPane() JScrollPane(Component view) scrollPane.getViewPort().add(view)

40 DESS ISIDIS / Exemple class ScrollPanel extends JPanel { public ScrollPanel() { setLayout(new BorderLayout()); Icon iconeTigre = new ImageIcon("BigTiger.gif"); JLabel etiquetteTigre = new JLabel(iconeTigre); JScrollPane scrollPane = new JScrollPane(etiquetteTigre); add(scrollPane, BorderLayout.CENTER); }

41 DESS ISIDIS / JSplitPane Panneau à compartiments, chaque compartiment est ajustable Seule une classe de look-and-feel est nécessaire. Panneau à séparation verticale ou horizontale Constructeurs : JSplitPane(int orientation, boolean dessinContinu, Component gauche, Component droit) JSplitPane(int orientation, Component gauche, Component droit) JSplitPane(int orientation, boolean dessinContinu) JSplitPane(int orientation) JSplitPane() // horizontal par défaut

42 DESS ISIDIS / JSplitPane : exemple (1) La taille de la barre de séparation peut être réglée par setDividerSize(int taille) Laffichage continue spécifié explicitement par setContinuousLayout(boolean dessinContinu) Poignée douverture/ fermeture spécifiées par setOneTouchExpandable(boolean ouvrable) ImageIcon bleue = new ImageIcon("bleue.gif"); aireGauche = new PanneauBoules(150, bleue.getImage()); ImageIcon rouge = new ImageIcon("rouge.gif"); aireDroite = new PanneauBoules(150, rouge.getImage()); JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_ SPLIT, aireGauche, aireDroite); sp.setDividerSize(5); sp.setContinuousLayout(true); getContentPane().add(sp, BorderLayout.CENTER);

43 DESS ISIDIS / JSplitPane : exemple (2) public class Split extends JFrame { protected PanneauBoules aireGauche, aireDroite; public Split() {... ImageIcon bleue = new ImageIcon("bleue. gif"); aireGauche = new PanneauBoules(150, bleue.getImage()); ImageIcon rouge = new ImageIcon("rouge.gif"); aireDroite = new PanneauBoules(150, rouge.getImage()); JSplitPane sp = new JSplitPane(JSplitPane. HORIZONTAL_ SPLIT, aireGauche, aireDroite); sp.setDividerSize(5); sp.setContinuousLayout(true); getContentPane().add(sp, BorderLayout.CENTER); setVisible(true);... new Thread(aireGauche).start(); new Thread(aireDroite).start(); }

44 DESS ISIDIS / JSplitPane : exemple (3) class PanneauBoules extends JPanel implements Runnable, ComponentListener { Boule[] boules; Image img; Dimension dim; int sommeil; public PanneauBoules(int nBoules, Image img) { sommeil = 10; this.img = img; setBackground(Color.yellow); setPreferredSize(new Dimension(200, 300)); addComponentListener(this); boules = new Boule[nBoules]; dim = getPreferredSize(); for (int k= 0; k < nBoules; k++) boules[k] = new Boule(dim); } public void run() {...} }

45 DESS ISIDIS / JSplitPane : exemple (5) Runnable public void run() { for(;;) { for (int k = 0; k < boules.length; k++) boules[k].move(dim); repaint(); if (sommeil != 0) { try { Thread.sleep(sommeil); } catch(InterruptedException e) {} } public void paintComponent(Graphics g) { g.setColor(getBackground()); g.fillRect(0, 0, dim.width, dim.height); for (int k = 0; k < boules.length; k++) g. drawImage(img,(int) boules[k].x,(int) boules[k].y, this); }

46 DESS ISIDIS / JSplitPane : exemple (6) ComponentListener public void componentHidden(ComponentEvent e){} public void componentShown(ComponentEvent e){} public void componentMoved(ComponentEvent e){} public void componentResized(ComponentEvent e){ dim = getSize(); for (int k = 0; k < boules.length; k++) boules[k].moveIntoRect(dim); }

47 DESS ISIDIS / JSplitPane : exemple (7) Les boules class Boule { protected double x, y, vx, vy; public Boule(Dimension dim) { x = dim.width * Math.random(); y = dim.height * Math.random(); double angle = 2 * Math.PI * Math.random(); vx = 2* Math.cos(angle); vy = 2* Math.sin(angle); } public void move(Dimension dim) { double nx =x +vx; double ny =y +vy; if ((nx dim.width)) { vx = - vx; nx = x +vx; } if ((ny dim.height)) { vy = - vy; ny = y +vy; } x = nx; y = ny; } public void moveIntoRect(Dimension dim) { x = Math.max(x, 0); x = Math.min(x, dim.width); y = Math.max(y, 0); y = Math.min(y, dim.height);

48 DESS ISIDIS / JTree Description hiérarchique de données Sept autre classes utilisées TreeModel : contient les données figurant dans larbre TreeNode : implémentation des nœuds et de la structure darbre TreeSelectionModel : contient le ou les nœuds sélectionnés TreePath : un tel objet contient un chemin (de la racine vers le sommet sélectionné par exemple) TreeCellRenderer : est appelé pour dessiner un nœud TreeCellEditor : léditeur pour un nœud est éditable TreeUI : look-and-feel

49 DESS ISIDIS / JTree Un arbre est créé à partir dun TreeModel Il existe plusieurs modèles de sélection sélection dun seul élément sélection de plusieurs éléments contigus sélection de plusieurs éléments disparates On peut indiquer un CellRenderer pour afficher une cellule de façon particulière. On peut indiquer un CellEditor pour changer la valeur dune cellule interface TreeModel {... public Object getChild(Object parent, int index); public Object getRoot(); public boolean isLeaf(Object node);... }

50 DESS ISIDIS / JTree JTree fournit une vue du modèle Le modèle darbre est en deux étapes: Le modèle des nœuds est en trois étages: Constructeurs une feuille peut recevoir des fils ? reste sans fils ? interface TreeModel class DefaultTreeModel implements TreeModel interface TreeNode interface MutableTreeNode extends TreeNode class DefaultMutableTreeNode implements MutableTreeNode JTree() JTree(TreeNode racine) JTree(TreeNode racine, boolean enfantsPermis) JTree(TreeModel modele) JTree(TreeModel modele, boolean enfantsPermis)

51 DESS ISIDIS / Jtree : exemple class Arbre extends JPanel { JTree tree; public Arbre() { DefaultMutableTreeNode top, noeud, fils, n; top = new DefaultMutableTreeNode("Top"); tree = new JTree(top); noeud = new DefaultMutableTreeNode("Repertoire 1"); top.add(noeud); n = new DefaultMutableTreeNode("1a"); noeud.add(n); n = new DefaultMutableTreeNode("1b"); noeud.add(n);... noeud = new DefaultMutableTreeNode("Repertoire 2"); top.add(noeud); n = new DefaultMutableTreeNode("2a"); noeud.add(n);.... fils = new DefaultMutableTreeNode("2d"); noeud.add(fils); n = new DefaultMutableTreeNode("3a"); fils.add( n); }... }

52 DESS ISIDIS / JTree Le contenu dun nœud est appelé user object Cest un objet A laffichage, la méthode toString() dun nœud délègue à la méthode toString() du contenu.

53 DESS ISIDIS / JTree Un DefaultTreeCellRenderer soccupe du rendu. Il peut être modifié par des fonctions utilitaires par une redéfinition DefaultTreeCellRenderer rendu ; rendu = (DefaultTreeCellRenderer) tree.getCellRenderer(); rendu.setOpenIcon(new ImageIcon("Opened.gif")); rendu.setLeafIcon(new ImageIcon("Leaf.gif"));

54 DESS ISIDIS / JTree Un TreeSelectionListener rapporte tous les changements dans les sélections De nombreuses fonctions utilitaires tree.addTreeSelectionListener(new Selecteur()); class Selecteur implements TreeSelectionListener { public void valueChanged(TreeSelectionEvent e ) { message.setText("Nouveau : " + e.getNewLeadSelectionPath()); }

55 DESS ISIDIS / JTree On parcourt un arbre par une énumération Il en existe quatre : breadthFirstEnumeration depthFirstEnumeration postorderEnumeration preorderEnumeration public void actionPerformed(ActionEvent ev) { DefaultMutableTreeNode n, top; Enumeration e; top = (DefaultMutableTreeNode) tree.getModel().getRoot(); System.out.println("\ n En largeur"); e =top.breadthFirstEnumeration(); while (e.hasMoreElements()) { n = (DefaultMutableTreeNode) e.nextElement(); System.out.println(n.getUserObject() + " "); }

56 DESS ISIDIS / JTree : exemple(1) Dans cette application, on entre un nom de classe dans la zone de texte, et la classe sinsère dans la hiérarchie des classes. La classe Class permet de connaître la classe mère. On ninsère une classe que si elle nest pas déjà dans larbre.

57 DESS ISIDIS / JTree : exemple(2) Cest addClass(Class c) qui fait linsertion class ClassTreeFrame extends JFrame implements ActionListener { private DefaultMutableTreeNode root; private DefaultTreeModel model; private JTree tree; private JTextField textField; public ClassTreeFrame() { setTitle("ClassTree"); root = new DefaultMutableTreeNode(Object.class); model = new DefaultTreeModel(root); tree = new JTree(model); addClass( getClass()); getContentPane().add(new JScrollPane(tree), "Center"); textField = new JTextField(); textField.addActionListener(this); getContentPane().add(textField, "South"); }... }

58 DESS ISIDIS / JTree : exemple(3) public DefaultMutableTreeNode addClass(Class c) { if (c.isInterface() || c.isPrimitive()) return null; //pas les interfaces findUserObject(c) //cherche c dans tree DefaultMutableTreeNode node = findUserObject(c); if (node != null) return node; Class s = c.getSuperclass(); //classe mère DefaultMutableTreeNode parent = addClass(s); //appel récursif DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(c); model.insertNodeInto(newNode, parent, parent.getChildCount()); //à la fin //développe larbre pour que le nœud soit visible TreePath path = new TreePath(model.getPathToRoot(newNode)); tree.makeVisible(path); return newNode; }

59 DESS ISIDIS / JTree : exemple(4) Trouver un nœud dans un arbre Un simple parcours, en largeur par exemple public DefaultMutableTreeNode findUserObject(Object obj){ Enumeration e = root.breadthFirstEnumeration(); while (e.hasMoreElements()) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement(); if (node.getUserObject().equals(obj)) return node; } return null; }

60 DESS ISIDIS / JTree : exemple(5) Lire le nom de la classe On fait confiance à Java… public void actionPerformed(ActionEvent event) { String text = textField.getText(); try { Class c = Class.forName(text); //essayons addClass(c); textField.setText(""); } catch (ClassNotFoundException e) { Toolkit.getDefaultToolkit().beep(); //si la classe nexiste pas }

61 DESS ISIDIS / JTable JTable affiche des données dans un tableau TableModel régit la gestion des données On peut fournir les données dans un tableau bidimensionnel dobjets : Object[][] et utiliser le DefaultTableModel, mais il vaut mieux étendre AbstractTableModel. La sélection est régi par une modèle de sélection De plus, il y a un modèle de colonnes. Un tableau est entouré dascenseurs, en général.

62 DESS ISIDIS / JTable Les constructeurs sont : JTable() modèles par défaut pour les trois modèles JTable(int numRows, int numColumns) avec autant de cellules vides JTable(Object[][] rowData, Object[] columnNames) avec les valeurs des cellules de rowData et noms de colonnes columnNames. JTable(TableModel dm) avec le modèle de données dm, les autres par défaut. JTable(TableModel dm, TableColumnModel cm) avec modèle de données et modèle de colonnes fournis. JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) Les trois modèles sont fournis. JTable(Vector rowData, Vector columnNames) ici, les données sont fournies par colonne.

63 DESS ISIDIS / JTable : exemple class TablePlanetes extends JPanel { TablePlanetes() { setLayout(new BorderLayout()); JTable table = new JTable(cellules, columnNames); add(new JScrollPane(table), BorderLayout.CENTER); } private Object[][] cellules = { { "Mercure", new Double(2440), new Integer(0), "non"}, { "Vénus", new Double(6052), new Integer(0), "non"}, { "Terre", new Double(6378), new Integer(1), "non"}, { "Mars", new Double(3397), new Integer(2), "non"}, { "Jupiter", new Double(71492), new Integer(16), "oui"}, { "Saturne", new Double(60268), new Integer(18), "oui"}, { "Uranus", new Double(25559), new Integer(17), "oui"}, { "Neptune", new Double(24766), new Integer(8), "oui"}, { "Pluton", new Double(1137), new Integer(1), "non"} }; private String[] columnNames = { "Planète", "Rayon", "Lunes", "Gazeuse"};

64 DESS ISIDIS / JTable Les données sont accessible par un modèle. Ils peuvent être stockés ou calculés, de façon transparente. La classe AbstractTableModel implémente les méthodes dun modèle de table, sauf qui retournent respectivement le nombre de lignes le nombre de colonnes lobjet à afficher dans les ligne et colonne indiquées (sa méthode toString est utilisée). public int getRowCount() public int getColumnCount() public Object getValueAt(int ligne, int colonne)

65 DESS ISIDIS / JTable : un premier exemple En plus des trois méthodes obligées, la fonction getColumClass a été redéfinie, ce qui produit la justification à droite class SimpleTable extends JPanel { SimpleTable() { setLayout(new BorderLayout()); TableModel dataModel = new AbstractTableModel() { public int getColumnCount() { return 10; } public int getRowCount() { return 10;} public Object getValueAt(int row, int col) { return new Integer((1 + row)*(1 + col)); } public Class getColumnClass(int column) { return Number.class; } }; JTable table = new JTable(dataModel); add(new JScrollPane(table)); }

66 DESS ISIDIS / JTable : un deuxième exemple Construction par avec bien entendu méthodes à écrire (plus getColumnName qui, par défaut, numérote A, B, etc.): TableModel model = new ModelInvestment(30, 5, 10); JTable table = new JTable(model); class ModelInvestment extends AbstractTableModel {...} public int getRowCount() public int getColumnCount() public Object getValueAt(int ligne, int colonne) public String getColumnName(int colonne)

67 DESS ISIDIS / JTable : détails class ModelInvestment extends AbstractTableModel { private int annees; private int tauxMin; private int tauxMax; private static double depot = ; ModelInvestment(int annees, int tauxMin, int tauxMax) { this.annees = annees; this.tauxMin = tauxMin; this.tauxMax = tauxMax; } public int getRowCount() { return annees;} public int getColumnCount() { return tauxMax - tauxMin + 1;} public Object getValueAt(int ligne, int colonne) { double taux = (colonne + tauxMin) / 100.0; double depotFinal = depot * Math.pow(1 + taux, ligne); return NumberFormat.getCurrencyInstance().format(depotFinal); } public String getColumnName(int colonne) { double taux = (colonne + tauxMin) / 100.0; return NumberFormat.getPercentInstance().format(taux); }

68 DESS ISIDIS / JTable La classe abstraite java.text.NumberFormat est la classe de base pour le formatage de nombres. Nombreuses méthodes statiques retournant des formats appropriés. Le formatage effectif se fait par la méthode du format Exemples de méthodes: Le format dépend de la Locale, cest-à-dire du pays concerné. NumberFormat.getNumberInstance() NumberFormat.getCurrencyInstance() NumberFormat.getPercentInstance() String format(int donnée)

69 DESS ISIDIS / JTable La méthode boolean TableModel.isCellEditable(int l, int c) renvoie true si la cellule peut être modifiée (par défaut non) La méthode int JTable.columnAtPoint(Point p) renvoie lindice de la colonne du tableau où est le point La méthode int JTable.convertColumnIndexToModel(int colonne) renvoie lindice, dans le modèle, de lindice colonne

70 DESS ISIDIS / JTable : tri par ligne (1) On veut trier les lignes, en fonction dune colonne désignée par un double clic. On veut que le tri se fasse sur la vue, pas sur le modèle, pour que le modèle soit inchangé. Pour cela, on introduit un filtre de modèle similaire aux filtres de streams. Ce filtre enregistre une référence au modèle réel, et intercepte les communications entre la table et son modèle pour les réinterpréter. Le filtre maintient une permutation des lignes déterminée par le tri virtuel des lignes en fonction de la colonne choisie.

71 DESS ISIDIS / JTable : tri par ligne (2) Le modèle est associé à la table class TablePlanetes extends JPanel { TablePlanetes() { setLayout(new BorderLayout()); DefaultTableModel model = new DefaultTableModel(cellules, columnNames); FiltreTriModel sorter = new FiltreTriModel(model); JTable table = new JTable(sorter); sorter.addEcouteur(table); add(new JScrollPane(table), BorderLayout.CENTER); } private Object[][] cellules ={... }; private String[] columnNames = {... }; }

72 DESS ISIDIS / JTable : tri par ligne (3) La classe FiltreTriModel est un modèle de table avec une référence au modèle réel un tableau de lignes une méthode de tri de ce tableau Le tri est fait par la méthode statique de la classe Arrays, en fonction de la colonne choisie. Pour cela, la classe Ligne doit implémenter linterface Comparable. class FiltreTriModel extends AbstractTableModel { public FiltreTriModel(TableModel m) { model = m; lignes = new Ligne[model.getRowCount()]; for (int i = 0; i < lignes.length; i++) { lignes[i] = new Ligne(); lignes[i].index = i; } public void sort(int c) { colonneTri = c; Arrays.sort(lignes); fireTableDataChanged(); }... private TableModel model; private int colonneTri; private Ligne[] lignes; }

73 DESS ISIDIS / JTable : tri par ligne (4) La classe Ligne est interne à FiltreTriModel pour accéder facilement aux données. Une ligne est plus petite quune autre si son élément dans la colonne de tri est plus petit que lélément de cette même colonne dans lautre ligne. class FiltreTriModel extends AbstractTableModel {... private class Ligne implements Comparable { public int index; public int compareTo(Object autre) { Ligne autreLigne = (Ligne) autre; Object cellule = model.getValueAt(index, colonneTri); Object autreCellule = model.getValueAt(autreLigne.index, colonneTri); return ((Comparable) cellule).compareTo(autreCellule); }

74 DESS ISIDIS / JTable : tri par ligne (5) Les trois fonctions obligées tiennent compte de lordre virtuel class FiltreTriModel extends AbstractTableModel {... public Object getValueAt(int r, int c) { return model.getValueAt(lignes[r].index, c); } public int getRowCount(){ return model.getRowCount();} public int getColumnCount(){ return model.getColumnCount();} public String getColumnName(int c){ return model.getColumnName(c);} public Class getColumnClass(int c){ return (c == 1 || c == 2) ? Number.class : Object.class; }

75 DESS ISIDIS / JTable : tri par ligne (6) Et le double clic Cest len-tête de colonne qui réagit. Au double clic sur une colonne, on récupère le numéro de la colonne dans le modèle class FiltreTriModel extends AbstractTableModel {... public void addEcouteur(final JTable table) { table.getTableHeader().addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent event) { if (event.getClickCount() < 2) return; int tc = table.columnAtPoint(event.getPoint()); int mc = table.convertColumnIndexToModel(tc); sort(mc); } }); }

76 DESS ISIDIS / JTable : tri par ligne (7) Événements La classe JTable écoute les événements reçus du modèle Le modèle a plusieurs méthodes pour signaler des modifications de données Les colonnes sont régies par un TableColumnModel qui a ses propres notificateurs dévénements fireTableDataChanged() fireTableStructureChanged() fireTableRowsInverted(int first, int last) fireTableRowsUpdated(int first, int last) fireTableRowsDeleted(int first, int last) fireTableCellUpdated(int row, int col) fireTableChangedEvent(TableModelEvent e)

77 DESS ISIDIS / Undo/Redo Le « undo » (annuler) et « redo » (refaire) sont parmi les opérations les plus appréciées dans les interfaces ergonomiques. Ce sont des opérations difficiles à implémenter. Questions: quelles sont les transactions annulables ? quelle partie de lenvironnement doit être sauvegardée pour pouvoir le reconstituer ? vaut- il mieux conserver lopération, ou son inverse ? Java fournit un cadre surtout adapté aux opérations sur les textes. Plusieurs variantes existent, mais il reste du travail au programmeur.

78 DESS ISIDIS / UndoableEdit Linterface de base est UndoableEdit. Une implémentation par défaut est AbstractUndoableEdit « Edit » est synonyme de transaction ou opération, terme emprunté aux éditeurs de textes. Les méthodes sont boolean canUndo() indique que la transaction peut être annulée boolean canRedo() indique que la transaction peut être refaite void die() la transaction ne peut plus être annulée ni répétée void redo() throws CannotRedoException refait la transaction void undo() throws CannotUndoException annule la transaction

79 DESS ISIDIS / AbstractUndoableEdit Cest limplémentation par défaut de UndoableEdit Elle maintient deux booléens internes alive et done qui gèrent correctement le canUndo() et canRedo(). On sous-classe cette classe en redéfinissant undo() et redo(). On utilise la super-classe en appelant super. undo(), super.redo().

80 DESS ISIDIS / Exemple des boutons à cocher Lopération de coche ou décoche peut être annulée ou refaite, à partir dun autre composant (paire de boutons, plus souvent entrée de menu ou boutons dune barre doutils) Démarche: Chaque action sur le bouton génère un objet dune classe ToggleEdit dérivant de AbstractUndoableEdit. Lobjet contient le bouton concerné létat du bouton La classe ToggleEdit redéfinit les méthodes undo() et redo(). Lopération dannulation ou répétition est lancée en appelant la méthode undo() ou redo() sur lobjet créé.

81 DESS ISIDIS / ToggleEdit import javax.swing.undo.*; public class ToggleEdit extends AbstractUndoableEdit { private final JToggleButton bouton; private final boolean selectionne; public ToggleEdit(JToggleButton bouton) { this.bouton = bouton; selectionne = bouton.isSelected(); } public void redo() throws CannotRedoException { super.redo(); bouton.setSelected(selectionne); } public void undo() throws CannotUndoException { super.undo(); bouton.setSelected(!selectionne); }

82 DESS ISIDIS / Le panneau Le panneau est composé de trois boutons à cocher et de deux boutons dannulation et répétition: chaque bouton à cocher ( JCheckBox ) a un écouteur dont la méthode actionPerformed est : JCheckBox gras = new JCheckBox("gras"); JCheckBox ital = new JCheckBox("italique"); JCheckBox soul = new JCheckBox("souligné"); JButton undoButton = new JButton("Undo"); JButton redoButton = new JButton("Redo"); undoButton.addActionListener(new UndoIt()); redoButton.addActionListener(new RedoIt()); public void actionPerformed(ActionEvent ev) { JToggleButton b = (JToggleButton) ev.getSource(); edit = new ToggleEdit(b); updateButtons(); }

83 DESS ISIDIS / Les listeners class UndoIt implements ActionListener { public void actionPerformed(ActionEvent ev) { try { edit.undo(); } catch (CannotUndoException ex) {} finally { updateButtons(); } class RedoIt implements ActionListener { public void actionPerformed(ActionEvent ev) { try { edit.redo(); } catch (CannotRedoException ex) {} finally { updateButtons(); } private void updateButtons() { undoButton.setText(edit.getUndoPresentationName()); redoButton.setText(edit.getRedoPresentationName()); undoButton.setEnabled(edit.canUndo()); redoButton.setEnabled(edit.canRedo()); }

84 DESS ISIDIS / Complément Linterface UndoableEdit a une méthode getPresentationName qui retourne une chaîne de caractère modifiable en fonction de la transaction Les méthodes getUndoPresentationName et getRedoPresentationName concatènent le préfix Undo et Redo avec la chaîne fournie par getPresentationName class ToggleEdit { private final JToggleButton bouton; private final boolean selectionne;... public String getPresentationName() { return "\"" + bouton.getText() + (selectionne ? " on" : " off") + "\""; }...

85 DESS ISIDIS / Séquences de transactions Pour se « souvenir » dune séquence de transactions, et pouvoir revenir en arrière arbitrairement loin, on utilise un gestionnaire de transactions ( UndoManager ). Un UndoManager gère les transactions ( Edit ). Il permet de reculer ( undo ) et davancer ( redo ) tant que possible. Une transaction à inscrire dans un gestionnaire doit lui être notifiée, soit directement, par addEdit(UndoableEdit edit) soit en utilisant le fait quun UndoManager implémente un UndoableEditListener. On enregistre le gestionnaire dans la liste des auditeurs.

86 DESS ISIDIS / Implémentation simple Un UndoManager étend CompoundEdit qui lui étend AbstractUndoableEdit On remplace simplement la variable UndoEdit edit par UndoManager manager et on modifie actionPerformed() en conséquence

87 DESS ISIDIS / Modifications class TogglePanel extends Jpanel implements ActionListener { private UndoManager manager = new UndoManager(); private JButton undoButton,...; public void actionPerformed(ActionEvent e) { JToggleButton b = (JToggleButton) e.getSource(); manager.addEdit(new ToggleEdit(b)); updateButtons(); } class UndoIt implements ActionListener { public void actionPerformed(ActionEvent e){ try { manager.undo(); }... } private void updateButtons() { undoButton.setText(manager.getUndoPresentationName());... }

88 DESS ISIDIS / Un deuxième exemple Le programme de départ affiche, au clic de souris, un carré ou un cercle, selon que la touche majuscule nest pas ou est enfoncé. La séquence des formes engendrées est enregistrée dans un vecteur en vue dun affichage facile. Comment ladapter au undo/redo ? class SimplePaint extends JPanel { protected Vector formes = new Vector(); protected PaintCanvas canvas =new PaintCanvas(formes); protected int width = 50; protected int height = 50; public SimplePaint() { setLayout(new BorderLayout()); add(new Label("Do it", Label.CENTER), BorderLayout.NORTH); add(canvas, BorderLayout.CENTER); canvas.addMouseListener(new AjouterForme()); }... }

89 DESS ISIDIS / Les formes et le canvas class AjouterForme extends MouseAdapter { public void mousePressed(MouseEvent e) { Shape s; if (e.isShiftDown()) s = new Ellipse2D.Double(e.getX(), e.getY(), width, height); else s = new Rectangle2D.Double(e.getX(), e.getY(), width, height); formes.addElement(s); canvas.repaint(); } class PaintCanvas extends JPanel { Vector formes;... public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; super.paintComponent(g2); g2.setColor(Color.black); Enumeration enum = formes.elements(); while (enum.hasMoreElements()) { Shape shape = (Shape) enum.nextElement(); g2.draw(shape); }

90 DESS ISIDIS / Ajouter undo/redo (1) Comme pour lexemple précédent deux boutons « undo » et « redo » deux listeners dactions, un sur chaque bouton Création dune classe FormeEdit pour les transactions, et de deux classes dérivées.

91 DESS ISIDIS / Ajouter undo/redo (2) class FormeEdit extends AbstractUndoableEdit { protected Vector formes; protected Shape shape; public FormeEdit(Shape shape, Vector formes) { this.formes = formes; this.shape = shape; } public void undo() { super.undo(); formes.remove(shape); } public void redo() { super.redo(); formes.add(shape); } class CarreEdit extends FormeEdit { public CarreEdit(Shape s, Vector v) { super(s, v); } public String getPresentationName() { return "carré"; }

92 DESS ISIDIS / AjouterForme public void mousePressed(MouseEvent e) { Shape shape; UndoableEdit edit; if (e.isShiftDown()) { shape = new Ellipse2D.Double(e.getX(), e.getY(), width, height); edit = new CercleEdit(shape, formes); } else { shape = new Rectangle2D.Double(e.getX(), e.getY(), width, height); edit = new CarreEdit(shape, formes); } formes.addElement(shape); manager.addEdit(edit); canvas.repaint(); updateButtons(); }

93 DESS ISIDIS / Événements Il existe une classe spécifique UndoableEditEvent Un tel événement comporte une source, et un UndoableEdit Les auditeurs sont de linterface UndoableEditListener, avec la méthode undoableEditHappened(UndoableEditEvent e). UndoManager implémente UndoableEditListener, avec la méthode public void undoableEditHappened( UndoableEditEvent e){ addEdit( e.getEdit()) } Usage :... UndoableEdit edit;... edit = new CercleEdit(shape, formes); UndoableEditEvent ue; ue = new UndoableEditEvent(this, edit);... manager. undoableEditHappened(ue);...

94 DESS ISIDIS / Mais… Il manque une objet qui lance des UndoableEditEvent s. Limplémentation précédente fait comme si, la présente le fait. Seuls les documents de textes sont capables, pour linstant, den lancer. Lançons cela pour JPanel : class PaintCanvas extends JPanel {... Class uel = UndoableEditListener.class; public void addUndoableEditListener(UndoableEditListener listener) { listenerList.add(uel, listener); } public void removeUndoableEditListener(UndoableEditListener listener) { listenerList.remove(uel, listener); } public void fireUndoableEditUpdate(UndoableEditEvent e) { Object[] l = listenerList.getListenerList(); for (int i = l.length - 2; i >= 0; i -= 2) { if (l[i] == uel ) ((UndoableEditListener) l[i + 1]).undoableEditHappened(e); }

95 DESS ISIDIS / …et donc Un UndoManager est un listener parfait Il ne reste plus quà lancer les événements public FireUndo() {... canvas.addMouseListener(new AjouterForme()); canvas.addUndoableEditListener(manager); } public void mousePressed(MouseEvent e) { Shape shape; UndoableEdit edit; shape =... edit =... formes.addElement(shape); UndoableEditEvent ue = new UndoableEditEvent(this, edit); canvas.fireUndoableEditUpdate(ue); canvas.repaint(); updateButtons(); }

96 DESS ISIDIS / UndoableEditSupport Pour faciliter la vie aux programmeurs (en attendant que les choses se simplifient), Java propose une classe utilitaire de gestion de listeners et denvoi dévénements, les UndoableEditSupport. Ils réalisent pour lessentiel ce qui a été programmé en dur. class PaintCanvas extends JPanel { UndoableEditSupport support = new UndoableEditSupport();... public void addUndoableEditListener(UndoableEditListener listener) { support.addUndoableEditListener(listener); } public void removeUndoableEditListener(UndoableEditListener listener) { support.removeUndoableEditListener(listener); } public void postEdit(UndoableEdit e) {// le fireUndoableEditUpdate.... support.postEdit(e); }

97 DESS ISIDIS / UndoableEditSupport (fin) Au lieu de lancer les événements, on poste les Edit : public void mousePressed(MouseEvent e) { UndoableEdit edit; edit =... canvas.postEdit(edit);... }

98 DESS ISIDIS / Dans les textes Dans les textes, ça va tout seul. JTextArea editor = new JTextArea(); public UndoRedoText() { editor.getDocument().addUndoableEditListener(new ManageIt()); undoButton.addActionListener(new UndoIt()); redoButton.addActionListener(new RedoIt()); } class ManageIt implements UndoableEditListener { public void undoableEditHappened(UndoableEditEvent e) { manager.undoableEditHappened( e); updateButtons(); }

99 DESS ISIDIS / États Dans les exemples précédents, on conservait explicitement létat après modification. Un tel procédé nest pas suffisant dans de nombreuses situations, comme dans un groupe de boutons radio. Java propose une forme générale détat appelé StateEdit. Tout objet dont la classe implémente linterface StateEditable peut sauvegarder son état avant et après modification dans létat, et ainsi le récupérer. Mieux, la prise en compte de létat de départ et de létat darrivée peut être programmée, permettant ainsi de cumuler des modifications.

100 DESS ISIDIS / États : description Un StateEdit est créé par La classe de unObjet implémente linterface StateEditable. Un StateEdit contient en interne une table de hachage (en fait deux). Les méthodes de StateEditable permettent de sauvegarder et de récupérer les données à conserver. La sauvegarde débute à la création, et sarrête par la méthode end() de StateEdit. StateEdit etat = new StateEdit(unObjet); public void storeState( Hashtable h); public void restoreState( Hashtable h);

101 DESS ISIDIS / États : structure de lexemple class TogglePanel extends JPanel implements ActionListener, StateEditable { StateEdit etat; JButton undoButton, redoButton, chooseButton, endButton; JRadioButton gras, ital, soul; ButtonGroup polices; public TogglePanel() { // installer les composants; chooseButton.addActionListener(new ChooseIt()); endButton.addActionListener(new EndIt()); undoButton.addActionListener(new UndoIt()); redoButton.addActionListener(new RedoIt()); } public void storeState(Hashtable h) {...} public void restoreState(Hashtable h) {...} class ChooseIt implements ActionListener {...} class EndIt implements ActionListener {...} class UndoIt implements ActionListener {...} class RedoIt implements ActionListener {...} }

102 DESS ISIDIS / États : fin de lexemple Trois boutons radio sont donnés. A partir de Choose, on démarre lenregistrement des modifications, jusquà lactivation du bouton Ok. Un Undo restitue létat initial, et un Redo revient à létat final. class ChooseIt implements ActionListener { public void actionPerformed(ActionEvent ev) { etat = new StateEdit(TogglePanel.this); updateButtons(); } public void storeState(Hashtable h) { h. put(polices, polices.getSelection()); } public void restoreState(Hashtable h) { ButtonModel b = (ButtonModel) h.get(polices); b.setSelected(true); } class UndoIt implements ActionListener { public void actionPerformed(ActionEvent ev) { try { etat.undo(); } catch (CannotUndoException ex) {} updateB(); }

103 DESS ISIDIS / Le squelette dun éditeur de texte Objectifs Présenter des interactions dun composant de texte avec son environnement Ouverture et sauvegarde des fichiers Couper-coller Undo-Redo Limportant est la cohérence de lenvironnement Entrées des menus activables seulement si cela a un sens « Aide implicite » que cela apporte En revanche, on ignore le style du texte lui- même Style des paragraphes Polices de caractères

104 DESS ISIDIS / Cahier des charges Éditeur SDI (single document interface) un seul document présent une seule fenêtre de manipulation du texte Autres modèles une seule fenêtre, plusieurs documents plusieurs fenêtres, une par document Commandes de manipulation de documents nouveau, ouvrir, sauver, sauver sous Commandes de manipulation du texte copier-couper, coller, tout sélectionner annuler-rétablir Présentation de ces commandes sous forme menu - toolbar - raccourcis clavier

105 DESS ISIDIS / Textes et documents Classes de textes Classes de documents | +-- javax.swing JComponent | +-- javax.swing.text.JTextComponent | +-- javax.swing.JTextArea | +-- javax.swing.JTextField | +-- javax.swing.JEditorPane | +-- javax.swing.JTextPane java. lang. Object | +-- javax.swing.text.AbstractDocument implements Document | +-- javax.swing.text.PlainDocument | extends | +-- javax.swing.text.DefaultStyledDocument implements StyledDocument | +-- javax.swing.text.html.HTMLDocument

106 DESS ISIDIS / Document / Vue Un composant de texte présente une vue dun document. TextArea et TextField associés au PlainDocument TextPane associé à StyledDocument Correspondance JTextArea editor; Document document = editor.getDocument(); editor.setDocument(new PlainDocument());

107 DESS ISIDIS / Écouter le texte Via le document, insertion, suppression, remplacement Un DocumentListener implémente trois méthodes appelées après modification dun attribut, insertion, suppression. Document document = editor.getDocument(); document.addDocumentListener( un listener ); public void changedUpdate (DocumentEvent e); public void insertUpdate (DocumentEvent e); public void removeUpdate (DocumentEvent e);

108 DESS ISIDIS / Sélection On peut pister les déplacements du point dinsertion ( caret ) par un CaretListener Un CaretListener possède une méthode caretUpdate appelée chaque fois que le point dinsertion bouge Un CaretEvent fournit deux méthodes getDot() qui donne le point actuel getMark() qui donne le point précédent Un mouvement de souris, avec bouton enfoncé, ne provoque pas dévènement, mais provoque un évènement quand on relâche le bouton Caret caret = editor.getCaret(); caret.addCaretListener( un CaretListener ); public void caretUpdate( CaretEvent e) { int now = e. getDot(); int before = e. getMark(); boolean nowSelected = now != before;... }

109 DESS ISIDIS / Manipulations de texte Manipulations de texte prédéfinies (sont en fait des méthodes de JTextComponent ) : les dernières transfèrent dans le presse- papier système. Le DefaultEditorKit prédéfinit une trentaine dactions sur les composants de textes. void editor.cut(); void editor.copy(); void editor.paste(); void editor.selectAll(); Clipboard clip = Toolkit. getDefaultToolkit(). getSystemClipboard();

110 DESS ISIDIS / Vue densemble Le texte une seule zone de texte ( JTextArea ) le document associé ne change pas, sauf pour la commande « nouveau ». Les actions chaque action ( Action ) (nouveau,…, tout sélectionner) est implémentée dans une classe séparée Les menus et la barre doutils construits à partir des actions Les gestionnaires de cohérence de la cohérence des menu : une EditMenuManager de la cohérence des undo-redo : un UndoHandler de sauvegarde de documents modifiés : une StatusBar.

111 DESS ISIDIS / Composants Composants de la vue Composants de la gestion JTextComponent editor; JMenuBar menubar; JToolBar toolbar; StatusBar status; File currentFile = null; JFileChooser selecteurFichier; UndoHandler undoHandler; EditMenuManager editMenuManager;

112 DESS ISIDIS / Les Actions Une action… par action ! Action undoAction = new UndoAction(); Action redoAction = new RedoAction(); Action newAction = new NewAction(); Action openAction = new OpenAction(); Action saveAction = new SaveAction(); Action saveAsAction = new SaveAsAction(); Action exitAction = new ExitAction(); Action cutAction = new CutAction(); Action copyAction = new CopyAction(); Action pasteAction = new PasteAction(); Action selectAllAction = new SelectAllAction();

113 DESS ISIDIS / UndoAction Premier exemple : undoAction class UndoAction extends AbstractAction { public UndoAction() { super("Undo", new ImageIcon("gifs/undo.gif")); setEnabled(false); } public void actionPerformed(ActionEvent e) { try { undoHandler.undo(); } catch (CannotUndoException ex) {} undoHandler.update(); }

114 DESS ISIDIS / UndoHandler Il gère les undo, mais aussi létat des boutons ! class UndoHandler extends UndoManager { public void undoableEditHappened(UndoableEditEvent e) { super.addEdit(e.getEdit()); update(); } public void update() { undoAction.setEnabled(canUndo()); redoAction.setEnabled(canRedo()); }

115 DESS ISIDIS / CutAction Couper implique mettre dans la corbeille mettre à jour les boutons class CutAction extends AbstractAction { CutAction() { super("Cut", new ImageIcon("gifs/cut.gif")); } public void actionPerformed(ActionEvent e) { getEditor().cut(); // texte editMenuManager.doCut(); // boutons }

116 DESS ISIDIS / EditMenuManager Il gère les transitions entre les 4 états du menu la mise-à-jour de la vue (menu et toolbar) par la fonction update() class EditMenuManager implements CaretListener { int state; static final int EMPTY = 0, CUTCOPY = 1, PASTE = 2, FULL = 3; void doInitial() {...} void doCopy() {...} void doCut() {...} void doPaste() {...} void doSelected() {...} void doDeselected() {...} }

117 DESS ISIDIS / EditMenuManager (suite) Après une sélection : Après un copy : void doSelected() { if (state == EMPTY) state = CUTCOPY; else if (state == PASTE) state = FULL; updateEnables(state); } void doCopy() { if (state == CUTCOPY) { state = FULL; updateEnables(state); }

118 DESS ISIDIS / EditMenuManager (suite) Cest aussi un CaretListener, pour écouter les sélections public void caretUpdate(CaretEvent e) { int now = e.getDot(); int before = e.getMark(); boolean nowSelected = now != before; if (nowSelected) doSelected(); else doDeselected(); }

119 DESS ISIDIS / EditMenuManager (fin) La mise- à- jour des boutons est paresseuse public void updateEnables(int state) { switch (state) { case EMPTY : cutAction.setEnabled(false); copyAction.setEnabled(false); pasteAction.setEnabled(false); break; case CUTCOPY: cutAction.setEnabled(true); copyAction.setEnabled(true); pasteAction.setEnabled(false); break; case PASTE:... case FULL:... }

120 DESS ISIDIS / Ouvrir un fichier Il faut sassurer que le fichier courant nest pas modifié sil est modifié, demander une éventuelle sauvegarde ouvrir un dialogue de choix de fichier lire ce fichier Ces opérations sont assumées par la méthode actionPerformed() class OpenAction extends AbstractAction { OpenAction() { super("Ouvrir...", new ImageIcon("gifs/open. gif")); } public void actionPerformed(ActionEvent e) {...} }

121 DESS ISIDIS / Ouvrir un fichier (suite) public void actionPerformed(ActionEvent e) { if (!isConfirmed( "Voulez vous sauver le texte courant\ n"+ " avant d'ouvrir un autre fichier ?", "Sauver avant d'ouvrir ?")) return; int answer = selecteurFichier.showOpenDialog(frame); if (answer != JFileChooser.APPROVE_ OPTION) return; currentFile = selecteurFichier.getSelectedFile(); try { FileReader in = new FileReader(currentFile); getEditor().read(in, null); in.close(); } catch (IOException ex) { ex.printStackTrace(); } status.setSaved(); frame.setTitle(currentFile.getName()); }

122 DESS ISIDIS / Ouvrir un fichier (fin) boolean isConfirmed(String question, String titre) { if (! status.isModified()) return true; int reponse = JOptionPane.showConfirmDialog(null, question, titre, JOptionPane.YES_ NO_ CANCEL_ OPTION); switch(reponse) { case JOptionPane.YES_ OPTION:{ saveAction.actionPerformed(null); return !status.isModified(); } case JOptionPane.NO_ OPTION: return true; case JOptionPane.CANCEL_ OPTION: return false; } return false; }

123 DESS ISIDIS / État du document Il nexiste pas de fonction qui indique une modification du document StatusBar assume ce rôle… class StatusBar extends JPanel implements DocumentListener { boolean modStatus = false; // true = modified; public boolean isModified() { return modStatus; } public void changedUpdate(DocumentEvent ev) { setModified();} public void insertUpdate(DocumentEvent ev) { setModified();} public void removeUpdate(DocumentEvent ev) { setModified();} public void setSaved() { modStatus = false; getEditor().getDocument().addDocumentListener(this); saveAction.setEnabled(false); } public void setModified() { modStatus = true; getEditor().getDocument().removeDocumentListener(this); saveAction.setEnabled(true); }

124 DESS ISIDIS / Les menus Dans le menu « Fichier », on ajoute des raccourcis protected JMenuBar createMenubar() { JMenuBar mb = new JMenuBar(); JMenu menu; JMenuItem item; menu = new JMenu("Fichier"); item = menu. add(newAction); item.setIcon(null); item.setMnemonic('N'); item = menu. add(openAction); item.setIcon(null); item.setMnemonic('O');... menu.addSeparator(); item = menu.add(exitAction); mb. add(menu);... return mb; }

125 DESS ISIDIS / La barre doutils On ajoute les tooltips, des espaces et de la glue private JToolBar createToolbar() { JButton b; JToolBar tb = new JToolBar(); b = tb.add(newAction); b.setText(null); b.setToolTipText("nouveau");... tb.add(Box.createHorizontalStrut(10)); b = tb.add(copyAction); b.setText(null); b.setToolTipText("copier");... tb.add(Box.createHorizontalGlue()); return tb; }


Télécharger ppt "JAVA Avancé DESS ISIDIS Année 2003/2004 Jean-Christophe Soulié"

Présentations similaires


Annonces Google