Swing : les interactions

Ce chapitre présente différents types d’interaction que l’on peut mettreen place dans les applications graphiques basées sur Swing.

Les listeners

Pour interagir avec l’utilisateur, chaque composant graphique peut intercepterdes événements (frappe d’une touche sur le clavier, clic souris…) et réagiren conséquence. Pour associer un comportement à un événement, on ajoute unécouteur d’événement (listener) au composant, c’est-à-dire un objetqui implémente l’interface EventListener.Cette interface est simplement une interface marqueur dont héritent toutes lesinterfacesqui représentent des listeners pour des événements particuliers.

 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
package com.cgi.udev.gui
														;import java.awt.Dimension;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;import java.util.EventObject;import javax.swing.JComponent;import javax.swing.JEditorPane;import javax.swing.JFrame;import javax.swing.WindowConstants;public class ExempleListener extends JFrame {private class ExempleMouseListener implements
														MouseListener {@Overridepublic void mouseClicked(MouseEvent e) {printEvent(e);}@Overridepublic void mouseEntered(MouseEvent e) {printEvent(e);}@Overridepublic void mouseExited(MouseEvent e) {printEvent(e);}@Overridepublic void mousePressed(MouseEvent e) {printEvent(e);}@Overridepublic void mouseReleased(MouseEvent e) {printEvent(e);}}private class ExempleKeyListener implements
														KeyListener {@Overridepublic void keyPressed(KeyEvent e) {printEvent(e);}@Overridepublic void keyReleased(KeyEvent e) {printEvent(e);}@Overridepublic void keyTyped(KeyEvent
														e) {printEvent(e);}}@Overrideprotected void frameInit()
														{super.frameInit();this.setDefaultCloseOperation(
														WindowConstants.
														EXIT_ON_CLOSE);
														this.setTitle("Exemple listener"
														);JComponent component = new
														JEditorPane();component.setPreferredSize(
														new Dimension(
														300, 300));component.addMouseListener(
														new ExempleMouseListener());
														component.addKeyListener(new ExempleKeyListener());
														this.add(component
														);this.pack();}private void printEvent(EventObject e) {System.out.println(e);}public static void main
														(String[]
														args) {JFrame window = new
														ExempleListener();
														window.setLocationRelativeTo(
														null);window.setVisible(true
														);}}

Dans l’exemple ci-dessus, l’application affiche un éditeur de texte sous la formed’un carré de 300 pixels sur 300 pixels. Aux lignes 72 et 73, on ajoute à cecomposantune instance de MouseListeneret une instance de KeyListener(les classesimplémentant ces interfaces sont déclarées sous la forme de classes internes). Ceslisteners se contentent d’afficher sur la sortie standard la représentationsous forme de chaîne de caractères de chaque événement.

Chaque événement fournit des informations liées à son origine. Par exemple, unMouseEventindique si un bouton de la souris est pressé. Un KeyEventindiquela touche du clavier qui est soit pressée soit relâchée. Un composant peut utiliserces informations pour modifier son état. Ainsi, la classe JEditorPane ,utiliséedans l’exemple précédent, enregistre en interne un KeyListenerpour savoirsi une touche a été pressée et en déduit le caractère qui doit être ajouté dansl’éditeur.

Un listener couramment utilisé est le type ActionListener.Ce listenerécoute les événements de type ActionEvent.Un ActionEventreprésenteune interaction utilisateur simple. Il est associé à une commande qui estun simple identifiant sous la forme d’une chaîne de caractères. Les boutonsacceptent des listeners de ce type.

  123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
