C. Recanati
InHM, Java Swing
MI1, 2008

TP n° 9

Exercice 1 (suite de l’ex.2, TP 7- 8 : Sketcher comme logiciel de dessin).
On va garder provisoirement le JTextArea pour afficher des informations sur les événements, mais à terme, on le supprimera pour dessiner dans la fenêtre. 
Les items du menu Elements permettent de sélectionner la couleur et le type de l’élément (la forme) que l’on souhaite dessiner. On rajoute donc deux nouveaux membres à la classe SketcherFrame : elementColor de type Color et elementType de type entier. Ces membres seront mis à jour quand on sélectionnera les options du menu Elements.

Pour que les initialisations soient claires et faciles à modifier, on définit dans un fichier séparé une interface Constants qui définit les types d’éléments entiers LIGNE, RECTANGLE, CERCLE, COURBE et les conditions initiales des membres elementType et elementColor ( DEFAULT_ELEMENT_TYPE et DEFAULT_ELEMENT_COLOR). La classe SketcherFrame implémentera l’interface Constants et se servira de ces constantes pour faire les initialisations. 

1) Définir l’interface Constants et modifier la création des cases à cocher du sous-menu Couleurs pour que la case à cocher marquée initialement corresponde à la couleur de défaut initiale. Modifier également la création des boutons radio pour qu’ils soient sélectionnés au départ conformément au type initial de elementType.

2) Pour gérer les types d’options du menu Elements, définir une classe interne TypeListener qui implémente ActionListener et comporte un membre privé type permettant de stocker le type de l’élément écouté. Cette classe aura un constructeur qui prend en argument le type et le mémorise dans son champs privé type.  Cela permettra d’adapter les écouteurs aux différents items du menu Elements à leur création. Lorsqu’une option du menu sera activée, l’écouteur consultera son type privé et mettra elementType à jour.

3) On procèdera de même pour gérer la couleur en implémentant une classe interne ColorListener qui comportera un membre privé color permettant de définir la couleur de l’élément écouté.

Si vous avez de l’avance…
4) Ajouter des écouteurs anonymes aux boutons de la ToolBar (‘anonymes’ signifie qu’on définit l’écouteur directement au moment où on l’ajoute, et sans le nommer) pour afficher dans le JTextArea quel bouton (gauche, milieu, ou droit) a été enfoncé.

Eléments de correction
Exercice 1
//  fichier Sketcher.java
import java.awt.*;

public class Sketcher {
   static SketcherFrame window; 
   public static void main(String[] args) {
		window = new SketcherFrame("Sketcher");

		Toolkit leKit = window.getToolkit();
		Dimension wndSize = leKit.getScreenSize();
		window.setBounds(wndSize.width/4, 
						wndSize.height/4,
		 				wndSize.width/2, 
						wndSize.height/2);
		window.setVisible(true);
   }
}

//  fichier Constants.java
import java.awt.*;

public interface Constants {

    // Les types d'élements
    int LIGNE = 101;
    int RECTANGLE = 102;
    int CERCLE = 103;
    int COURBE = 104;
    
    // Les conditions initiales
    int DEFAULT_ELEMENT_TYPE = RECTANGLE;
    Color DEFAULT_ELEMENT_COLOR = Color.green;
}

