Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Improve tests.
[graphlib_java.git] / DrawingWindow.java
1 import java.util.Collections;
2 import java.util.HashMap;
3 import java.util.Map;
4 import java.awt.Color;
5 import java.awt.Dimension;
6 import java.awt.Graphics2D;
7 import java.awt.Graphics;
8 import java.awt.Point;
9 import java.awt.Polygon;
10 import java.awt.Toolkit;
11 import java.awt.event.KeyAdapter;
12 import java.awt.event.KeyEvent;
13 import java.awt.event.MouseAdapter;
14 import java.awt.event.MouseEvent;
15 import java.awt.event.WindowAdapter;
16 import java.awt.event.WindowEvent;
17 import java.awt.image.BufferedImage;
18 import javax.swing.JFrame;
19 import javax.swing.JPanel;
20
21 /**
22  * Fenêtre de dessin
23  *
24  * <p>Cette classe permet d'écrire des applications graphiques simples
25  * en dessinant dans une fenêtre.
26  *
27  * <p><b>NB.</b> Pour toutes les méthodes de dessin, le coin en haut à
28  * gauche de la fenêtre a les coordonnées (0, 0).  Le coin en bas à
29  * droite de la fenêtre a les coordonnées (largeur - 1, hauteur - 1),
30  * si la fenêtre est de dimension largeur × hauteur.
31  *
32  * <p>Un appui sur la touche &lt;Esc&gt; provoque la fermeture de la
33  * fenêtre.  Comme pour la plupart des applications, il est également
34  * possible de fermer la fenêtre via le gestionnaire de fenêtres.
35  *
36  * @author Arnaud Giersch &lt;arnaud.giersch@univ-fcomte.fr&gt;
37  * @version 20141021b
38  */
39 public class DrawingWindow {
40
41     /** Largeur de la fenêtre */
42     public final int width;
43
44     /** Hauteur de la fenêtre */
45     public final int height;
46
47     /**
48      * Construit une nouvelle fenêtre de dessin avec le titre et les dimensions
49      * passés en paramètres.
50      *
51      * @param title             titre de la fenêtre
52      * @param width             largeur de la fenêtre
53      * @param height            hauteur de la fenêtre
54      *
55      * @see javax.swing.JPanel
56      */
57     public DrawingWindow(String title, int width, int height) {
58
59         this.title = new String(title);
60         this.width = width;
61         this.height = height;
62
63         mouseLock = new Object();
64         mouseEvent = null;
65         mousePos = new Point(0, 0);
66         mouseButton = 0;
67
68         image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
69         graphics = image.createGraphics();
70
71         try {
72             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
73                     public void run() { createGUI(); }
74                 });
75         }
76         catch (Exception e) {
77             System.err.println("Error: interrupted while creating GUI");
78             System.err.println("Got exception: " + e);
79             System.exit(1);
80         }
81
82         setColor(Color.BLACK);
83         setBgColor(Color.WHITE);
84         clearGraph();
85         sync();
86     }
87
88     /**
89      * Change la couleur de dessin.
90      *
91      * @param color         couleur
92      *
93      * @see java.awt.Color
94      * @see #setColor(int)
95      * @see #setColor(String)
96      * @see #setColor(float, float, float)
97      * @see #setBgColor(Color)
98      */
99     public void setColor(Color color) {
100         graphics.setColor(color);
101     }
102
103     /**
104      * Change la couleur de dessin.
105      *
106      * La couleur est un entier, tel que retourné par {@link #getPointColor}.
107      * Normalement de la forme #00RRGGBB.
108      *
109      * @param rgb       couleur
110      *
111      * @see #setColor(String)
112      * @see #setColor(float, float, float)
113      * @see #setBgColor(int)
114      * @see #getPointColor
115      */
116     public void setColor(int rgb) {
117         setColor(new Color(rgb));
118     }
119
120     /**
121      * Change la couleur de dessin.
122      *
123      * @param name          nom de couleur
124      *
125      * @see #setColor(int)
126      * @see #setColor(float, float, float)
127      * @see #setBgColor(String)
128      * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">liste des noms de couleurs</a>
129      */
130     public void setColor(String name) {
131         Color color = colorMap.get(name);
132         if (color != null)
133             setColor(color);
134         else
135             System.err.println("Warning: color not found: " + name);
136     }
137
138     /**
139      * Change la couleur de dessin.
140      *
141      * Les composantes de rouge, vert et bleu de la couleur doivent être
142      * compris entre 0 et 1.  Si le trois composantes sont à 0, on obtient
143      * du noir; si les trois composantes sont à 1, on obtient du blanc.
144      *
145      * @param red           composante de rouge
146      * @param green         composante de vert
147      * @param blue          composante de bleu
148      *
149      * @see #setColor(int)
150      * @see #setColor(String)
151      * @see #setBgColor(float, float, float)
152      */
153     public void setColor(float red, float green, float blue) {
154         setColor(new Color(red, green, blue));
155     }
156
157     /**
158      * Change la couleur de fond.
159      *
160      * @param color         couleur
161      *
162      * @see #setBgColor(int)
163      * @see #setBgColor(String)
164      * @see #setBgColor(float, float, float)
165      * @see #setColor(Color)
166      * @see #clearGraph
167      */
168     public void setBgColor(Color color) {
169         bgColor = color;
170     }
171
172     /** Change la couleur de fond.
173      *
174      * @param rgb       couleur
175      *
176      * @see #setBgColor(String)
177      * @see #setBgColor(float, float, float)
178      * @see #setColor(int)
179      * @see #getPointColor
180      * @see #clearGraph
181      */
182     public void setBgColor(int rgb) {
183         setBgColor(new Color(rgb));
184     }
185
186     /**
187      * Change la couleur de fond.
188      *
189      * @param name          nom de couleur
190      *
191      * @see #setBgColor(int)
192      * @see #setBgColor(float, float, float)
193      * @see #setColor(String)
194      * @see #clearGraph
195      * @see <a href="http://www.w3.org/TR/SVG/types.html#ColorKeywords">liste des noms de couleurs</a>
196      */
197     public void setBgColor(String name) {
198         Color color = colorMap.get(name);
199         if (color != null)
200             setBgColor(color);
201         else
202             System.err.println("Warning: color not found: " + name);
203     }
204
205     /** Change la couleur de fond.
206      *
207      * @param red           composante de rouge
208      * @param green         composante de vert
209      * @param blue          composante de bleu
210      *
211      * @see #setBgColor(int)
212      * @see #setBgColor(String)
213      * @see #setColor(float, float, float)
214      * @see #clearGraph
215      */
216     public void setBgColor(float red, float green, float blue) {
217         setBgColor(new Color(red, green, blue));
218     }
219
220     /**
221      * Efface la fenêtre.
222      *
223      * La fenêtre est effacée avec la couleur de fond courante.
224      *
225      * @see #setBgColor
226      */
227     public void clearGraph() {
228         synchronized (image) {
229             Color c = graphics.getColor();
230             graphics.setColor(bgColor);
231             graphics.fillRect(0, 0, width, height);
232             graphics.setColor(c);
233         }
234         panel.repaint();
235     }
236
237     /** Dessine un point.
238      *
239      * Dessine un point (pixel) aux coordonnées (x, y), avec la couleur de
240      * dessin courante.
241      *
242      * @see #setColor
243      */
244     public void drawPoint(int x, int y) {
245         if (x < 0 || y < 0 || x >= width || y >= height)
246             return;
247         synchronized (image) {
248             image.setRGB(x, y, graphics.getColor().getRGB());
249         }
250         panel.repaint(x, y, 1, 1);
251     }
252
253     /**
254      * Dessine un segment.
255      *
256      * Dessine un segement de droite entre les coordonnées (x1, y1) et
257      * (x2, y2), avec la couleur de dessin courante.
258      *
259      * @see #setColor
260      */
261     public void drawLine(int x1, int y1, int x2, int y2) {
262         synchronized (image) {
263             graphics.drawLine(x1, y1, x2, y2);
264         }
265         panel.repaint(Math.min(x1, x2), Math.min(y1, y2),
266                       Math.abs(x1 - x2) + 1, Math.abs(y1 - y2) + 1);
267     }
268
269     /** Dessine un rectangle.
270      *
271      * Dessine le rectangle parallèle aux axes et défini par les
272      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
273      * la couleur de dessin courante.
274      *
275      * @see #fillRect
276      * @see #setColor
277      */
278     public void drawRect(int x1, int y1, int x2, int y2) {
279         int x = Math.min(x1, x2);
280         int y = Math.min(y1, y2);
281         int w = Math.abs(x1 - x2);
282         int h = Math.abs(y1 - y2);
283         synchronized (image) {
284             graphics.drawRect(x, y, w, h);
285         }
286         panel.repaint(x, y, w + 1, h + 1);
287     }
288
289     /** Dessine un rectangle plein.
290      *
291      * Dessine le rectangle plein parallèle aux axes et défini par les
292      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
293      * la couleur de dessin courante.
294      *
295      * @see #drawRect
296      * @see #setColor
297      */
298     public void fillRect(int x1, int y1, int x2, int y2) {
299         int x = Math.min(x1, x2);
300         int y = Math.min(y1, y2);
301         int w = Math.abs(x1 - x2) + 1;
302         int h = Math.abs(y1 - y2) + 1;
303         synchronized (image) {
304             graphics.fillRect(x, y, w, h);
305         }
306         panel.repaint(x, y, w, h);
307     }
308
309     /**
310      * Dessine un cercle.
311      *
312      * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
313      * couleur de dessin courante.
314      *
315      * @see #fillCircle
316      * @see #setColor
317      */
318     public void drawCircle(int x, int y, int r) {
319         synchronized (image) {
320             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
321         }
322         panel.repaint(x - r, y - r, 2 * r + 1, 2 * r + 1);
323     }
324
325     /**
326      * Dessine un disque.
327      *
328      * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
329      * Utilise la couleur de dessin courante.
330      *
331      * @see #drawCircle
332      * @see #setColor
333      */
334     public void fillCircle(int x, int y, int r) {
335         synchronized (image) {
336             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
337             graphics.fillOval(x - r, y - r, 2 * r, 2 * r);
338         }
339         panel.repaint(x - r, y - r, 2 * r + 1, 2 * r + 1);
340     }
341
342     /**
343      * Dessine un triangle.
344      *
345      * Dessine un triangle défini par les coordonnées de ses sommets:
346      * (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de dessin
347      * courante.
348      *
349      * @see #fillTriangle
350      * @see #setColor
351      */
352
353     public void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
354         Polygon poly = new Polygon();
355         poly.addPoint(x1, y1);
356         poly.addPoint(x2, y2);
357         poly.addPoint(x3, y3);
358         synchronized (image) {
359             graphics.drawPolygon(poly);
360         }
361         panel.repaint(poly.getBounds());
362     }
363
364     /**
365      * Dessine un triangle plein.
366      *
367      * Dessine un triangle plein défini par les coordonnées de ses
368      * sommets: (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de
369      * dessin courante.
370      *
371      * @see #drawTriangle
372      * @see #setColor
373      */
374     public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
375         Polygon poly = new Polygon();
376         poly.addPoint(x1, y1);
377         poly.addPoint(x2, y2);
378         poly.addPoint(x3, y3);
379         synchronized (image) {
380             graphics.drawPolygon(poly);
381             graphics.fillPolygon(poly);
382         }
383         panel.repaint(poly.getBounds());
384     }
385
386     /**
387      * Écrit du texte.
388      *
389      * Écrit le texte text, aux coordonnées (x, y).
390      */
391     public void drawText(int x, int y, String text) {
392         synchronized (image) {
393             graphics.drawString(text, x, y);
394         }
395         panel.repaint(); // don't know how to calculate tighter bounding box
396     }
397
398     /**
399      * Retourne la couleur d'un pixel.
400      *
401      * Retourne la couleur du pixel de coordonnées (x, y).
402      *
403      * @return              couleur du pixel
404      *
405      * @see #setColor(int)
406      * @see #setBgColor(int)
407      */
408     public int getPointColor(int x, int y) {
409         return (x < 0 || y < 0 || x >= width || y >= height) ?
410             0 : image.getRGB(x, y) & 0x00ffffff;
411     }
412
413     /**
414      * Attend l'appui sur un des boutons de la souris.
415      *
416      * @return vrai (true) si un bouton a été pressé
417      *
418      * @see #waitMousePress(long)
419      * @see #getMouseX
420      * @see #getMouseY
421      * @see #getMouseButton
422      */
423     public boolean waitMousePress() {
424         return waitMousePress(-1);
425     }
426
427     /**
428      * Attend l'appui sur un des boutons de la souris.
429      *
430      * @param timeout   temps maximal d'attente (millisecondes)
431      *
432      * @return vrai (true) si un bouton a été pressé
433      *
434      * @see #waitMousePress()
435      * @see #getMouseX
436      * @see #getMouseY
437      * @see #getMouseButton
438      */
439     public boolean waitMousePress(long timeout) {
440         boolean result = false;
441         synchronized (mouseLock) {
442             if (timeout != 0) {
443                 mouseEvent = null;
444                 try {
445                     if (timeout > 0)
446                         mouseLock.wait(timeout);
447                     else // (timeout < 0)
448                         mouseLock.wait();
449                 }
450                 catch (InterruptedException e) {
451                 }
452             }
453             if (mouseEvent != null) {
454                 mousePos.setLocation(mouseEvent.getPoint());
455                 mouseButton = mouseEvent.getButton();
456                 mouseEvent = null;
457                 result = true;
458             }
459         }
460         return result;
461     }
462
463     /**
464      * Retourne la position (x) de la souris la dernière fois qu'un
465      * bouton a été pressé pendant l'appel à {@link #waitMousePress()}.
466      *
467      * @return position (x)
468      */
469     public int getMouseX() {
470         return mousePos.x;
471     }
472
473     /**
474      * Retourne la position (y) de la souris la dernière fois qu'un
475      * bouton a été pressé pendant l'appel à {@link #waitMousePress()}.
476      *
477      * @return position (y)
478      */
479     public int getMouseY() {
480         return mousePos.y;
481     }
482
483     /**
484      * Retourne le numéro du bouton de la souris pressé pendant
485      * le dernier appel à {@link #waitMousePress()}.
486      *
487      * @return numéro de bouton (1: gauche, 2: milieu, 3: droit)
488      */
489     public int getMouseButton() {
490         return mouseButton;
491     }
492
493     /**
494      * Synchronise le contenu de la fenêtre.
495      *
496      * Pour des raisons d'efficacités, le résultat des fonctions de dessin
497      * n'est pas affiché immédiatement.  L'appel à sync permet de
498      * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
499      * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
500      * soit à jour.
501      */
502     public void sync() {
503         // put an empty action on the event queue, and  wait for its completion
504         try {
505             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
506                     public void run() { }
507                 });
508         }
509         catch (Exception e) {
510         }
511     }
512
513     /**
514      *  Ferme la fenêtre graphique.
515      */
516     public void closeGraph() {
517         javax.swing.SwingUtilities.invokeLater(new Runnable() {
518                 public void run() {
519                     WindowEvent ev =
520                         new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
521                     Toolkit.getDefaultToolkit()
522                         .getSystemEventQueue().postEvent(ev);
523                 }
524             });
525     }
526
527
528     /**
529      * Suspend l'exécution pendant un certain temps.
530      *
531      * @param secs          temps d'attente en seconde
532      *
533      * @see #msleep
534      * @see #usleep
535      */
536     public static void sleep(long secs) {
537         try {
538             Thread.sleep(secs * 1000);
539         }
540         catch (Exception e) {
541         }
542     }
543
544     /**
545      * Suspend l'exécution pendant un certain temps.
546      *
547      * @param msecs          temps d'attente en millisecondes
548      *
549      * @see #sleep
550      * @see #usleep
551      */
552     public static void msleep(long msecs) {
553         try {
554             Thread.sleep(msecs);
555         }
556         catch (Exception e) {
557         }
558     }
559
560     /**
561      * Suspend l'exécution pendant un certain temps.
562      *
563      * @param usecs          temps d'attente en microsecondes
564      *
565      * @see #sleep
566      * @see #msleep
567      */
568     public static void usleep(long usecs) {
569         try {
570             Thread.sleep(usecs / 1000, (int)(usecs % 1000) * 1000);
571         }
572         catch (Exception e) {
573         }
574     }
575
576     /* PRIVATE STUFF FOLLOWS */
577
578     private static final Map<String, Color> colorMap;
579
580     static {
581         Map<String, Color> m = new HashMap<String, Color>();
582         // From http://www.w3.org/TR/SVG/types.html#ColorKeywords
583         m.put("aliceblue",              new Color(0x00f0f8ff));
584         m.put("antiquewhite",           new Color(0x00faebd7));
585         m.put("aqua",                   new Color(0x0000ffff));
586         m.put("aquamarine",             new Color(0x007fffd4));
587         m.put("azure",                  new Color(0x00f0ffff));
588         m.put("beige",                  new Color(0x00f5f5dc));
589         m.put("bisque",                 new Color(0x00ffe4c4));
590         m.put("black",                  new Color(0000000000));
591         m.put("blanchedalmond",         new Color(0x00ffebcd));
592         m.put("blue",                   new Color(0x000000ff));
593         m.put("blueviolet",             new Color(0x008a2be2));
594         m.put("brown",                  new Color(0x00a52a2a));
595         m.put("burlywood",              new Color(0x00deb887));
596         m.put("cadetblue",              new Color(0x005f9ea0));
597         m.put("chartreuse",             new Color(0x007fff00));
598         m.put("chocolate",              new Color(0x00d2691e));
599         m.put("coral",                  new Color(0x00ff7f50));
600         m.put("cornflowerblue",         new Color(0x006495ed));
601         m.put("cornsilk",               new Color(0x00fff8dc));
602         m.put("crimson",                new Color(0x00dc143c));
603         m.put("cyan",                   new Color(0x0000ffff));
604         m.put("darkblue",               new Color(0x0000008b));
605         m.put("darkcyan",               new Color(0x00008b8b));
606         m.put("darkgoldenrod",          new Color(0x00b8860b));
607         m.put("darkgray",               new Color(0x00a9a9a9));
608         m.put("darkgreen",              new Color(0x00006400));
609         m.put("darkgrey",               new Color(0x00a9a9a9));
610         m.put("darkkhaki",              new Color(0x00bdb76b));
611         m.put("darkmagenta",            new Color(0x008b008b));
612         m.put("darkolivegreen",         new Color(0x00556b2f));
613         m.put("darkorange",             new Color(0x00ff8c00));
614         m.put("darkorchid",             new Color(0x009932cc));
615         m.put("darkred",                new Color(0x008b0000));
616         m.put("darksalmon",             new Color(0x00e9967a));
617         m.put("darkseagreen",           new Color(0x008fbc8f));
618         m.put("darkslateblue",          new Color(0x00483d8b));
619         m.put("darkslategray",          new Color(0x002f4f4f));
620         m.put("darkslategrey",          new Color(0x002f4f4f));
621         m.put("darkturquoise",          new Color(0x0000ced1));
622         m.put("darkviolet",             new Color(0x009400d3));
623         m.put("deeppink",               new Color(0x00ff1493));
624         m.put("deepskyblue",            new Color(0x0000bfff));
625         m.put("dimgray",                new Color(0x00696969));
626         m.put("dimgrey",                new Color(0x00696969));
627         m.put("dodgerblue",             new Color(0x001e90ff));
628         m.put("firebrick",              new Color(0x00b22222));
629         m.put("floralwhite",            new Color(0x00fffaf0));
630         m.put("forestgreen",            new Color(0x00228b22));
631         m.put("fuchsia",                new Color(0x00ff00ff));
632         m.put("gainsboro",              new Color(0x00dcdcdc));
633         m.put("ghostwhite",             new Color(0x00f8f8ff));
634         m.put("gold",                   new Color(0x00ffd700));
635         m.put("goldenrod",              new Color(0x00daa520));
636         m.put("gray",                   new Color(0x00808080));
637         m.put("grey",                   new Color(0x00808080));
638         m.put("green",                  new Color(0x00008000));
639         m.put("greenyellow",            new Color(0x00adff2f));
640         m.put("honeydew",               new Color(0x00f0fff0));
641         m.put("hotpink",                new Color(0x00ff69b4));
642         m.put("indianred",              new Color(0x00cd5c5c));
643         m.put("indigo",                 new Color(0x004b0082));
644         m.put("ivory",                  new Color(0x00fffff0));
645         m.put("khaki",                  new Color(0x00f0e68c));
646         m.put("lavender",               new Color(0x00e6e6fa));
647         m.put("lavenderblush",          new Color(0x00fff0f5));
648         m.put("lawngreen",              new Color(0x007cfc00));
649         m.put("lemonchiffon",           new Color(0x00fffacd));
650         m.put("lightblue",              new Color(0x00add8e6));
651         m.put("lightcoral",             new Color(0x00f08080));
652         m.put("lightcyan",              new Color(0x00e0ffff));
653         m.put("lightgoldenrodyellow",   new Color(0x00fafad2));
654         m.put("lightgray",              new Color(0x00d3d3d3));
655         m.put("lightgreen",             new Color(0x0090ee90));
656         m.put("lightgrey",              new Color(0x00d3d3d3));
657         m.put("",                       new Color(0000000000));
658         m.put("lightpink",              new Color(0x00ffb6c1));
659         m.put("lightsalmon",            new Color(0x00ffa07a));
660         m.put("lightseagreen",          new Color(0x0020b2aa));
661         m.put("lightskyblue",           new Color(0x0087cefa));
662         m.put("lightslategray",         new Color(0x00778899));
663         m.put("lightslategrey",         new Color(0x00778899));
664         m.put("lightsteelblue",         new Color(0x00b0c4de));
665         m.put("lightyellow",            new Color(0x00ffffe0));
666         m.put("lime",                   new Color(0x0000ff00));
667         m.put("limegreen",              new Color(0x0032cd32));
668         m.put("linen",                  new Color(0x00faf0e6));
669         m.put("magenta",                new Color(0x00ff00ff));
670         m.put("maroon",                 new Color(0x00800000));
671         m.put("mediumaquamarine",       new Color(0x0066cdaa));
672         m.put("mediumblue",             new Color(0x000000cd));
673         m.put("mediumorchid",           new Color(0x00ba55d3));
674         m.put("mediumpurple",           new Color(0x009370db));
675         m.put("mediumseagreen",         new Color(0x003cb371));
676         m.put("mediumslateblue",        new Color(0x007b68ee));
677         m.put("mediumspringgreen",      new Color(0x0000fa9a));
678         m.put("mediumturquoise",        new Color(0x0048d1cc));
679         m.put("mediumvioletred",        new Color(0x00c71585));
680         m.put("midnightblue",           new Color(0x00191970));
681         m.put("mintcream",              new Color(0x00f5fffa));
682         m.put("mistyrose",              new Color(0x00ffe4e1));
683         m.put("moccasin",               new Color(0x00ffe4b5));
684         m.put("navajowhite",            new Color(0x00ffdead));
685         m.put("navy",                   new Color(0x00000080));
686         m.put("oldlace",                new Color(0x00fdf5e6));
687         m.put("olive",                  new Color(0x00808000));
688         m.put("olivedrab",              new Color(0x006b8e23));
689         m.put("orange",                 new Color(0x00ffa500));
690         m.put("orangered",              new Color(0x00ff4500));
691         m.put("orchid",                 new Color(0x00da70d6));
692         m.put("palegoldenrod",          new Color(0x00eee8aa));
693         m.put("palegreen",              new Color(0x0098fb98));
694         m.put("paleturquoise",          new Color(0x00afeeee));
695         m.put("palevioletred",          new Color(0x00db7093));
696         m.put("papayawhip",             new Color(0x00ffefd5));
697         m.put("peachpuff",              new Color(0x00ffdab9));
698         m.put("peru",                   new Color(0x00cd853f));
699         m.put("pink",                   new Color(0x00ffc0cb));
700         m.put("plum",                   new Color(0x00dda0dd));
701         m.put("powderblue",             new Color(0x00b0e0e6));
702         m.put("purple",                 new Color(0x00800080));
703         m.put("red",                    new Color(0x00ff0000));
704         m.put("rosybrown",              new Color(0x00bc8f8f));
705         m.put("royalblue",              new Color(0x004169e1));
706         m.put("saddlebrown",            new Color(0x008b4513));
707         m.put("salmon",                 new Color(0x00fa8072));
708         m.put("sandybrown",             new Color(0x00f4a460));
709         m.put("seagreen",               new Color(0x002e8b57));
710         m.put("seashell",               new Color(0x00fff5ee));
711         m.put("sienna",                 new Color(0x00a0522d));
712         m.put("silver",                 new Color(0x00c0c0c0));
713         m.put("skyblue",                new Color(0x0087ceeb));
714         m.put("slateblue",              new Color(0x006a5acd));
715         m.put("slategray",              new Color(0x00708090));
716         m.put("slategrey",              new Color(0x00708090));
717         m.put("snow",                   new Color(0x00fffafa));
718         m.put("springgreen",            new Color(0x0000ff7f));
719         m.put("steelblue",              new Color(0x004682b4));
720         m.put("tan",                    new Color(0x00d2b48c));
721         m.put("teal",                   new Color(0x00008080));
722         m.put("thistle",                new Color(0x00d8bfd8));
723         m.put("tomato",                 new Color(0x00ff6347));
724         m.put("turquoise",              new Color(0x0040e0d0));
725         m.put("violet",                 new Color(0x00ee82ee));
726         m.put("wheat",                  new Color(0x00f5deb3));
727         m.put("white",                  new Color(0x00ffffff));
728         m.put("whitesmoke",             new Color(0x00f5f5f5));
729         m.put("yellow",                 new Color(0x00ffff00));
730         m.put("yellowgreen",            new Color(0x009acd32));
731         colorMap = Collections.unmodifiableMap(m);
732     }
733
734     private static int instances = 0;
735
736     private final String title; // window's title
737     private JFrame frame;       // the frame (window)
738     private DWPanel panel;      // the panel showing the image
739     private BufferedImage image; // the image we draw into
740     private Graphics2D graphics; // graphics associated with image
741     private Color bgColor;       // background color, for clearGraph()
742     private boolean isClosed;    // is the window closed ?
743     private Object mouseLock;       // lock for mouse events
744     private MouseEvent mouseEvent;  // last mouse event
745     private Point mousePos;         // last mouse position
746     private int mouseButton;        // last mouse click
747
748     // To be run on the Event Dispatching Thread
749     void createGUI() {
750         panel = new DWPanel();
751         frame = new JFrame(title);
752         frame.add(panel);
753         frame.pack();
754         frame.setResizable(false);
755         frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
756         frame.addWindowListener(new DWWindowHandler());
757         frame.addKeyListener(new DWKeyHandler());
758         panel.addMouseListener(new DWMouseHandler());
759         frame.setLocationByPlatform(true);
760         frame.setVisible(true);
761     }
762
763     private class DWWindowHandler extends WindowAdapter {
764         public void windowOpened(WindowEvent ev) {
765             DrawingWindow w = DrawingWindow.this;
766             DrawingWindow.instances++;
767             w.isClosed = false;
768         }
769
770         public void windowClosed(WindowEvent ev) {
771             DrawingWindow w = DrawingWindow.this;
772             if (!w.isClosed) {
773                 w.isClosed = true;
774                 if (DrawingWindow.instances <= 0)
775                     throw new AssertionError("Bad instance counter: " +
776                                              DrawingWindow.instances);
777                 DrawingWindow.instances--;
778                 if (DrawingWindow.instances == 0)
779                     System.exit(0);
780             }
781         }
782     }
783
784     private class DWKeyHandler extends KeyAdapter {
785         public void keyPressed(KeyEvent ev) {
786             DrawingWindow w = DrawingWindow.this;
787             if (ev.getKeyCode() == KeyEvent.VK_ESCAPE) {
788                 w.closeGraph();
789             }
790         }
791     }
792
793     private class DWMouseHandler extends MouseAdapter {
794         public void mousePressed(MouseEvent ev) {
795             DrawingWindow w = DrawingWindow.this;
796             synchronized (w.mouseLock) {
797                 w.mouseEvent = ev;
798                 mouseLock.notifyAll();
799             }
800         }
801     }
802
803     private class DWPanel extends JPanel {
804         DWPanel() {
805             DrawingWindow w = DrawingWindow.this;
806             Dimension dimension = new Dimension(w.width, w.height);
807             super.setMinimumSize(dimension);
808             super.setMaximumSize(dimension);
809             super.setPreferredSize(dimension);
810         }
811
812         public void paint(Graphics g) {
813             DrawingWindow w = DrawingWindow.this;
814             synchronized (w.image) {
815                 g.drawImage(w.image, 0, 0, null);
816             }
817         }
818
819         private static final long serialVersionUID = 0;
820     }
821 }