package com.cgi.udev.gui
														;import java.awt.GridBagConstraints;import java.awt.GridBagLayout;import java.awt.Insets;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JComboBox;import javax.swing.JComponent;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.JTextArea;import javax.swing.JTextField;import javax.swing.WindowConstants;public class ExempleActionListener extends
														JFrame {private JComboBox<String
														> civilite;private JTextField nom;private JTextField prenom;
														private JTextArea adresse;@Overrideprotected void frameInit()
														{super.frameInit();this.setDefaultCloseOperation(
														WindowConstants.
														EXIT_ON_CLOSE);
														this.setTitle("Exemple Listeners"
														);this.getContentPane().
														setLayout(new GridBagLayout());
														int rowIndex = 0;civilite = new JComboBox<String>(new String[] {"Madame",
														"Monsieur"});nom = new JTextField
														();prenom = new JTextField
														();adresse = new JTextArea
														(10, 20);addRow(rowIndex++, "Civilité"
														, civilite);
														addRow(rowIndex++, "Nom"
														, nom);addRow(rowIndex++, "Prénom"
														, prenom);addRow(rowIndex++, "Addresse"
														, adresse);
														JButton okButton = new
														JButton("Ok");okButton.addActionListener(
														new ActionListener()
														{@Overridepublic void actionPerformed(
														ActionEvent e) {onOk();}});JButton cancelButton = new JButton(
														"Annuler");cancelButton.addActionListener(new
														ActionListener()
														{@Overridepublic void actionPerformed(
														ActionEvent e) {onCancel();}});addButtons(rowIndex++, okButton, cancelButton);this.pack();this.setResizable(false
														);}private void onOk() {// On affiche le contenu du formulaire sur la sortie standardSystem.out.println
														(String.format(
														"%1$s %2$s %3$s résidant au %4$s",
														civilite.getSelectedItem
														(), prenom.
														getText(), nom.getText(),
														adresse.getText()));}private void onCancel() {// on cache la fenêtrethis.setVisible(false
														);// on supprime la fenêtrethis.dispose();}private void addRow(
														int rowIndex,
														String titre, JComponent component
														) {GridBagConstraints cst = new
														GridBagConstraints();cst.fill = GridBagConstraints
														.HORIZONTAL;cst.anchor = GridBagConstraints
														.NORTH;cst.insets = new
														Insets(5, 20,
														5, 20);cst.gridy = rowIndex;cst.gridx = 0
														;cst.weightx = .3;JLabel label = new JLabel
														(titre);
														label.setLabelFor(component
														);this.add(label, cst);
														cst.gridx = 1
														;cst.weightx = .7;this.add(component
														, cst);}private void addButtons(
														int rowIndex,
														JButton...buttons) {JPanel panel = new JPanel();for (JButton button
														: buttons) {panel.add(button);}GridBagConstraints cst = new
														GridBagConstraints();cst.insets = new Insets(5, 10,
														0, 0);cst.fill = GridBagConstraints
														.HORIZONTAL;cst.gridy = rowIndex
														;cst.gridx = 0;cst.gridwidth = 2
														;this.add(panel, cst);
														}public static void main
														(String[]
														args) {JFrame window = new
														ExempleActionListener();window.setLocationRelativeTo(
														null);window.setVisible(true
														);}}

Le code ci-dessus reprend l’application de saisie de formulaire qui utilisaitle GridBagLayoutdans le chapitre précédent. Entre les lignes 44 et 58, oncrée les boutons de l’applicationen ajoutant des instances de ActionListenersous la forme de classes anonymes. Lorsquel’utilisateur clique sur le bouton Ok (respectivement Annuler), laméthodeprivée onOk (respectivement onCancel) est appelée. La méthode onOk(lignes64-68) affiche sur la sortie standard les informations récupérées des différenteszones de saisie. La méthode onCancel (lignes 70-75) cache la fenêtre etappellela méthode disposepour la détruire.

Les menus

Les menus avec Swing sont principalement gérés par trois classes :

JMenuBar
Cette classe représente une barre de menu.
JMenu
Cette classe représente un menu.
JMenuItem
Cette classe représente une entrée cliquable dans un menu. Elle se comportecomme un JButton.

De plus, il existe des sous classes de JMenuItempour représenter des entréesde menu plus complexes.

