Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Create GUI stuff ont the EDT.
[graphlib_java.git] / DrawingWindow.java
1 import java.awt.*;
2 import java.awt.event.*;
3 import java.awt.image.*;
4 import javax.swing.*;
5 import java.lang.reflect.*;
6
7 /**
8  * Fenêtre de dessin
9  *
10  * <p>Cette classe permet d'écrire des applications graphiques simples
11  * en dessinant dans une fenêtre.
12  *
13  * <p><b>NB.</b> Pour toutes les méthodes de dessin, le coin en haut à
14  * gauche de la fenêtre a les coordonnées (0, 0).  Le coin en bas à
15  * droite de la fenêtre a les coordonnées (largeur - 1, hauteur - 1),
16  * si la fenêtre est de dimension largeur × hauteur.
17  *
18  * <p>Un appui sur la touche &lt;Esc&gt; provoque la fermeture de la
19  * fenêtre.  Comme pour la plupart des applications, il est également
20  * possible de fermer la fenêtre via le gestionnaire de fenêtres.
21  *
22  * <p>Télécharger le code: <a href="DrawingWindow.java">DrawingWindow.java</a>
23  *
24  * <p>Télécharger des exemples d'utilisation:
25  * <a href="Hello.java">Hello.java</a>
26  * <a href="Exemple1.java">Exemple1.java</a>
27  * <a href="Exemple2.java">Exemple2.java</a>
28  * <a href="Exemple3.java">Exemple3.java</a>
29  *
30  * @author Arnaud Giersch &lt;arnaud.giersch@univ-fcomte.fr&gt;
31  * @version 20141008
32  */
33 public class DrawingWindow {
34
35     /** Largeur de la fenêtre */
36     public final int width;
37
38     /** Hauteur de la fenêtre */
39     public final int height;
40
41     /**
42      * Construit une nouvelle fenêtre de dessin avec le titre et les dimensions
43      * passés en paramètres.
44      *
45      * @param title             titre de la fenêtre
46      * @param width             largeur de la fenêtre
47      * @param height            hauteur de la fenêtre
48      *
49      * @see javax.swing.JPanel
50      */
51     public DrawingWindow(String title, int width, int height) {
52
53         this.title = new String(title);        
54         this.width = width;
55         this.height = height;
56
57         image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
58         graphics = image.createGraphics();
59
60         try {
61             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
62                     public void run() { createGUI(); }
63                 });
64         }
65         catch (Exception e) {
66             System.err.println("Error: interrupted while creating GUI");
67             System.err.println("Got exception: " + e);
68             System.exit(1);
69         }
70
71         setColor(Color.BLACK);
72         setBgColor(Color.WHITE);
73         clearGraph();
74         sync();
75     }
76
77     /**
78      * Change la couleur de dessin.
79      *
80      * @param color         couleur
81      *
82      * @see java.awt.Color
83      * @see #setColor(String)
84      * @see #setColor(float, float, float)
85      * @see #setBgColor(Color)
86      */
87     public void setColor(Color color) {
88         graphics.setColor(color);
89     }
90
91     /**
92      * Change la couleur de dessin.
93      *
94      * Le nom de couleur est de la forme "black", "white", "red", "blue", ...
95      *
96      * @param name          nom de couleur
97      *
98      * @see #setColor(Color)
99      * @see #setColor(float, float, float)
100      * @see #setBgColor(String)
101      */
102     public void setColor(String name) {
103         try {
104             Field field = Class.forName("java.awt.Color").getField(name);
105             graphics.setColor((Color)field.get(null));
106         } catch (Exception e) {
107             System.err.println("Warning: color not found: " + name);
108         }
109     }
110
111     /**
112      * Change la couleur de dessin.
113      *
114      * Les composantes de rouge, vert et bleu de la couleur doivent être
115      * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
116      * du noir; si les trois composantes sont à 1, on obtient du blanc.
117      *
118      * @param red           composante de rouge
119      * @param green         composante de vert
120      * @param blue          composante de bleu
121      *
122      * @see #setColor(Color)
123      * @see #setColor(String)
124      * @see #setBgColor(float, float, float)
125      */
126     public void setColor(float red, float green, float blue) {
127         setColor(new Color(red, green, blue));
128     }
129
130     /**
131      * Change la couleur de fond.
132      *
133      * @param color         couleur
134      *
135      * @see #setBgColor(String)
136      * @see #setBgColor(float, float, float)
137      * @see #setColor(Color)
138      * @see #clearGraph()
139      */
140     public void setBgColor(Color color) {
141         bgColor = color;
142     }
143
144     /**
145      * Change la couleur de fond.
146      *
147      * @param name          nom de couleur
148      *
149      * @see #setBgColor(Color)
150      * @see #setBgColor(float, float, float)
151      * @see #setColor(String)
152      * @see #clearGraph()
153      */
154     public void setBgColor(String name) {
155         try {
156             Field field = Class.forName("java.awt.Color").getField(name);
157             bgColor = (Color)field.get(null);
158         } catch (Exception e) {
159             System.err.println("Warning: color not found: " + name);
160         }
161     }
162
163     /** Change la couleur de fond.
164      *
165      * @param red           composante de rouge
166      * @param green         composante de vert
167      * @param blue          composante de bleu
168      *
169      * @see #setBgColor(Color)
170      * @see #setBgColor(String)
171      * @see #setColor(float, float, float)
172      * @see #clearGraph()
173      */
174     public void setBgColor(float red, float green, float blue) {
175         bgColor = new Color(red, green, blue);
176     }
177
178     /**
179      * Efface la fenêtre.
180      *
181      * La fenêtre est effacée avec la couleur de fond courante.
182      *
183      * @see #setBgColor
184      */
185     public void clearGraph() {
186         synchronized (image) {
187             Color c = graphics.getColor();
188             graphics.setColor(bgColor);
189             graphics.fillRect(0, 0, width, height);
190             graphics.setColor(c);
191         }
192         panel.repaint();
193     }
194
195     /** Dessine un point.
196      *
197      * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
198      * dessin courante.
199      *
200      * @see #setColor
201      */
202     public void drawPoint(int x, int y) {
203         synchronized (image) {
204             image.setRGB(x, y, graphics.getColor().getRGB());
205         }
206         panel.repaint(x, y, 1, 1);
207     }
208
209     /**
210      * Dessine un segment.
211      *
212      * Dessine un segement de droite entre les coordonnées (x1, y1) et
213      * (x2, y2), avec la couleur de dessin courante.
214      *
215      * @see #setColor
216      */
217     public void drawLine(int x1, int y1, int x2, int y2) {
218         synchronized (image) {
219             graphics.drawLine(x1, y1, x2, y2);
220         }
221         panel.repaint(Math.min(x1, x2), Math.min(y1, y2),
222                       Math.abs(x1 - x2) + 1, Math.abs(y1 - y2) + 1);
223     }
224
225     /** Dessine un rectangle.
226      *
227      * Dessine le rectangle parallèle aux axes et défini par les
228      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
229      * la couleur de dessin courante.
230      *
231      * @see #fillRect
232      * @see #setColor
233      */
234     public void drawRect(int x1, int y1, int x2, int y2) {
235         int x = Math.min(x1, x2);
236         int y = Math.min(y1, y2);
237         int w = Math.abs(x1 - x2);
238         int h = Math.abs(y1 - y2);
239         synchronized (image) {
240             graphics.drawRect(x, y, w, h);
241         }
242         panel.repaint(x, y, w, h);
243     }
244
245     /** Dessine un rectangle plein.
246      *
247      * Dessine le rectangle plein parallèle aux axes et défini par les
248      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
249      * la couleur de dessin courante.
250      *
251      * @see #drawRect
252      * @see #setColor
253      */
254     public void fillRect(int x1, int y1, int x2, int y2) {
255         int x = Math.min(x1, x2);
256         int y = Math.min(y1, y2);
257         int w = Math.abs(x1 - x2);
258         int h = Math.abs(y1 - y2);
259         synchronized (image) {
260             graphics.fillRect(x, y, w, h);
261         }
262         panel.repaint(x, y, w, h);
263     }
264
265     /**
266      * Dessine un cercle.
267      *
268      * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
269      * couleur de dessin courante.
270      *
271      * @see #fillCircle
272      * @see #setColor
273      */
274     public void drawCircle(int x, int y, int r) {
275         synchronized (image) {
276             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
277         }
278         panel.repaint(x - r, y - r, 2 * r, 2 * r);
279     }
280
281     /**
282      * Dessine un disque.
283      *
284      * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
285      * Utilise la couleur de dessin courante.
286      *
287      * @see #drawCircle
288      * @see #setColor
289      */
290     public void fillCircle(int x, int y, int r) {
291         synchronized (image) {
292             graphics.fillOval(x - r, y - r, 2 * r, 2 * r);
293         }
294         panel.repaint(x - r, y - r, 2 * r, 2 * r);
295     }
296
297     /**
298      * Écrit du texte.
299      *
300      * Écrit le texte text, aux coordonnées (x, y).
301      */
302     public void drawText(int x, int y, String text) {
303         synchronized (image) {
304             graphics.drawString(text, x, y);
305         }
306         panel.repaint(); // don't know how to calculate tighter bounding box
307     }
308
309     /**
310      * Retourne la couleur d'un pixel.
311      *
312      * Retourne la couleur du pixel de coordonnées (x, y).
313      *
314      * @return              couleur du pixel
315      */
316     public int getPointColor(int x, int y) {
317         return image.getRGB(x, y);
318     }
319
320     /**
321      * Synchronise le contenu de la fenêtre.
322      *
323      * Pour des raisons d'efficacités, le résultat des fonctions de dessin
324      * n'est pas affiché immédiatement.  L'appel à sync permet de
325      * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
326      * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
327      * soit à jour.
328      */
329     public void sync() {
330         // put an empty action on the event queue, and  wait for its completion
331         try {
332             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
333                     public void run() { }
334                 });
335         }
336         catch (Exception e) {
337         }
338     }
339
340     /**
341      *  Ferme la fenêtre graphique.
342      */
343     public void closeGraph() {
344         javax.swing.SwingUtilities.invokeLater(new Runnable() {
345                 public void run() {
346                     WindowEvent ev =
347                         new WindowEvent(frame,
348                                         WindowEvent.WINDOW_CLOSING);
349                     Toolkit.getDefaultToolkit()
350                         .getSystemEventQueue().postEvent(ev);
351                 }
352             });
353     }
354
355
356     /**
357      * Suspend l'exécution pendant un certain temps.
358      *
359      * @param secs          temps d'attente en seconde
360      */
361     static void sleep(long secs) {
362         try {
363             Thread.sleep(secs * 1000);
364         }
365         catch (Exception e) {
366         }
367     }
368
369     /**
370      * Suspend l'exécution pendant un certain temps.
371      *
372      * @param msecs          temps d'attente en millisecondes
373      */
374     static void msleep(long msecs) {
375         try {
376             Thread.sleep(msecs);
377         }
378         catch (Exception e) {
379         }
380     }
381
382     /**
383      * Suspend l'exécution pendant un certain temps.
384      *
385      * @param usecs          temps d'attente en microsecondes
386      */
387     static void usleep(long usecs) {
388         try {
389             Thread.sleep(usecs / 1000, (int)(usecs % 1000) * 1000);
390         }
391         catch (Exception e) {
392         }
393     }
394
395     /* PRIVATE STUFF FOLLOWS */
396
397     private final String title; // window's title
398     private JFrame frame;       // the frame (window)
399     private DWPanel panel;      // the panel showing the image
400     private BufferedImage image; // the image we draw into
401     private Graphics2D graphics; // graphics associated with image
402     private Color bgColor;       // background color, for clearGraph()
403
404     // To be run on the Event Dispatching Thread
405     void createGUI() {
406         panel = new DWPanel(this);
407
408         frame = new JFrame(title);
409         frame.add(panel);
410         frame.pack();
411         frame.setResizable(false);
412         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
413         frame.addKeyListener(panel);
414         frame.setLocationByPlatform(true);
415         frame.setVisible(true);
416     }
417
418     private class DWPanel extends JPanel implements KeyListener {
419
420         private static final long serialVersionUID = 0;
421
422         final DrawingWindow w;
423
424         DWPanel(DrawingWindow w) {
425             this.w = w;
426             Dimension dimension = new Dimension(w.width, w.height);
427             super.setMinimumSize(dimension);
428             super.setMaximumSize(dimension);
429             super.setPreferredSize(dimension);
430         }
431
432         public void paint(Graphics g) {
433             synchronized (w.image) {
434                 g.drawImage(w.image, 0, 0, null);
435             }
436         }
437
438         public void keyPressed(KeyEvent e) {
439             if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
440                 w.closeGraph();
441             }
442         }
443
444         public void keyReleased(KeyEvent e) { }
445         public void keyTyped(KeyEvent e) { }
446
447     }
448 }