Java nous permet de doter une fenêtre de menus déroulants. Comme dans la plupart des applications du commerce, nous disposons de deux possibilités complémentaires :
- Créer une barre de menus qui s'affiche en haut de la fenêtre, et dans laquelle chaque menu pourra faire apparaître une liste d'options.
- Faire apparaître à un moment donné ce que nous nommons un menu surgissant (ou menu contextuel), formé quant à lui d'une seule liste d'options spécifiques correspondantes à l'endroit où se situe la demande.
Nous commencerons par la première possibilité, ce qui nous permettra d'exposer les principes généraux de construction de menus et d'exploitation des événements correspondants. Puis nous verrons comment utiliser des options de menus se présentant sous la forme de cases à cocher ou de boutons radio. Nous aborderons ensuite le cas particulier des menus surgissants.
Nous apprendrons à accéder à une option de menu par le biais de lettres mnémoniques ou de combinaisons de touches nommés accélérateurs. Nous verrons également comment éclairer l'utilisateur sur le rôle d'une option par une bulle d'information. Nous apporterons ensuite quelques précisions concernant la dynamique des menus, c'est-à-dire l'activation ou la désactivation d'une option lors de l'exécution, ou encore l'introduction ou la suppression d'options.
Enfin, nous montrerons comment la notion d'action facilite la réalisation de code dans lesquels une même action peut être provoquée de différentes manières par l'utilisateur.
Une barre de menu située au sommet de la fenêtre contient les noms des menus déroulants, il suffit de cliquer dessus pour les ouvrir, ce qui fait apparaître des options de menu et de sous-menus. Lorsque l'utilisateur clique sur une option de menu, tous les menus sont fermés et un message est envoyé au programme.
Un JMenu est un menu déroulant standard dont le nom est fixé. Les menus peuvent contenir d'autres menus en tant qu'éléments de sous-menu, ce qui permet de mettre en place des structures de menus complexes. Nous pouvons les placer à tout endroit destiné à un composant. Une autre classe, JMenuBar, héberge des menus dans une barre horizontale. Les barres de menu sont elles aussi de vrais composants, que vous pouvez donc placer à tout endroit d'un conteneur : en haut, en bas ou au milieu. Mais au milieu d'un conteneur, on placera généralement plutôt un JComboBox qu'un menu quelconque.
Ces menus déroulants usuels font donc intervenir trois sortes d'objets :
Les options d'un menu, appelé aussi éléments, peuvent être associés à des images et des raccourcis clavier ; il existe même des éléments de menu qui ressemblent à des cases à cocher ou des boutons radio. Les éléments d'un menu sont une sorte de bouton : comme les boutons, ils déclenchent des événements d'action lorsqu'ils sont sélectionnés. Il est alors possible de répondre aux éléments d'un menu en enregistrant des écouteurs d'action avec eux.
La création de menus est une opération assez simple. Voici la séquence à respecter avec ses différentes phases :
JMenuBar barre = new JMenuBar();
Une barre de menus est un simple composant que vous ajouter n'importe où. Le plus souvent, elle est placée au sommet d'un cadre de fenêtre. Pour cela, la méthode setJMenuBar() doit être appelée :
JFrame fenêtre = new JFrame();
fenêtre.setJMenuBar(barre);
Les différents objets menus sont créés par appel d'un constructeur de JMenu, auquel nous fournissons le nom d'un menu, tel qu'il figurera dans la barre. Ensuite, chaque objet menu est ajouté à la barre au travers de la méthode add() de la classe JMenuBar :
JMenu édition = new JMenu("Edition");
barre.add(édition);
Enfin, les différentes options d'un menu sont créées par appel d'un constructeur de JMenuItem, auquel nous fournissons, là encore, le nom de l'option telle qu'elle apparaîtra lorsque l'utilisateur lancera l'affichage du menu déroulant. Chaque option est ajoutée à un menu au travers de la méthode add() de la classe JMenu :
JMenuItem couper = new JMenuItem("Couper");
édition.add(couper);
JMenuItem copier = new JMenuItem("Copier");
édition.add(copier);
JMenuItem coller = new JMenuItem("Coller");
édition.add(coller);
édition.addSeparator();
JMenu option = new JMenu("Option");
édition.add(option);
Comme pour la barre d'outils, il est possible de prévoir un séparateur dans votre menu déroulant. ceci est réalisé au travers de la méthode addSeparator() de JMenu.
Remarquez bien qu'un menu peut lui-même contenir un autre menu, qui devient alors un sous-menu. Il suffit, tout simplement, de proposer le nouveau menu au travers de la méthode add() de JMenu comme nous l'avons fait dans l'exemple précédent.
import javax.swing.*; import java.awt.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu fichier = new JMenu("Fichier"); private JMenu édition = new JMenu("Edition"); private JMenuItem ouvrir = new JMenuItem("Ouvrir"); private JMenuItem enregistrer = new JMenuItem("Enregistrer"); private JMenuItem quitter = new JMenuItem("Quitter"); private JMenuItem couper = new JMenuItem("Couper"); private JMenuItem copier = new JMenuItem("Copier"); private JMenuItem coller = new JMenuItem("Coller"); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(fichier); barre.add(édition); fichier.add(ouvrir); fichier.add(enregistrer); fichier.addSeparator(); fichier.add(quitter); édition.add(couper); édition.add(copier); édition.add(coller); getContentPane().setBackground(Color.YELLOW); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new Menus(); } }
Lorsqu'un utilisateur sélectionne un menu, un événement de type Action est déclenché. Nous connaisons déjà bien cette technique. Rappelez-vous qu'il s'agit d'installer un écouteur d'action à chaque option de menu pour prendre en compte tous les fonctionnalités désirées.
Ainsi, l'action de sélection d'une option (JMenuItem) génère un événement de type Action que nous pouvons traiter en associant un écouteur ActionListener à l'objet correspondant au travers de la méthode addActionListener(). Dans la méthode actionPerformed() de cet écouteur, l'option concernée pourra être identifiée classiquement au moyen de la méthode getSource() de la classe ActionEvent.
Nous pourront aussi, le cas échéant, recourir à la méthode getActionCommand() de la même classe ActionEvent, qui comme pour un bouton, fournit la chaîne de commande associée à l'option. Par défaut, il s'agit tout simplement du nom de l'option (fournie au constructeur de JMenuItem) mais, là encore, celle-ci pourrait éventuellement être modifiée en se servant de la méthode setActionCommand() de la classe JMenuItem.
Je rappelle que cette méthode setActionCommand() est issue de la classe abstraite AbstractButton qui est utilisée, par héritage, aussi bien par la classe JButton que JMenuItem.
Pour connaître plus précisément la classe AbstractButton, repportez-vous sur l'étude suivante : Sélection et choix.
§
Une possibilité intéressante, qui permet de traiter la fonctionnalité requise de façon personnalisée pour chaque option, est de proposer une classe anonyme au moment de l'appel de la méthode addActionListener(), et qui gère ainsi l'événement spécifique à chaque objet JMenuItem.
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JMenuItem couper = new JMenuItem("Couper"); private JMenuItem copier = new JMenuItem("Copier"); private JMenuItem coller = new JMenuItem("Coller"); private JTextArea éditeur = new JTextArea(); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); couper.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(couper); copier.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(copier); coller.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); édition.add(coller); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new Menus(); } }
Nous n'avons parlé que des événements générés par les options elles-mêmes. En toute rigueur, les menus (JMenu) génèrent des événements de la catégorie MenuEvent lors de leur affichage ou lors de leur disparition. L'écouteur correspondant est ajouté par addMenuListener() et il doit alors implémenter l'interface MenuListener contenant les trois méthodes (il n'y a pas d'adaptateur) : menuSelected(), menuDeselected() et menuCanceled(). En pratique, ces possibilités sont peu utilisées.
Il existe une méthode bien pratique de JMenu, qui s'appelle également add(), mais qui attend une chaîne de caractères au lieu d'un objet de type JMenuItem. Cette méthode ajoute tout simplement une option à la fin du menu, donc également un JMenuItem qui porte le même nom que l'argument proposé.
JMenu édition = new JMenu("Edition");
édition.add("Couper");
L'avantage de cette technique, c'est qu'en une seule ligne, nous créons l'option, et nous l'ajoutons, en même temps, au menu déroulant. De plus, cette méthode add() renvoie l'option de menu créée (comme l'autre méthode add() d'ailleurs), afin que vous puissiez la capturer et lui associer un écouteur :
JMenuItem couper = édition.add("Couper");
couper.addActionListener(écouteur);
Grâce à cette technique, nous pouvons même proposer trois traitements spécifiques sur une seule ligne de code :
JMenu édition = new JMenu("Edition");
édition.add("Couper").addActionListener(écouteur);
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); édition.add("Couper").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add("Copier").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add("Coller").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new Menus(); } }
Il est possible de faire figurer à côté du nom d'options un petit pictogramme que nous nommons souvent une icône. En réalité, les options de menu sont très semblables aux boutons, puisque dans les deux cas, JButton et JMenuItem héritent de la classe abstraite AbstractButton. Comme les boutons, les menus peuvent donc contenir un libellé, une icône, ou les deux.
Cette possibilité n'existe que pour les options de type JMenuItem ; elle n'est donc pas disponible pour les boutons radio ou les cases à cocher.
.
Vous pouvez spécifier une icône à l'aide des constructeurs suivant :
Nous avons aussi la possiblité de proposer une icône à une option de menu ultérieurement, après sa création, à l'aide de la méthode setIcon() dont la classe JMenuItem hérite de la classe AbstractButton.
Pour connaître plus précisément la classe AbstractButton, repportez-vous sur l'étude suivante : Sélection et choix.
§
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); édition.add(new JMenuItem("Couper", new ImageIcon("couper.gif"))).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new JMenuItem("Copier", new ImageIcon("copier.gif"))).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new JMenuItem("Coller", new ImageIcon("coller.gif"))).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new Menus(); } }
Le texte des options est placé par défaut à droite de l'icône. Si vous préférez que le texte soit placé à gauche, appelez la méthode setHorizontalTextPosition() qui, encore une fois, est issue de la classe AbstractButton, avec la constante suivante SwingConstants.LEFT. Dans le même ordre d'idée, il est possible de régler l'écartement qui existe entre l'icône et le libellé de l'option au moyen de la méthode setIconTextGap(int).
Les utilisateurs expérimentés préfèrent souvent sélectionner une option de menu par le biais d'un caractère mnémonique, plutôt que d'utiliser la souris, dans le cas où l'utilisateur a déjà les mains sur le clavier. Le caractère mnémonique est un caractère (unique) qui apparaît sous la forme d'un soulignement de la lettre concernée dans le nom du menu ou de l'option. Ainsi :
JMenu édition = new JMenu("Edition");
édition.setMnemonic('E');
JMenuItem couper = new JMenuItem("Couper");
couper.setMnemonic('p');
JMenuItem copier = new JMenuItem("Copier");
copier.setMnemonic('i');
JMenuItem coller = new JMenuItem("Coller");
coller.setMnemonic('l');
Nous pouvons aussi préciser le caractère mnémonique lors de la phase de construction. Attention toutefois, vous ne pouvez spécifier de caractère mnémonique que dans les constructeurs d'une option de menu (JMenuItem), et non dans le constructeur d'un menu (JMenu). Dans ce dernier cas, la seule possiblité reste l'utilisation de la méthode setMnemonic() :
JMenu édition = new JMenu("Edition");
édition.setMnemonic('E');
JMenuItem couper = new JMenuItem("Couper", 'p');
JMenuItem copier = new JMenuItem("Copier", 'i');
JMenuItem coller = new JMenuItem("Coller", 'l');
Notez que java ne vérifie pas si le caractère mnémonique mentionné appartient bien au nom du menu. Lorsque la lettre utilisé ne fait pas partie du libellé, elle n' apparaît pas dans le menu, mais son activation permet néanmoins de sélectionner l'option. On peut bien entendu douter de l'utilité de caractères mnémoniques invisibles.
Par ailleurs, si plusieurs options d'un même menu se voient attribuer le même caractères mnémonique, seul le premier sera exploitable par ce biais.
§
Parfois, vous ne souhaiterez pas souligner la première letttre de l'élément de menu correspondant au caractère mnémonique. Si vous avez, par exemple, un 'E' mnémonique pour l'option de menu "Enregistrer", vous pourriez souligner le deuxième 'e'. Vous pouvez ainsi spécifier le caractère qui sera souligné en appelant la méthode setDisplayedMnemonicIndex(int).
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); édition.setMnemonic('E'); édition.add(new JMenuItem("Couper", 'p')).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new JMenuItem("Copier", 'i')).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new JMenuItem("Coller", 'l')).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new Menus(); } }
Les touches ménmoniques permettent de sélectionner une option ou un sous-menu à partir d'un menu déjà ouvert. Les combinaisons de touches ou accélérateurs sont des raccourcis clavier qui permettent de sélectionner des options sans avoir à ouvrir de menu. Par exemple, de nombreux programmes associent les combinaisons <Ctrl+X>, <Crtl+C> et <Ctrl+V> aux options Couper, Copier et Coller du menu Edition.
Un accélérateur est donc nécessairement une combinaison de touches que nous associons à une option de menu (jamais à un menu) et qui s'affiche à droite de son nom. Il suffit que l'utilisateur frappe cette combinaison de touches pour provoquer la sélection de l'option correspondante et ce, indépendamment de ce qui s'affiche dans la fenêtre à ce moment là.
JMenuItem couper = new JMenuItem("Couper");
couper.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK));
JMenuItem copier = new JMenuItem("Copier");
copier.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
JMenuItem coller = new JMenuItem("Coller");
coller.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK));
JMenuItem collageSpécial = new JMenuItem("Collage spécial...");
collageSpécial.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK));
Il existe également une méthode getKeyStroke(String) de la classe KeyStroke qui possède une seul paramètre de type chaîne de caractères. Cette méthode est capable d'analyser le texte proposer et donne le raccourci clavier équivalent. L'avantage de cette méthode c'est qu'elle est beaucoup plus concise que la précédente. Par contre, il faut bien respecter la syntaxe prévue à cet effet. Il faut d'abord préciser le ou les modificateurs (shift | control | ctrl | meta | alt | altGraph) et ensuite directement le nom de la touche concernée (en majuscule) . Voici quelques exemples qui illustrent bien la syntaxe à suivre :
- "INSERT" => getKeyStroke(KeyEvent.VK_INSERT, 0);
-
"control DELETE" => getKeyStroke(KeyEvent.VK_DELETE, InputEvent.CTRL_MASK);
- "alt shift X" => getKeyStroke(KeyEvent.VK_X, InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);
Vous ne pouvez associer de raccourcis clavier qu'aux options de menu, et non aux menus. Les raccourcis clavier n'ouvrent pas le menu. Ils déclenchent directement l'événement d'action associé à l'option de menu.
D'un point de vue conceptuel, l'ajout d'un raccourci clavier à une option de menu est semblable à la technique d'ajout d'un raccourci à un composant Swing. Cependant, lorsque le raccourci clavier est ajouté à une option de menu, la combinaison de touches est automatiquement affichée en regard de l'option.
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); édition.setMnemonic('E'); édition.add(new Option("Couper", 'p', 'X')).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new Option("Copier", 'i', 'C')).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new Option("Coller", 'l', 'V')).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class Option extends JMenuItem { public Option(String intitulé, char mnémonique, char raccourci) { super(intitulé, new ImageIcon(intitulé.toLowerCase()+".gif")); setMnemonic(mnémonique); setAccelerator(KeyStroke.getKeyStroke("control "+raccourci)); } } public static void main(String[] args) { new Menus(); } }
Dans la plupart des applications professionnelle, un petit rectangle (nommé tooltip en anglais) contenant un bref texte explicatif apparaît lorsque vous laissez un instant la souris sur certains composants (boutons, menus, ...). Java vous permet d'obtenir un tel affichage pour n'importe quel composant. Il vous suffit pour cela de lui associer le texte désiré à l'aide de la méthode setToolTipText() qui existe depuis la classe JComponent.
Par ailleurs, dans les exemples précédents, un menu était formé d'une simple liste d'options. En fait, dans de nombreuses applications, une option peut à son tour faire apparaître une liste de sous-options. Pour obtenir ce résultat avec Java, il vous suffit d'utiliser dans un menu une option qui soit non plus du type JMenuItem, mais de type JMenu (comme le menu lui-même). Vous pouvez alors rattacher à ce sous-menu les options de votre choix. La démarche peut être répétée autant de fois que vous voulez.
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); private JMenu option = new JMenu("Option"); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); option.setMnemonic('O'); option.add(new Option("Lecture seule", "Empêche la saisie de l'utilisateur")); option.addSeparator(); option.add(new Option("Mode insertion", "Le texte s'insère à l'endroit du curseur")); option.add(new Option("Mode suppression", "Le texte saisie remplace celui existant")); édition.setMnemonic('E'); édition.add(new Option("Couper", 'X', "Enlève le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new Option("Copier", 'C', "Copie le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new Option("Coller", 'V', "Ajoute le texte copier")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); édition.addSeparator(); édition.add(option); <----------------------------- éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class Option extends JMenuItem { public Option(String intitulé, char raccourci, String aide) { this(intitulé, aide); setAccelerator(KeyStroke.getKeyStroke("control "+raccourci)); } public Option(String intitulé, String aide) { super(intitulé, new ImageIcon(intitulé.toLowerCase()+".gif")); setToolTipText(aide); } } public static void main(String[] args) { new Menus(); } }
Jusqu'à présent, nous avons présenté uniquement les options de type JMenuItem qui sont, il est vrai, les plus usuelles. Nous avons aussi la possibilité d'utiliser dans un menu :
Lorsque l'utilisateur sélectionne l'une de ces options, elle passe à l'état activé ou désactivée, selon son état initial.
§
Comme les boutons radio classiques, les options boutons radio fonctionnent de façon standard. Ainsi, vous devez les inclure dans un groupe (objet de type ButtonGroup) de manière à assurer l'unicité de la sélection à l'intérieur du groupe. Dans cette infrastructure, quand l'un des boutons est choisi, tous les autres sont automatiquement désactivés.
Les événements générés par ces nouvelles options sont les mêmes types que ceux générés par les boutons correspondants. Autrement dit :
Nous remarquons que pour les options bouton radio, l'événement Item prend une signification différente de l'événement Action puisqu'il permet de cerner les changements d'états.
Pour en savoir plus sur les boutons radio classiques et leurs événements assosiés.
Pour en savoir plus sur les cases à cocher classiques et leurs événements associés.
Lorsque vous utilisez ce type d'option, vous n'avez généralement pas besoin d'être informé du moment exact où l'utilisateur effectue sa sélection. Vous pouvez alors simplement employer la méthode isSelected() (de la classe JRadioButtonMenuItem ou JCheckBoxMenuItem) pour tester l'état actuel d'une option (cela implique bien sûr que vous conserviez une référence sur l'option de menu dans un attribut de la classe fenêtre. Vous pouvez également utiliser la méthode setSelected() pour définir l'état d'une option.
On nottera effectivement que les options usuelles d'un menu devaient obligatoirement être traitées au moment de leur sélection. En revanche, avec les options cases à cocher ou boutons radio, nous disposons de plus de liberté. Nous pouvons, en effet, les traiter comme des options usuelles mais nous pouvons aussi nous contenter de s'intéresser à leur état à un moment donné.
Les constructeurs disponibles sont exactement de même natures que ceux que nous avons déjà exploités pour les cases à cocher et les boutons radio. Voici donc ci-dessous la liste des constructeurs qui sont à votre disposition et qui vous offrent énormément de possibilité :
JCheckBoxMenuItem()
JCheckBoxMenuItem(Action action)
JCheckBoxMenuItem(String libellé)
JCheckBoxMenuItem(Icon icône)
JCheckBoxMenuItem(String libellé, Icon icône)
JCheckBoxMenuItem(String libellé, boolean sélectionné)
JCheckBoxMenuItem(Icon icône, boolean sélectionné)
JCheckBoxMenuItem(String libellé, Icon icône, boolean sélectionné)
JRadioButtonMenuItem()
JRadioButtonMenuItem(Action action)
JRadioButtonMenuItem(String libellé)
JRadioButtonMenuItem(Icon icône)
JRadioButtonMenuItem(String libellé, Icon icône)
JRadioButtonMenuItem(String libellé, boolean sélectionné)
JRadioButtonMenuItem(Icon icône, boolean sélectionné)
JRadioButtonMenuItem(String libellé, Icon icône, boolean sélectionné)
Nous reprenons l'application précédente à laquelle nous rajoutons des fonctionnalités supplémentaires. Il est ainsi possible de bloquer l'éditeur pour soit momentanément en lecture seule. Par ailleurs, nous avons la possibilité de choisir la couleur du texte (uniquement trois valeurs proposées) :
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); private JMenu option = new JMenu("Option"); private ButtonGroup groupe = new ButtonGroup(); private JCheckBoxMenuItem lecture = new JCheckBoxMenuItem("Lecture seule"); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); option.setMnemonic('O'); lecture.setToolTipText("Empêche la saisie de l'utilisateur"); option.add(lecture).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.setEditable(!lecture.isSelected()); } }); option.addSeparator(); option.add(new OptionRadioBouton("Noir", Color.BLACK, true)); option.add(new OptionRadioBouton("Rouge", Color.RED, false)); option.add(new OptionRadioBouton("Bleu", Color.BLUE, false)); édition.setMnemonic('E'); édition.add(new Option("Couper", 'X', "Enlève le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new Option("Copier", 'C', "Copie le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new Option("Coller", 'V', "Ajoute le texte copier")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); édition.addSeparator(); édition.add(option); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class Option extends JMenuItem { public Option(String intitulé, char raccourci, String aide) { super(intitulé, new ImageIcon(intitulé.toLowerCase()+".gif")); setAccelerator(KeyStroke.getKeyStroke("control "+raccourci)); setToolTipText(aide); } } private class OptionRadioBouton extends JRadioButtonMenuItem implements ActionListener { private Color couleur; public OptionRadioBouton(String libellé, Color couleur, boolean actif) { super(libellé, actif); this.couleur = couleur; setToolTipText("Couleur du texte de l'éditeur"); groupe.add(this); addActionListener(this); } public void actionPerformed(ActionEvent e) { éditeur.setForeground(couleur); } } public static void main(String[] args) { new Menus(); } }
Nous venons de voir comment utiliser des menus usuels, c'est-à-dire rattachés à une barre de menus et donc affichés en permanence dans la fenêtre. Java vous permet également d'utiliser ce que nous appelons des menus surgissants, c'est-à-dire des menus (sans nom) dont la liste d'options apparaît suite à une certaine action de l'utilisateur, en général un clic sur le bouton droit de la souris.
Ces menus ne sont pas attachés à une barre de menu, mais flotte à l'intérieur d'une fenêtre à l'endroit du clic de la souris et proposent juste les options nécessaires correspondantes à la situation actuelle. Dans ce cadre là, ces menus sont aussi appelés des menus contextuels.
Pour ce faire, il vous suffit de créer un objet de type JPopupMenu, auquel vous rattachez des objets de type JMenuItem, exactement comme vous l'auriez fait avec un objet de type JMenu.
JPopupMenu édition = new JPopupMenu();
JMenuItem couper = new JMenuItem("Couper", new ImageIcon("couper.gif"));
couper.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK));
couper.setMnemonic('p');
JMenuItem copier = new JMenuItem("Copier", new ImageIcon("copier.gif"));
copier.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
copier.setMnemonic('i');
JMenuItem coller = new JMenuItem("Coller", new ImageIcon("coller.gif"));
coller.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK));
coller.setMnemonic('l');
édition.add(couper);
édition.add(copier);
édition.add(coller);
Nous pouvons, bien sûr, utiliser des options cases à cocher ou des options boutons radio. Toutes les techniques que nous avons mises en oeuvre dans la classe JMenu s'appliquent en totalité dans la classe JPopupMenu.
Contrairement à la barre de menu standard qui apparaît toujours au sommet d'un cadre, un menu contextuel doit être explicitement affiché à l'aide de la méthode show() de la classe JPopupMenu. Vous devez alors spécifier le composant parent et la position du menu contextuel en fonction du système de coordonnées du parent :
JTextArea éditeur = new JTextArea();
édition.show(éditeur, x, y);
Le menu restera affiché jusqu'à ce que l'utilisateur sélectionne une option ou encore qu'il ferme le menu en cliquant à côté. Cette méthode show() est intéressante, toutefois, il est nécessaire d'élaborer une gestion d'événements adaptée afin de préciser les coordonnées de la souris.
JPopupMenu édition = new JPopupMenu();
JTextArea éditeur = new JTextArea();
éditeur.setComponentPopupMenu(édition);
Très occasionnellement, vous pouvez placer un composant dans un autre qui dispose d'un menu contextuel. Le composant enfant peut hériter du menu contextuel du composant parent en appelant la méthode setInheritsPopuMenu(true).
Les événements générés par les options d'un menu surgissant restent les mêmes que ceux que nous avons déjà rencontrés : Action et éventuellement Item.
A l'instar des menus usuels, les menus surgissants génèrent des événements lors de leur affichage ou de leur disparition. Cette fois, il s'agit d'événements de la catégorie PopupMenuEvent. L'écouteur correspondant est ajouté par la méthode addPopupMenuListener(). Il doit alors implémenter l'interface PopupMenuListener, comportant les méthodes popupMenuWillBecomeVisible(), popupMenuWillBecomeInvisible() et popupMenuCanceled().
Reprenons l'éditeur précédent. Cette fois-ci, il ne possèdera plus la barre de menu. A la place, un menu contextuel, avec exactement les mêmes options, apparaîtra lorsque l'utilisateur cliquera avec le bouton droit de la souris dans la zone d'édition :
import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Menus extends JFrame { private JPopupMenu édition = new JPopupMenu(); private JTextArea éditeur = new JTextArea(); private JMenu option = new JMenu("Option"); private ButtonGroup groupe = new ButtonGroup(); private JCheckBoxMenuItem lecture = new JCheckBoxMenuItem("Lecture seule"); public Menus() { super("Les menus"); éditeur.setComponentPopupMenu(édition); option.setMnemonic('O'); lecture.setToolTipText("Empêche la saisie de l'utilisateur"); option.add(lecture).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.setEditable(!lecture.isSelected()); } }); option.addSeparator(); option.add(new OptionRadioBouton("Noir", Color.BLACK, true)); option.add(new OptionRadioBouton("Rouge", Color.RED, false)); option.add(new OptionRadioBouton("Bleu", Color.BLUE, false)); édition.add(new Option("Couper", 'X', "Enlève le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new Option("Copier", 'C', "Copie le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new Option("Coller", 'V', "Ajoute le texte copier")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); édition.addSeparator(); édition.add(option); éditeur.setBackground(Color.YELLOW); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class Option extends JMenuItem { public Option(String intitulé, char raccourci, String aide) { super(intitulé, new ImageIcon(intitulé.toLowerCase()+".gif")); setAccelerator(KeyStroke.getKeyStroke("control "+raccourci)); setToolTipText(aide); } } private class OptionRadioBouton extends JRadioButtonMenuItem implements ActionListener { private Color couleur; public OptionRadioBouton(String libellé, Color couleur, boolean actif) { super(libellé, actif); this.couleur = couleur; setToolTipText("Couleur du texte de l'éditeur"); groupe.add(this); addActionListener(this); } public void actionPerformed(ActionEvent e) { éditeur.setForeground(couleur); } } public static void main(String[] args) { new Menus(); } }
Dans les exemples précédents, les menus (usuels ou surgissants) étaient créés une fois pour toute, de sorte qu'ils présentaient toujours les mêmes options et que celles-ci étaient toujours actives. En fait, Java vous permet :
Il arrive qu'une option de menu spécifique ne peut être sélectionnée que dans certains contextes. Par exemple, lorsqu'un document se trouve en lecture seule, les options "Couper" et "Coller" n'ont aucunes utilité. Bien sûr, nous pourrions les supprimer du menu à l'aide de la méthode remove() de JMenu, mais les utilisateurs seraient désorientés de voir son contenu changer ainsi. Par conséquent, il vaut mieux désactiver les options pouvant donner lieu à des commandes inappropriées. Une option de menu désactivée apparaît grisée et ne peut pas être sélectionnée.
JMenuItem coller = new JMenuItem("Coller");
coller.setEnabled(true);
Il existe en fait deux façons de procéder :
La méthode menuSelected() est appelée avant que le menu ne soit affiché. Par conséquent, elle peut être utilisée pour activer ou désactiver une option.
Attention : Désactiver des éléments de menu juste avant l'affichage du menu est une idée brillante, mais cela ne fonctionne pas pour ceux qui disposent aussi de touches accélérateur. Le menu n'étant jamais ouvert à la pression de la touche accélérateur, l'action n'est donc jamais désactivée ; elle est toujours déclenchée par la touche accélérateur.
En fait, nous verrons dans le chapitre suivant que les objets de type AbstractAction fourniront une solution plus élégante et plus générale : il suffira d'activer l'action pour activer toutes les options associées.
En pratique, cette seconde possiblité est rarement utilisée et ce pour d'évidentes raisons ergonomiques. En effet, il n'est guère appréciable pour l'utilisateur de voir les options d'un menu apparaître au fil de l'exécution. Il est beaucoup plus raisonnable de se limiter aux possibilités d'activation et de désactivation exposées précédemment.
A titre indicatif, sachez que vous disposez (aussi bien JMenu que pour JPopupMenu) des méthodes insert() et remove(), respectivement pour introduire de nouvelles options ou pour les supprimer par la suite.
Nous allons reprendre notre éditeur avec sa barre de menu. Les options "Couper" et "Coller" pourront être désactivées lorsque le document se trouvera en lecture seule.
import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.event.*; public class Menus extends JFrame implements MenuListener { private JMenuBar barre = new JMenuBar(); private JMenu édition = new JMenu("Edition"); private JTextArea éditeur = new JTextArea(); private JMenu option = new JMenu("Option"); private ButtonGroup groupe = new ButtonGroup(); private JCheckBoxMenuItem lecture = new JCheckBoxMenuItem("Lecture seule"); public Menus() { super("Les menus"); setJMenuBar(barre); barre.add(édition); option.setMnemonic('O'); lecture.setToolTipText("Empêche la saisie de l'utilisateur"); option.add(lecture).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.setEditable(!lecture.isSelected()); } }); option.addSeparator(); option.add(new OptionRadioBouton("Noir", Color.BLACK, true)); option.add(new OptionRadioBouton("Rouge", Color.RED, false)); option.add(new OptionRadioBouton("Bleu", Color.BLUE, false)); édition.setMnemonic('E'); édition.add(new Option("Couper", 'X', "Enlève le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.cut(); } }); édition.add(new Option("Copier", 'C', "Copie le texte")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.copy(); } }); édition.add(new Option("Coller", 'V', "Ajoute le texte copier")).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.paste(); } }); édition.addSeparator(); édition.add(option); éditeur.setBackground(Color.YELLOW); édition.addMenuListener(this); add(new JScrollPane(éditeur)); setSize(300, 200); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private class Option extends JMenuItem { public Option(String intitulé, char raccourci, String aide) { super(intitulé, new ImageIcon(intitulé.toLowerCase()+".gif")); setAccelerator(KeyStroke.getKeyStroke("control "+raccourci)); setToolTipText(aide); } } private class OptionRadioBouton extends JRadioButtonMenuItem implements ActionListener { private Color couleur; public OptionRadioBouton(String libellé, Color couleur, boolean actif) { super(libellé, actif); this.couleur = couleur; setToolTipText("Couleur du texte de l'éditeur"); groupe.add(this); addActionListener(this); } public void actionPerformed(ActionEvent e) { éditeur.setForeground(couleur); } } public static void main(String[] args) { new Menus(); } public void menuSelected(MenuEvent e) { édition.getItem(0).setEnabled(!lecture.isSelected()); édition.getItem(2).setEnabled(!lecture.isSelected()); } public void menuDeselected(MenuEvent e) { } public void menuCanceled(MenuEvent e) { } }
Contexte : La classe JToolBar possède aussi une méthode spéciale add() pour ajouter un objet de type Action au lieu d'un composant. Un JButton approprié est alors automatiquement créé, qui suit l'état activé de l'action pour que si l'action est désactivée, le bouton le soit aussi. La classe JMenu a la même possibilité d'accepter des objets Action pour les enfants. L'intérêt c'est que les actions fréquemment utilisées apparaissent souvent à la fois dans les composants JMenu et JToolBar, mais ces actions sont décrites une seule fois pour ces deux types de conteneurs.
Il existe souvent plusieurs manières d'activer une même commande. L'utilisateur peut effectivement choisir la fonction désirée, par exemple l'ouverture d'un fichier, soit par l'intermédiaire d'un menu, soit à partir d'un raccourci clavier ou soit en cliquant sur un bouton dans une barre d'outils.
Dans ce cadre là, si nous souhaitons réaliser des logiciels de qualité, il est préférable que le traitement de la commande (par exemple l'ouverture du fichier) ne soit réalisée qu'en un seul point du code. Nous pouvons déjà tendre vers cet idéal en faisant en sorte que les écouteurs appropriés se contentent d'appeler une méthode unique responsable de l'action en question. En général, cependant, cela ne sera pas suffisant et il faudra s'acheminer vers la création d'objets abstraits encapsulant toutes les informations nécessaires à la réalisation d'une action (par exemple la boîte de dialogue à faire apparaître, le nom de la méthode à solliciter, etc.).
C'est dans ce contexte que Java offre un outil très puissant. Il s'agit de la classe AbstractAction qui comporte déjà les services de base que nous pouvons attendre d'une classe destinée à représenter une telle action. Bien entendu, nous pourrons la compléter à volonté par héritage.
Avant de prendre connaissance de cette classe, revoyons le traitement de la gestion des événements de façon classique. Il est en effet très facile avec AWT de lier tous les événements au même écouteur. Par exemple, supposons que écouteurOuvrir soit un écouteur d'action dont la méthode actionPerformed() permet l'ouverture d'un fichier texte. Vous pouvez attacher le même objet comme écouteur de plusieurs sources d'événements :
Puis chaque commande d'ouverture de fichier est gérée d'une seule manière, quelle que soit l'action qui l'a déclenchée : un clic sur un bouton, un choix sur un menu ou une frappe au clavier.
Toutefois, le paquetage Swing fournit un mécanisme beaucoup plus pratique pour encapsuler des commandes et les attacher à plusieurs sources d'événement : il s'agit de l'interface Action. Une action est un objet qui encapsule :
L'interface Action possède les méthodes suivantes :
interface Action { void actionPerformed(ActionEvent événement); void setEnabled(boolean disponible); boolean isEnabled(); void putValue(String clé, Object valeur); Object getValue(String clé); void addPropertyChangeListener(PropertyChangeListener écouteur); void removePropertyChangeListener(PorpertyChangeListener écouteur); }
La première méthode actionPerformed() est la méthode habituelle de l'interface ActionListener : en fait, l'interface Action est dérivée de ActionListener. Par conséquent, il est possible d'utiliser un objet Action partout où un objet ActionListener est attendu.
Les deux méthodes suivantes setEnabled() et isEnabled() permettent d'activer ou de désactiver l'action, et de vérifier si elle est activée. Lorsqu'une action attachée à un menu ou à une barre d'outils est désactivée, l'option correspondante apparaît en grisé.
action.putValue(Action.NAME, "Ouvrir"); action.putValue(Action.SMALL_ICON, new ImageIcon("ouvrir.gif"));
Nom de l'action prédéfini | Valeur de l'action |
---|---|
NAME | Nom de l'action ; affiché sur les boutons et les options de menu. |
SMALL_ICON | Emplacement de stockage d'une petite icône ; pour affichage sur un bouton, une option de menu ou dans la barre d'outils. |
SHORT_DESCRIPTION | Courte description de l'icône ; pour affichage dans une bulle d'aide. |
LONG_DESCRIPTION | Description détaillée de l'icône ; pour utilisation potentielle dans l'aide en ligne. |
MNEMONIC_KEY | Abreviation mnémonique ; pour affichage dans une option de menu. |
ACCELERATOR_KEY | Emplacement pour le stockage d'un raccourci clavier. |
ACTION_COMMAND_KEY | Utilisée dans la méthode registerKeyboardAction(), maintenant obsolète. |
DEFAULT | Propiété fourre-tout. |
Si l'objet Action est ajouté à un menu ou à une barre d'outils, le nom et l'icône sont automatiquement récupérés et affichés dans l'option du menu ou sur le bouton de la barre d'outils. La valeur de SHORT_DESCRIPTION s'affiche dans une bulle d'aide.
Les deux dernières méthodes addPropertyChangeListener() et removePropertyChangeListener() de l'interface Action permettent aux autres objets - en particulier les menus ou les barres d'outils qui ont déclenché l'action - de recevoir une notification lorsque les propriétés de l'objet Action sont modifiées. Par exemple, si un menu est recencé en tant qu'écouteur de changement de propriétés d'un objet Action, et que l'objet Action soit ensuite désactivé, le menu est prévenu et peut alors affiché en grisé la rubrique correspondant à l'action. Les écouteurs de changement de propriété sont une construction générique intégré au modèle de composant des JavaBeans.
Notez que Action est une interface et non une classe. Toute classe implémentant cette interface est donc tenue d'implémenter les sept méthodes citées. Heureusement, un bon samaritain a implémenté toutes ces méthodes - sauf actionPerformed() - dans une classe nommée AbstractAction, qui se charge de stocker les couples clé/valeur et de gérer les écouteurs de changement de propriété. Il ne vous reste plus qu'à étendre AbstractAction et à écrire la méthode actionPerformed(). Cette classe propose trois constructeurs :
AbstractAction() // création d'une action sans spécification d'intitulé, ni d'icône associée
AbstractAction(String libellé) // création d'une action en spécifiant son intitulé sans icône associée
AbstractAction(String libellé, Icon icône) // création d'une action avec un libellé et son icône associée.
Ces actions sont extrêmement intéressantes puisqu'elles peuvent être insérer dans n'importe quel composant de choix ou de menu.
JButton(Action action);
JToggleButton(Action action);
JCheckBox(Action action); // JCheckBox hérite de JToggleButton
JRadioButton(Action action); // JRadioButton hérite de JToggleButton
JMenuItem(Action action);
JMenu(Action action); // JMenu hérite de JMenuItem
JCheckBoxMenuItem(Action action); // JCheckBoxMenuItem hérite deJMenuItem
JRadioButtonMenuItem(Action action); // JRadioButtonMenuItem hérite deJMenuItem
JMenuItem add(Action action);
JButton add(Action action);
Grâce à ces différentes techniques, il est très facile de mettre en oeuvre un ensemble de sélections dont chacune est représentée par différents éléments graphiques et dont chacune est recensée en un seul endroit commun qui est l'action.
Afin de bien illustrer l'intérêt de cette classe AbstractAction, je vous propose de mettre en oeuvre un simple éditeur de texte. Cet éditeur comporte une barre de menu, une barre d'outils et un menu contextuel qui proposent les mêmes actions, c'est-à-dire l'ouverture et la sauvegarde d'un texte ainsi que les rubriques d'éditions classiques, comme le couper, le copier et le coller. Chacun des éléments possède un libellé, une icône, une bulle d'aide avec, en plus, une activation possible par raccourci clavier. Il est possible de placer temporairement l'éditeur en lecture seule. Suivant l'état du document, certaines options deviennent grisées.
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; public class Menus extends JFrame { private JMenuBar menu = new JMenuBar(); private JToolBar barre = new JToolBar(); private JMenu fichier = new JMenu("Fichier"); private Actions actionNouveau = new Actions("Nouveau", 'N', "Tout effacer dans la zone d'édition"); private Actions actionOuvrir = new Actions("Ouvrir", 'O', "Ouvrir le fichier texte"); private Actions actionEnregistrer = new Actions("Enregistrer", 'W', "Sauvegarder le texte"); private Actions actionCouper = new Actions("Couper", 'X', "Enlever le texte sélectionné"); private Actions actionCopier = new Actions("Copier", 'C', "Copier le texte sélectionné"); private Actions actionColler = new Actions("Coller", 'V', "Coller le texte à l'endroit du curseur"); private JMenu édition = new JMenu("Edition"); private JTextPane éditeur = new JTextPane(); private JCheckBoxMenuItem lecture = new JCheckBoxMenuItem("Lecture seule"); private JPopupMenu surgissant = new JPopupMenu(); public Menus() { super("Les menus"); setJMenuBar(menu); add(barre, BorderLayout.NORTH); menu.add(fichier); fichier.setMnemonic('F'); fichier.add(actionNouveau); fichier.add(actionOuvrir); fichier.add(actionEnregistrer); activation(false); menu.add(édition); édition.setMnemonic('E'); édition.add(actionCouper); édition.add(actionCopier); édition.add(actionColler); édition.addSeparator(); édition.add(lecture).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { éditeur.setEditable(!lecture.isSelected()); actionCouper.setEnabled(!lecture.isSelected()); actionColler.setEnabled(!lecture.isSelected()); } }); lecture.setToolTipText("Empêche la saisie de l'utilisateur"); barre.add(actionNouveau); barre.add(actionOuvrir); barre.add(actionEnregistrer); barre.addSeparator(); barre.add(actionCouper); barre.add(actionCopier); barre.add(actionColler); surgissant.add(actionEnregistrer); surgissant.addSeparator(); surgissant.add(actionCopier); surgissant.add(actionColler); éditeur.setComponentPopupMenu(surgissant); éditeur.setBackground(Color.YELLOW); éditeur.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent ev) { activation(éditeur.getDocument().getLength()!=0); } }); add(new JScrollPane(éditeur)); setSize(400, 300); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } private void activation(boolean actif) { actionEnregistrer.setEnabled(actif); actionCouper.setEnabled(actif); actionCopier.setEnabled(actif); } public static void main(String[] args) { new Menus(); } private class Actions extends AbstractAction { private String méthode; public Actions(String libellé, char raccourci, String description) { super(libellé, new ImageIcon(libellé.toLowerCase()+".gif")); putValue(SHORT_DESCRIPTION, description); // putValue(MNEMONIC_KEY, (int)libellé.charAt(0)); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("control "+raccourci)); méthode = libellé.toLowerCase(); } public void actionPerformed(ActionEvent e) { try { this.getClass().getDeclaredMethod(méthode).invoke(this); } catch (Exception ex) { Menus.this.setTitle("Problème");} } private void nouveau() { Menus.this.setTitle("Nouveau document"); éditeur.setText(""); activation(false); } private void ouvrir() throws IOException { JFileChooser boîte = new JFileChooser(); if (boîte.showOpenDialog(Menus.this)==JFileChooser.APPROVE_OPTION) { File fichier = boîte.getSelectedFile(); Menus.this.setTitle(fichier.getName()); éditeur.read(new FileInputStream(fichier), null); } } private void enregistrer() throws IOException { JFileChooser boîte = new JFileChooser(); if (boîte.showSaveDialog(Menus.this)==JFileChooser.APPROVE_OPTION) { File fichier = boîte.getSelectedFile(); Menus.this.setTitle(fichier.getName()); if (!fichier.exists()) fichier.createNewFile(); PrintWriter écriture = new PrintWriter(fichier); écriture.println(éditeur.getText()); écriture.close(); } } private void couper() { éditeur.cut(); } private void copier() { éditeur.copy(); } private void coller() { éditeur.paste(); } } }