La classe JFrameest déjà capable de gérer en interne une instance de JMenuBar.Il suffit d’appeler la méthode JFrame.setJMenuBarpour ajouter la barre de menu.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
package com.cgi.udev.gui
														;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.ButtonGroup;import javax.swing.JCheckBoxMenuItem;import javax.swing.JFrame;import javax.swing.JMenu;import javax.swing.JMenuBar;import javax.swing.JMenuItem;import javax.swing.JRadioButtonMenuItem;import javax.swing.WindowConstants;public class ExempleMenu extends JFrame {@Overrideprotected void frameInit()
														{super.frameInit();this.setDefaultCloseOperation(
														WindowConstants.
														EXIT_ON_CLOSE);
														this.setTitle("Exemple Menus"
														);this.setJMenuBar(new JMenuBar());
														this.getJMenuBar().add(createMenuFichier());
														this.getJMenuBar().add(createMenuSpecial());
														this.setSize(500
														, 300);
														}private JMenu createMenuFichier() {
														JMenu menu = new JMenu
														("Fichier");
														menu.add(new
														JMenuItem(
														"Nouveau"));JMenu subMenu = new
														JMenu(
														"Importer");subMenu.add(new JMenuItem(
														"Document simple"
														));subMenu.add(new JMenuItem(
														"Document complexe"));menu.add(subMenu);menu.addSeparator();menu.add(new JMenuItem(
														"Imprimer..."));
														menu.add(new
														JMenuItem("Aperçu impression..."));
														menu.addSeparator();menu.add(new JMenuItem(
														"Fermer")).
														addActionListener(new
														ActionListener()
														{@Overridepublic void actionPerformed(
														ActionEvent e) {ExempleMenu.this.
														dispose();}});return menu;}private JMenu createMenuSpecial() {
														JMenu menu = new JMenu
														("Spécial");
														menu.add(new
														JCheckBoxMenuItem("Activer"
														, false));menu.addSeparator();JRadioButtonMenuItem[] radioButtons =
														{new JRadioButtonMenuItem(
														"Bleu",
														true),new JRadioButtonMenuItem(
														"Vert"),
														new JRadioButtonMenuItem(
														"Rouge")};
														ButtonGroup buttonGroup = new
														ButtonGroup();for (JRadioButtonMenuItem radioButton:
														radioButtons) {buttonGroup.add(radioButton);menu.add(radioButton);}return menu;}public static void main
														(String[]
														args) {JFrame window = new
														ExempleMenu();window.setLocationRelativeTo(
														null);window.setVisible(true
														);}}

L’application ci-dessus crée une barre de menu avec un exemple pour chaque typed’entrée.

../_images/exemple_menus.png

Dans une application complète, il faudrait ajouter un ActionListenerpour chaque entrée des menus. Dans cet exemple, seul le menu Fermer a unActionListenerpour terminer l’application.

L’interface Action

Dans une application, une même fonctionnalité peut souvent être déclenchée deplusieurs façons par un utilisateur :

  • en cliquant dans un menu
  • en cliquant sur une icône dans la barre d’icônes
  • en exécutant un raccourci clavier
  • en cliquant sur un bouton dans une boite de dialogue

Swing permet de gérer ce phénomène grâce à l’interface Action.Plutôt que d’ajouterun listener, il est possible d’associer une action à une objet de type JMenuItem ouJButton.L’interface Actionhérite de ActionListenerpour pouvoir fournir uncomportement lorsque l’utilisateur clique sur un bouton. Mais l’interface Action permet également de définir un libellé, une icône, une description et un raccourciclavier. Tous les composants associés s’adapteront en fonction de l’état del’action.Si une action est désactivée avec sa méthode setEnabledalors tous les boutonsassociés apparaîtront grisés.

 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