// fichier SketcherFrame.java 
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SketcherFrame extends JFrame 
							implements constants {
                      
	protected JTextArea output ;
	protected JScrollPane scrollPane;
	private JMenuBar menuBar;

	private Color elementColor = 
						DEFAULT_ELEMENT_COLOR ;
	private int elementType = DEFAULT_ELEMENT_TYPE;     

	protected String newline = "\n";
        
	public SketcherFrame(String titre) {

		JMenu menu, submenu; 
		JMenuItem menuItem;
		JRadioButtonMenuItem rbMenuItem;
		JCheckBoxMenuItem cbMenuItem;

		setTitle(titre);
		setDefaultCloseOperation(EXIT_ON_CLOSE);

			//Créer la toolbar.
		JToolBar toolBar = new JToolBar();
		toolBar.setFloatable(false);
		addButtons(toolBar);

		// etc. début de la construction inchangé
	
		// Construire le menu Elements …
		menu = new JMenu("Elements");
		menu.setMnemonic(KeyEvent.VK_E);         
		menuBar.add(menu);

		ButtonGroup group = new ButtonGroup();

		rbMenuItem = new JRadioButtonMenuItem("Ligne",
							elementType == LIGNE);
		rbMenuItem.setMnemonic(KeyEvent.VK_L);
		group.add(rbMenuItem);
		rbMenuItem.addActionListener(new 
							TypeListener(LIGNE));
		menu.add(rbMenuItem);

		rbMenuItem = new JRadioButtonMenuItem
					("Rectangle",
						elementType == RECTANGLE);
		rbMenuItem.setMnemonic(KeyEvent.VK_R);
		group.add(rbMenuItem);
		rbMenuItem.addActionListener
					(new TypeListener(RECTANGLE));
		menu.add(rbMenuItem);

		// etc. pour les autres types de formes

		menu.addSeparator();

		submenu = new JMenu("Couleur");
		submenu.setMnemonic(KeyEvent.VK_C);
			// le groupe de check box menu items
		ButtonGroup group2 = new ButtonGroup();

		cbMenuItem = new JCheckBoxMenuItem("Rouge",
				   elementColor.equals(Color.red));
		cbMenuItem.setMnemonic(KeyEvent.VK_R);
		group2.add(cbMenuItem);
		cbMenuItem.addActionListener
				(new ColorListener(Color.red));
		submenu.add(cbMenuItem);

		// etc. pour bleu, vert, etc.

		//Construire le menu Aide

		// fin du constructeur
	}

	class TypeListener implements ActionListener {
		private int type;
        
		TypeListener(int type) {
			this.type = type;	// mémorise le type
		}
        
		public void actionPerformed(ActionEvent e) {
        
			elementType = type; // l’essentiel !
            
			JMenuItem source 
				= (JMenuItem)(e.getSource());
			String s = "ActionEvent detecte dans
\TypeListener."
				+ newline
				+ "    Event source: " 
				+ source.getText()
				+ " (une instance de " 
				+ getClassName(source) + ")";
			output.append(s + newline);
		}
	}

	class ColorListener implements ActionListener {
		private Color color;
        
		ColorListener(Color color) {
			this.color = color;	// mémorise color
		}
        
		public void actionPerformed(ActionEvent e) {
        
			elementColor = color; // l’essentiel !
            
			JMenuItem source =
					 (JMenuItem)(e.getSource());
			String s = "ActionEvent detecte dans
\ ColorListener."
					+ newline
					+ "    Event source: " 
					+ source.getText()
					+ " (une instance de " 
					+ getClassName(source) + ")";
			output.append(s + newline);
		}

	}

    // Retourne juste le nom de la classe 
	protected String getClassName(Object o) {
		String classString = o.getClass().getName();
		int dotIndex = classString.lastIndexOf(".");
		return classString.substring(dotIndex+1);
	}

	protected void addButtons(JToolBar toolBar) {
		JButton button = null;

			// premier bouton
		button = new JButton(new
				 ImageIcon("images/left.gif"));
		button.setToolTipText("bouton de gauche");
		button.addActionListener(new
					 ActionListener() {
		 	public void actionPerformed
						(ActionEvent e){
			  displayResult("ActionEvent bouton1");
		    }
		});
		toolBar.add(button);

        // ibid deuxieme et troisieme boutons
	}

	protected void displayResult
					(String actionDescription) {
		output.append(actionDescription + newline);
    }
}

Exercice 2
On va créer un JApplet qui présente une liste scrollée dans sa partie centrale et au dessus, deux boutons alignés horizontalement permettant respectivement : 
1.	de détruire les items sélectionnés dans la liste et 
2.	d’ajouter un item à la liste. 

1) Compléter pour cela le squelette de programme suivant :
public class Test extends JApplet {
	private JList list = new JList();

	String[] items = { "item[0]", "item[1]", "item[2]", "item[3]", 
				"item[4]", "item[5]", "item[6]", "item[7]",
				 "item[8]", "item[9]" };

	public void init() {
		Container contentPane = getContentPane();
		ControlPanel controlPanel = new ControlPanel(list);  // plus loin

		contentPane.add(controlPanel, BorderLayout.NORTH);
		contentPane.add(new JScrollPane(list),
						 BorderLayout.CENTER);
		populateList();
	}
	public void populateList() {
	// construire un modèle à partir de DefaultListModel
	// l’initialiser avec le tableau d’items et l’affecter à la liste privée
	//  ...
	}
}
class ControlPanel extends JPanel {
	private JButton remove = new JButton("supprimer la selection");
	private JButton add = new JButton("ajouter un item");
	private JList list ;

	public ControlPanel(JList ls) {
		this.list = ls ;
		add(remove);
		add(add);

		remove.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
			// récupérer les indices sélectionnés de la liste
			// et récupérer le modèle
			// ...
		// puis supprimer les items correspondants du modèle
			// ...
				}
			}
		});
		add.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// récupérer le modèle de la liste

				// récupérer la chaîne (l’item) à rajouter dans
				// une boîte de dialogue modale

				// l’ajouter au modèle

SwingUtilities.invokeLater(new Runnable() {
					public void run() { 
						list.ensureIndexIsVisible(
									model.getSize()-1);
					}
				});
			}
		});
	}
}
Remarque : avec invokeLater,  la procédure ensureIndexIsVisible  n’est  effectuée qu’après que les événements de mise à jour aient été traités.

2) On rajoute au panneau de contrôle une JComboBox présentant le mode de sélection utilisée par la liste, soit : SINGLE_SELECTION, SINGLE_INTERVAL_SELECTION,  ou MULTIPLE_INTERVAL_SELECTION. On écrira une procédure privée initializeSelectionMode() qui initialise l’item sélectionné de la comboBox conformément au mode de sélection initial de la liste, et on ajoutera un ItemListener à la comboBox pour mettre à jour le mode de sélection de la liste quand un nouveau mode est sélectionné sur la comboBox.

