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) {