package com.cgi.udev.gui
														;import java.awt.Desktop;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.net.URI;import javax.swing.AbstractAction;import javax.swing.Action;import javax.swing.JButton;import javax.swing.JCheckBoxMenuItem;import javax.swing.JFrame;import javax.swing.JMenu;import javax.swing.JMenuBar;import javax.swing.JMenuItem;import javax.swing.JPanel;import javax.swing.KeyStroke;import javax.swing.UIManager;import javax.swing.WindowConstants;public class ExempleMenu extends JFrame {private Action exempleAction;
														private class ExempleAction extends AbstractAction
														{public ExempleAction() {
														super("Java",
														UIManager.getIcon(
														"FileView.fileIcon"));putValue(SHORT_DESCRIPTION,
														"Cliquez pour en savoir plus sur Java");
														putValue(ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_A, ActionEvent.CTRL_MASK));}@Overridepublic void actionPerformed(
														ActionEvent e) {try {// on ouvre la page Web dans le navigateur par défautDesktop.getDesktop().browse(new URI("https://fr.wikipedia.org/wiki/Java_(langage)"));
														} catch (Exception ex) {ex.printStackTrace();
														}}}@Overrideprotected void frameInit()
														{super.frameInit();this.setDefaultCloseOperation(
														WindowConstants.
														EXIT_ON_CLOSE);
														this.setTitle("Exemple Menus"
														);this.exempleAction =
														new ExempleAction();
														this.setJMenuBar(new
														JMenuBar());this.getJMenuBar().add(createMenu());
														JPanel panel = new JPanel
														();panel.add(new JButton(
														exempleAction));
														this.add(panel
														);this.setSize(500, 300);}private JMenu createMenu()
														{JMenu menu = new JMenu("Menu");
														menu.add(new
														JMenuItem(exempleAction));JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem
														("Activer"
														, true);checkBox.addActionListener(
														new ActionListener()
														{@Overridepublic void actionPerformed(
														ActionEvent e) {exempleAction.setEnabled(
														checkBox.getState());}});menu.add(checkBox);menu.addSeparator();menu.add(new JMenuItem(
														"Fermer")).
														addActionListener(new
														ActionListener()
														{@Overridepublic void actionPerformed(
														ActionEvent e) {ExempleMenu.this.
														dispose();}});return menu;}public static void main
														(String[]
														args) {JFrame window = new
														ExempleMenu();window.setLocationRelativeTo(
														null);window.setVisible(true
														);}}

Dans l’exemple ci-dessus, on déclare une action comme classe interne. Plutôtque d’implémenter l’interface Action,la classe interne ExempleActionétend la classe abstraite AbstractAction.Comme l’interface Actionpeutêtre assez complexe à implémenter, cette classe abstraite fournit la plus grandepartie du code hormis l’implémentation de la méthode actionPerformedquicorrespond au traitement proprement dit.

L’action déclarée par notre application possède un nom, une description(pour afficher une bulle d’aide), une icône et un raccourci clavier(Ctrl+A). La même instance deExempleAction est associée àune entrée dans le menu et au bouton dans la fenêtre. De plus, une autre entrée dansle menu permet de désactiver l’action (ce qui aura pour conséquence de désactiverle raccourci clavier et de griser le bouton et le menu associés).

Les boites de dialogue

Si vous souhaitez créer des boites de dialogues, Swing fournit la classe JDialog .Cette classe offre des fonctionnalités spécifiques pour ce type d’interface. Parexemple JDialogpermet de choisir si la boite de dialogue doit être modale(c’est-à-dire si elle doit empêcher toute interaction avec la fenêtre parente) ounonmodale.