3) Modifier encore le panneau de contrôle ControlPanel pour qu’il affiche sous les boutons précédents: 
	1. les indices des items sélectionnés dans la liste (sous une forme comme :  ‘Items sélectionnés : 3, 4, 5, 9 ‘  réalisée avec deux labels disposés horizontalement) ; 
	2. au dessous les indices minimum et maximum des items sélectionnés. Pour la mise à jour de ces labels, on écrira une méthode update() pour le panneau de contrôle qui sera appelée quand la sélection de la liste change (utiliser un ListSelectionListener sur la liste).
Indication : pour le placement des composants dans le ControlPanel, on pourra utiliser trois JPanel et un BoxLayout vertical. Le JPanel du haut servira à placer les boutons et la comboBox, celui du milieu les labels des indices sélectionnés, et celui du dessous les labels des indices minimum et maximum.

Eléments de correction

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class Test2 extends JApplet {
	private JList list = new JList();

	String[] items = { "item[0]", "item[1]", "item[2]", 
				"item[3]", "item[4]", "item[5]",
				"item[6]", "item[7]", 
				"item[8]", "item[9]" };

	public void init() {
		Container contentPane = getContentPane();
		ControlPanel controlPanel = new ControlPanel(list);

		contentPane.add(controlPanel, BorderLayout.NORTH);
		contentPane.add(new JScrollPane(list), 
						BorderLayout.CENTER);
		populateList();
		controlPanel.update() ;
                
	}
	public void populateList() {
		DefaultListModel model = new DefaultListModel();

		for(int i=0; i < items.length; ++i)
			model.addElement(items[i]);

		list.setModel(model);
	}
}
class ControlPanel extends JPanel {
	JButton remove = new JButton("supprimer la selection");
	JButton add = new JButton("ajouter un item");
	private JComboBox mode = new JComboBox();
        
	private JList list;

	private String single = "SINGLE_SELECTION",
                    singleInterval = "SINGLE_INTERVAL_SELECTION",
                    multipleInterval = "MULTIPLE_INTERVAL_SELECTION";

	private JLabel minLabel = new JLabel(),
			   maxLabel = new JLabel(),
			   selIndicesLabel = new JLabel();

	public ControlPanel(JList   lis) {
		JPanel top = new JPanel(), 
			   mid = new JPanel(),
			   bottom = new JPanel();

		this.list = lis;

		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 
		setBorder(BorderFactory.createEtchedBorder());

		top.add(remove);
		top.add(add);
		top.add(mode);

		mode.addItem(single);
		mode.addItem(singleInterval);
		mode.addItem(multipleInterval);
		initializeSelectionMode();
		
		mid.add(new JLabel("Minimum:")); 	mid.add(minLabel);
		mid.add(new JLabel("Maximum:")); 	mid.add(maxLabel);

		add(top);
		add(mid);
		add(bottom);

		bottom.add(new JLabel("Indices selectionnes:"));
		bottom.add(selIndicesLabel);

		remove.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int[] selected = list.getSelectedIndices();
				DefaultListModel model =
							(DefaultListModel)list.getModel();

				for(int i=0; i < selected.length; ++i) {
					model.removeElementAt(selected[i] - i);
				}
			}
		});
		add.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				final DefaultListModel model =
					(DefaultListModel)list.getModel();

				String s = JOptionPane.showInputDialog(
								list,
								"Entrer un item : ");

				model.addElement(s);

				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						list.ensureIndexIsVisible(
								model.getSize()-1);
					}
				});
			}
		});
		mode.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if(e.getStateChange() == ItemEvent.SELECTED) 
					setSelectionMode((String)e.getItem());
			}
		});   
		list.addListSelectionListener( new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				update();
			}
		});

	}
	public void update() {
		int 	min = list.getMinSelectionIndex(),
			max = list.getMaxSelectionIndex() ;

		minLabel.setText(Integer.toString(min) + " / ");
		maxLabel.setText(Integer.toString(max));

		int[] selected = list.getSelectedIndices();
		String s = new String();

		for(int i = 0; i < selected.length; ++i) {
			s += Integer.toString(selected[i]);

			if(i < selected.length-1)
				s += ",";
		}
		selIndicesLabel.setText(s);
		validate();
	}

	private void initializeSelectionMode() {
		int m = list.getSelectionMode();

		switch(m) {
			case ListSelectionModel.SINGLE_SELECTION:
				mode.setSelectedItem(single);
				break;
			case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
				mode.setSelectedItem(singleInterval);
				break;
			case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
				mode.setSelectedItem(multipleInterval);
				break;
		}
	}
	private void setSelectionMode(String s) {
		if(s.equals("SINGLE_SELECTION")) {
                        System.out.println("single selected");
			list.setSelectionMode(
				ListSelectionModel.SINGLE_SELECTION);
		}
		else if(s.equals("SINGLE_INTERVAL_SELECTION")) {
                       	System.out.println("single interval selected");
			list.setSelectionMode(
		       ListSelectionModel.SINGLE_INTERVAL_SELECTION);
		}
		else if(s.equals("MULTIPLE_INTERVAL_SELECTION")) {
                       	System.out.println("multiple interval selected");
			list.setSelectionMode(
		  ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		}
	}
}