Swing fournit également la classe JOptionPanequi permet de créer rapidementdes boites de dialogue simples. Les méthodes fournies par la classeJOptionPanesont bloquantes. Cela signifie que l’exécution s’arrête jusqu’àce que l’utilisateur ait choisi parmi les options de la boite de dialogue.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
package com.cgi.udev.gui
														;import java.util.Random;import javax.swing.JFrame;import javax.swing.JOptionPane;public class ExempleOptionPane extends JFrame {private static final String
														APP_TITLE = "Exemple OptionPane";
														public static void main
														(String[]
														args) {JOptionPane.showMessageDialog(
														null,
														"Bonjour"
														, APP_TITLE,
														JOptionPane.PLAIN_MESSAGE);JOptionPane.showMessageDialog(
														null, "Ceci est un message",
														APP_TITLE, JOptionPane.
														INFORMATION_MESSAGE);JOptionPane.showMessageDialog(
														null, "Ceci est un avertissement",
														APP_TITLE, JOptionPane.WARNING_MESSAGE);
														JOptionPane.showMessageDialog(
														null, "Ceci est une erreur",
														APP_TITLE, JOptionPane.ERROR_MESSAGE);JOptionPane.showMessageDialog(
														null, "On passe à la suite ?",
														APP_TITLE, JOptionPane.QUESTION_MESSAGE);
														int jouer = JOptionPane.
														showConfirmDialog(
														null, "Voulez-vous jouer ?",
														APP_TITLE, JOptionPane.YES_NO_OPTION);if (jouer == JOptionPane.YES_OPTION) {Random random = new
														Random();do {int bonneResponse = random
														.nextInt(
														20) + 1;String reponse = JOptionPane.showInputDialog(
														null, "Donnez un nombre entre 1 et 20 :",
														APP_TITLE, JOptionPane
														.QUESTION_MESSAGE
														);if (reponse == null) {break;}try {int valeur = Integer
														.valueOf(reponse);if (valeur ==
														bonneResponse){JOptionPane.showMessageDialog(
														null, "Bravo vous avez gagné !",
														APP_TITLE, JOptionPane.
														INFORMATION_MESSAGE);} else {JOptionPane.showMessageDialog(
														null, "Perdu ! La bonne réponse était " +
														bonneResponse +
														".",APP_TITLE, JOptionPane.WARNING_MESSAGE);
														}} catch (NumberFormatException nfe
														) {JOptionPane.showMessageDialog(
														null,
														"'"
														+ reponse +
														"' n'est pas un nombre !",
														APP_TITLE, JOptionPane.ERROR_MESSAGE);}} while (JOptionPane.showConfirmDialog
														(null, "Voulez-vous rejouer ?",
														APP_TITLE, JOptionPane.YES_NO_OPTION) == JOptionPane.
														YES_OPTION);JOptionPane.showMessageDialog(
														null,
														"Au revoir..."
														, APP_TITLE,
														JOptionPane.PLAIN_MESSAGE);}}}

Les boites de dialogues avancées

Swing fournit des classes pour des boites de dialogue évoluées.

Boite de dialogue des fichiers

La classe JFileChooserpermet de créer une boite de dialogue de sélection defichiers et/ou de répertoires.

 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
package
															com.cgi.udev.gui;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.IOException;import javax.swing.JEditorPane;import javax.swing.JFileChooser;import javax.swing.JFrame;import javax.swing.JMenu;import javax.swing.JMenuBar;import javax.swing.JMenuItem;import javax.swing.JOptionPane;import javax.swing.JScrollPane;import javax.swing.WindowConstants;import javax.swing.filechooser.FileNameExtensionFilter;
															public class EditeurTexte extends JFrame
															{private JEditorPane editor;@Overrideprotected void frameInit(){super.frameInit();this.setDefaultCloseOperation(
															WindowConstants
															.EXIT_ON_CLOSE);
															this.setTitle("Simple éditeur de texte"
															);this.setJMenuBar(new
															JMenuBar());
															this.getJMenuBar().
															add(createMenu());
															editor = new JEditorPane();
															editor.setEditable(
															false);this.add(new
															JScrollPane(
															editor));this.setSize(800
															, 600);;}private JMenu createMenu()
															{JMenu menu = new JMenu("Fichier"
															);menu.add(new JMenuItem(
															"Ouvrir..."
															)).
															addActionListener
															(new ActionListener
															() {@Overridepublic void actionPerformed(
															ActionEvent e
															) {open();}});menu.addSeparator();menu.add(new
															JMenuItem(
															"Fermer"
															)).
															addActionListener
															(new ActionListener
															() {@Overridepublic void actionPerformed(
															ActionEvent e
															) {dispose();}});return menu;}private void open()
															{JFileChooser fileChooser = new
															JFileChooser();fileChooser.setFileSelectionMode(
															JFileChooser.
															FILES_ONLY);
															fileChooser.setMultiSelectionEnabled(
															false);fileChooser.addChoosableFileFilter(
															new FileNameExtensionFilter
															("Fichiers texte (txt, html, rtf)",
															"txt",
															"html"
															,
															"xhtml"
															,
															"rtf"));int choix = fileChooser.
															showOpenDialog
															(this);
															if (choix == JFileChooser
															.APPROVE_OPTION
															) {try {editor.setPage(fileChooser.
															getSelectedFile
															().toURI().
															toString());
															} catch (IOExceptione) {e.printStackTrace();
															JOptionPane.showMessageDialog(
															this, "Une erreur est survenue :\n" +
															e.getMessage(),
															"Erreur", JOptionPane.
															ERROR_MESSAGE);
															}}}public static void main
															(String[] args) {JFrame window = new
															EditeurTexte();window.setLocationRelativeTo(
															null);window.setVisible(true
															);}}

La classe ci-dessus crée un éditeur de texte simple qui permet de choisir lefichierque l’on veut consulter. La méthode open déclarée à partir de la ligne55utilise une instance de JFileChooserpour récupérer le fichier sélectionnéet donner son URL au composant JEditorPanequi l’affiche.

Boite de sélection de couleur

La classe JColorChooseraffiche une boite de dialogue permettant de choisir unecouleur.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
package
															com.cgi.udev.gui;import java.awt.Color;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JColorChooser;import javax.swing.JFrame;import javax.swing.JMenu;import javax.swing.JMenuBar;import javax.swing.JMenuItem;import javax.swing.JPanel;import javax.swing.WindowConstants;public class VisualiseurCouleur extends
															JFrame {private JPanel colorPanel;@Overrideprotected void frameInit(){super.frameInit();this.setDefaultCloseOperation(
															WindowConstants
															.EXIT_ON_CLOSE);
															this.setTitle("Selecteur de couleur"
															);this.setJMenuBar(new
															JMenuBar());
															this.getJMenuBar().
															add(createMenu());
															this.colorPanel = new
															JPanel();this.add(this
															.colorPanel
															);this.setSize(800, 600);;}private JMenu createMenu()
															{JMenu menu = new JMenu("Couleur"
															);menu.add(new JMenuItem("Couleur de fond...")).
															addActionListener
															(new ActionListener
															() {@Overridepublic void actionPerformed(
															ActionEvent e
															) {chooseColor();}});menu.addSeparator();menu.add(new
															JMenuItem(
															"Fermer"
															)).
															addActionListener
															(new ActionListener
															() {@Overridepublic void actionPerformed(
															ActionEvent e
															) {dispose();}});return menu;}private void chooseColor()
															{Color newColor = JColorChooser
															.showDialog
															(this, "Choisissez la couleur de fond"
															,colorPanel.getBackground());
															this.colorPanel.setBackground
															(newColor);}public static void main(String[] args) {JFrame window = new
															VisualiseurCouleur
															();window.setLocationRelativeTo(
															null);window.setVisible(true
															);}}

L’application ci-dessus offre un menu pour changer la couleur de fond de lafenêtre.

Exercice

Application pour sauvegarder les donnéespersonnelles (suite)

Objectif

Reprenez l’application de saisie des données personnelles.Cette applicationdoit avoir un bouton Sauver qui ouvre une boite de dialogue desauvegardede fichier. Le fichier doit ensuite être sauvé sur le disque au format VCard.

L’application doit vérifier au préalable qu’au moins leschamps prénom et nomont été saisis. Si ce n’est pas le cas, une boite de dialogue doitinformerl’utilisateur des champs manquants. Utilisez pour cela JOptionPane .

Application pour charger/sauvegarder les donnéespersonnelles (suite)

Objectif

Reprenez l’application de saisie des données personnelles.Ajoutez un menuDonnées.Ce menu contient quatre entrées :

Entrée Raccourci Description
Ouvrir… Ctrl+O Cette entrée de menu permet de sélectionner sur le disque unfichier avecl’extension vcf (l’extension des fichiers VCard) et dechargerles données dans les champs de l’application.
Sauver… Ctrl+S Cette entrée réalise la même action que le bouton Sauver introduità l’exercice précédent
Réinitialiser   Cette entrée remet tous les champs à vide après avoir demandéconfirmationà l’utilisateur.
Quitter Ctrl+Q Cette entrée permet de fermer l’application

Créez des classes héritant de AbstractActionpour gérer ces entrées etassociez-les aux raccourcis clavier.