Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
1b93c57457ce9248ca02aade8f5efbe45a93870d
[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 20141021
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         synchronized (image) {
246             image.setRGB(x, y, graphics.getColor().getRGB());
247         }
248         panel.repaint(x, y, 1, 1);
249     }
250
251     /**
252      * Dessine un segment.
253      *
254      * Dessine un segement de droite entre les coordonnées (x1, y1) et
255      * (x2, y2), avec la couleur de dessin courante.
256      *
257      * @see #setColor
258      */
259     public void drawLine(int x1, int y1, int x2, int y2) {
260         synchronized (image) {
261             graphics.drawLine(x1, y1, x2, y2);
262         }
263         panel.repaint(Math.min(x1, x2), Math.min(y1, y2),
264                       Math.abs(x1 - x2) + 1, Math.abs(y1 - y2) + 1);
265     }
266
267     /** Dessine un rectangle.
268      *
269      * Dessine le rectangle parallèle aux axes et défini par les
270      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
271      * la couleur de dessin courante.
272      *
273      * @see #fillRect
274      * @see #setColor
275      */
276     public void drawRect(int x1, int y1, int x2, int y2) {
277         int x = Math.min(x1, x2);
278         int y = Math.min(y1, y2);
279         int w = Math.abs(x1 - x2);
280         int h = Math.abs(y1 - y2);
281         synchronized (image) {
282             graphics.drawRect(x, y, w, h);
283         }
284         panel.repaint(x, y, w + 1, h + 1);
285     }
286
287     /** Dessine un rectangle plein.
288      *
289      * Dessine le rectangle plein parallèle aux axes et défini par les
290      * coordonnées de deux sommets opposés (x1, y1) et (x2, y2).  Utilise
291      * la couleur de dessin courante.
292      *
293      * @see #drawRect
294      * @see #setColor
295      */
296     public void fillRect(int x1, int y1, int x2, int y2) {
297         int x = Math.min(x1, x2);
298         int y = Math.min(y1, y2);
299         int w = Math.abs(x1 - x2) + 1;
300         int h = Math.abs(y1 - y2) + 1;
301         synchronized (image) {
302             graphics.fillRect(x, y, w, h);
303         }
304         panel.repaint(x, y, w, h);
305     }
306
307     /**
308      * Dessine un cercle.
309      *
310      * Dessine un cercle de centre (x, y) et de rayon r.  Utilise la
311      * couleur de dessin courante.
312      *
313      * @see #fillCircle
314      * @see #setColor
315      */
316     public void drawCircle(int x, int y, int r) {
317         synchronized (image) {
318             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
319         }
320         panel.repaint(x - r, y - r, 2 * r + 1, 2 * r + 1);
321     }
322
323     /**
324      * Dessine un disque.
325      *
326      * Dessine un disque (cercle plein) de centre (x, y) et de rayon r.
327      * Utilise la couleur de dessin courante.
328      *
329      * @see #drawCircle
330      * @see #setColor
331      */
332     public void fillCircle(int x, int y, int r) {
333         synchronized (image) {
334             graphics.drawOval(x - r, y - r, 2 * r, 2 * r);
335             graphics.fillOval(x - r, y - r, 2 * r, 2 * r);
336         }
337         panel.repaint(x - r, y - r, 2 * r + 1, 2 * r + 1);
338     }
339
340     /**
341      * Dessine un triangle.
342      *
343      * Dessine un triangle défini par les coordonnées de ses sommets:
344      * (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de dessin
345      * courante.
346      *
347      * @see #fillTriangle
348      * @see #setColor
349      */
350
351     public void drawTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
352         Polygon poly = new Polygon();
353         poly.addPoint(x1, y1);
354         poly.addPoint(x2, y2);
355         poly.addPoint(x3, y3);
356         synchronized (image) {
357             graphics.drawPolygon(poly);
358         }
359         panel.repaint(poly.getBounds());
360     }
361
362     /**
363      * Dessine un triangle plein.
364      *
365      * Dessine un triangle plein défini par les coordonnées de ses
366      * sommets: (x1, y1), (x2, y2) et (x3, y3).  Utilise la couleur de
367      * dessin courante.
368      *
369      * @see #drawTriangle
370      * @see #setColor
371      */
372     public void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) {
373         Polygon poly = new Polygon();
374         poly.addPoint(x1, y1);
375         poly.addPoint(x2, y2);
376         poly.addPoint(x3, y3);
377         synchronized (image) {
378             graphics.drawPolygon(poly);
379             graphics.fillPolygon(poly);
380         }
381         panel.repaint(poly.getBounds());
382     }
383
384     /**
385      * Écrit du texte.
386      *
387      * Écrit le texte text, aux coordonnées (x, y).
388      */
389     public void drawText(int x, int y, String text) {
390         synchronized (image) {
391             graphics.drawString(text, x, y);
392         }
393         panel.repaint(); // don't know how to calculate tighter bounding box
394     }
395
396     /**
397      * Retourne la couleur d'un pixel.
398      *
399      * Retourne la couleur du pixel de coordonnées (x, y).
400      *
401      * @return              couleur du pixel
402      *
403      * @see #setColor(int)
404      * @see #setBgColor(int)
405      */
406     public int getPointColor(int x, int y) {
407         return image.getRGB(x, y);
408     }
409
410     /**
411      * Attend l'appui sur un des boutons de la souris.
412      *
413      * @return vrai (true) si un bouton a été pressé
414      *
415      * @see #waitMousePress(long)
416      * @see #getMouseX
417      * @see #getMouseY
418      * @see #getMouseButton
419      */
420     public boolean waitMousePress() {
421         return waitMousePress(-1);
422     }
423
424     /**
425      * Attend l'appui sur un des boutons de la souris.
426      *
427      * @param timeout   temps maximal d'attente (millisecondes)
428      *
429      * @return vrai (true) si un bouton a été pressé
430      *
431      * @see #waitMousePress()
432      * @see #getMouseX
433      * @see #getMouseY
434      * @see #getMouseButton
435      */
436     public boolean waitMousePress(long timeout) {
437         boolean result = false;
438         synchronized (mouseLock) {
439             if (timeout != 0) {
440                 mouseEvent = null;
441                 try {
442                     if (timeout > 0)
443                         mouseLock.wait(timeout);
444                     else // (timeout < 0)
445                         mouseLock.wait();
446                 }
447                 catch (InterruptedException e) {
448                 }
449             }
450             if (mouseEvent != null) {
451                 mousePos.setLocation(mouseEvent.getPoint());
452                 mouseButton = mouseEvent.getButton();
453                 mouseEvent = null;
454                 result = true;
455             }
456         }
457         return result;
458     }
459
460     /**
461      * Retourne la position (x) de la souris la dernière fois qu'un
462      * bouton a été pressé pendant l'appel à {@link #waitMousePress()}.
463      *
464      * @return position (x)
465      */
466     public int getMouseX() {
467         return mousePos.x;
468     }
469
470     /**
471      * Retourne la position (y) de la souris la dernière fois qu'un
472      * bouton a été pressé pendant l'appel à {@link #waitMousePress()}.
473      *
474      * @return position (y)
475      */
476     public int getMouseY() {
477         return mousePos.y;
478     }
479
480     /**
481      * Retourne le numéro du bouton de la souris pressé pendant
482      * le dernier appel à {@link #waitMousePress()}.
483      *
484      * @return numéro de bouton (1: gauche, 2: milieu, 3: droit)
485      */
486     public int getMouseButton() {
487         return mouseButton;
488     }
489
490     /**
491      * Synchronise le contenu de la fenêtre.
492      *
493      * Pour des raisons d'efficacités, le résultat des fonctions de dessin
494      * n'est pas affiché immédiatement.  L'appel à sync permet de
495      * synchroniser le contenu de la fenêtre.  Autrement dit, cela bloque
496      * l'exécution du programme jusqu'à ce que le contenu de la fenêtre
497      * soit à jour.
498      */
499     public void sync() {
500         // put an empty action on the event queue, and  wait for its completion
501         try {
502             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
503                     public void run() { }
504                 });
505         }
506         catch (Exception e) {
507         }
508     }
509
510     /**
511      *  Ferme la fenêtre graphique.
512      */
513     public void closeGraph() {
514         javax.swing.SwingUtilities.invokeLater(new Runnable() {
515                 public void run() {
516                     WindowEvent ev =
517                         new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
518                     Toolkit.getDefaultToolkit()
519                         .getSystemEventQueue().postEvent(ev);
520                 }
521             });
522     }
523
524
525     /**
526      * Suspend l'exécution pendant un certain temps.
527      *
528      * @param secs          temps d'attente en seconde
529      *
530      * @see #msleep
531      * @see #usleep
532      */
533     public static void sleep(long secs) {
534         try {
535             Thread.sleep(secs * 1000);
536         }
537         catch (Exception e) {
538         }
539     }
540
541     /**
542      * Suspend l'exécution pendant un certain temps.
543      *
544      * @param msecs          temps d'attente en millisecondes
545      *
546      * @see #sleep
547      * @see #usleep
548      */
549     public static void msleep(long msecs) {
550         try {
551             Thread.sleep(msecs);
552         }
553         catch (Exception e) {
554         }
555     }
556
557     /**
558      * Suspend l'exécution pendant un certain temps.
559      *
560      * @param usecs          temps d'attente en microsecondes
561      *
562      * @see #sleep
563      * @see #msleep
564      */
565     public static void usleep(long usecs) {
566         try {
567             Thread.sleep(usecs / 1000, (int)(usecs % 1000) * 1000);
568         }
569         catch (Exception e) {
570         }
571     }
572
573     /* PRIVATE STUFF FOLLOWS */
574
575     private static final Map<String, Color> colorMap;
576
577     static {
578         Map<String, Color> m = new HashMap<String, Color>();
579         // From http://www.w3.org/TR/SVG/types.html#ColorKeywords
580         m.put("aliceblue",              new Color(0x00f0f8ff));
581         m.put("antiquewhite",           new Color(0x00faebd7));
582         m.put("aqua",                   new Color(0x0000ffff));
583         m.put("aquamarine",             new Color(0x007fffd4));
584         m.put("azure",                  new Color(0x00f0ffff));
585         m.put("beige",                  new Color(0x00f5f5dc));
586         m.put("bisque",                 new Color(0x00ffe4c4));
587         m.put("black",                  new Color(0000000000));
588         m.put("blanchedalmond",         new Color(0x00ffebcd));
589         m.put("blue",                   new Color(0x000000ff));
590         m.put("blueviolet",             new Color(0x008a2be2));
591         m.put("brown",                  new Color(0x00a52a2a));
592         m.put("burlywood",              new Color(0x00deb887));
593         m.put("cadetblue",              new Color(0x005f9ea0));
594         m.put("chartreuse",             new Color(0x007fff00));
595         m.put("chocolate",              new Color(0x00d2691e));
596         m.put("coral",                  new Color(0x00ff7f50));
597         m.put("cornflowerblue",         new Color(0x006495ed));
598         m.put("cornsilk",               new Color(0x00fff8dc));
599         m.put("crimson",                new Color(0x00dc143c));
600         m.put("cyan",                   new Color(0x0000ffff));
601         m.put("darkblue",               new Color(0x0000008b));
602         m.put("darkcyan",               new Color(0x00008b8b));
603         m.put("darkgoldenrod",          new Color(0x00b8860b));
604         m.put("darkgray",               new Color(0x00a9a9a9));
605         m.put("darkgreen",              new Color(0x00006400));
606         m.put("darkgrey",               new Color(0x00a9a9a9));
607         m.put("darkkhaki",              new Color(0x00bdb76b));
608         m.put("darkmagenta",            new Color(0x008b008b));
609         m.put("darkolivegreen",         new Color(0x00556b2f));
610         m.put("darkorange",             new Color(0x00ff8c00));
611         m.put("darkorchid",             new Color(0x009932cc));
612         m.put("darkred",                new Color(0x008b0000));
613         m.put("darksalmon",             new Color(0x00e9967a));
614         m.put("darkseagreen",           new Color(0x008fbc8f));
615         m.put("darkslateblue",          new Color(0x00483d8b));
616         m.put("darkslategray",          new Color(0x002f4f4f));
617         m.put("darkslategrey",          new Color(0x002f4f4f));
618         m.put("darkturquoise",          new Color(0x0000ced1));
619         m.put("darkviolet",             new Color(0x009400d3));
620         m.put("deeppink",               new Color(0x00ff1493));
621         m.put("deepskyblue",            new Color(0x0000bfff));
622         m.put("dimgray",                new Color(0x00696969));
623         m.put("dimgrey",                new Color(0x00696969));
624         m.put("dodgerblue",             new Color(0x001e90ff));
625         m.put("firebrick",              new Color(0x00b22222));
626         m.put("floralwhite",            new Color(0x00fffaf0));
627         m.put("forestgreen",            new Color(0x00228b22));
628         m.put("fuchsia",                new Color(0x00ff00ff));
629         m.put("gainsboro",              new Color(0x00dcdcdc));
630         m.put("ghostwhite",             new Color(0x00f8f8ff));
631         m.put("gold",                   new Color(0x00ffd700));
632         m.put("goldenrod",              new Color(0x00daa520));
633         m.put("gray",                   new Color(0x00808080));
634         m.put("grey",                   new Color(0x00808080));
635         m.put("green",                  new Color(0x00008000));
636         m.put("greenyellow",            new Color(0x00adff2f));
637         m.put("honeydew",               new Color(0x00f0fff0));
638         m.put("hotpink",                new Color(0x00ff69b4));
639         m.put("indianred",              new Color(0x00cd5c5c));
640         m.put("indigo",                 new Color(0x004b0082));
641         m.put("ivory",                  new Color(0x00fffff0));
642         m.put("khaki",                  new Color(0x00f0e68c));
643         m.put("lavender",               new Color(0x00e6e6fa));
644         m.put("lavenderblush",          new Color(0x00fff0f5));
645         m.put("lawngreen",              new Color(0x007cfc00));
646         m.put("lemonchiffon",           new Color(0x00fffacd));
647         m.put("lightblue",              new Color(0x00add8e6));
648         m.put("lightcoral",             new Color(0x00f08080));
649         m.put("lightcyan",              new Color(0x00e0ffff));
650         m.put("lightgoldenrodyellow",   new Color(0x00fafad2));
651         m.put("lightgray",              new Color(0x00d3d3d3));
652         m.put("lightgreen",             new Color(0x0090ee90));
653         m.put("lightgrey",              new Color(0x00d3d3d3));
654         m.put("",                       new Color(0000000000));
655         m.put("lightpink",              new Color(0x00ffb6c1));
656         m.put("lightsalmon",            new Color(0x00ffa07a));
657         m.put("lightseagreen",          new Color(0x0020b2aa));
658         m.put("lightskyblue",           new Color(0x0087cefa));
659         m.put("lightslategray",         new Color(0x00778899));
660         m.put("lightslategrey",         new Color(0x00778899));
661         m.put("lightsteelblue",         new Color(0x00b0c4de));
662         m.put("lightyellow",            new Color(0x00ffffe0));
663         m.put("lime",                   new Color(0x0000ff00));
664         m.put("limegreen",              new Color(0x0032cd32));
665         m.put("linen",                  new Color(0x00faf0e6));
666         m.put("magenta",                new Color(0x00ff00ff));
667         m.put("maroon",                 new Color(0x00800000));
668         m.put("mediumaquamarine",       new Color(0x0066cdaa));
669         m.put("mediumblue",             new Color(0x000000cd));
670         m.put("mediumorchid",           new Color(0x00ba55d3));
671         m.put("mediumpurple",           new Color(0x009370db));
672         m.put("mediumseagreen",         new Color(0x003cb371));
673         m.put("mediumslateblue",        new Color(0x007b68ee));
674         m.put("mediumspringgreen",      new Color(0x0000fa9a));
675         m.put("mediumturquoise",        new Color(0x0048d1cc));
676         m.put("mediumvioletred",        new Color(0x00c71585));
677         m.put("midnightblue",           new Color(0x00191970));
678         m.put("mintcream",              new Color(0x00f5fffa));
679         m.put("mistyrose",              new Color(0x00ffe4e1));
680         m.put("moccasin",               new Color(0x00ffe4b5));
681         m.put("navajowhite",            new Color(0x00ffdead));
682         m.put("navy",                   new Color(0x00000080));
683         m.put("oldlace",                new Color(0x00fdf5e6));
684         m.put("olive",                  new Color(0x00808000));
685         m.put("olivedrab",              new Color(0x006b8e23));
686         m.put("orange",                 new Color(0x00ffa500));
687         m.put("orangered",              new Color(0x00ff4500));
688         m.put("orchid",                 new Color(0x00da70d6));
689         m.put("palegoldenrod",          new Color(0x00eee8aa));
690         m.put("palegreen",              new Color(0x0098fb98));
691         m.put("paleturquoise",          new Color(0x00afeeee));
692         m.put("palevioletred",          new Color(0x00db7093));
693         m.put("papayawhip",             new Color(0x00ffefd5));
694         m.put("peachpuff",              new Color(0x00ffdab9));
695         m.put("peru",                   new Color(0x00cd853f));
696         m.put("pink",                   new Color(0x00ffc0cb));
697         m.put("plum",                   new Color(0x00dda0dd));
698         m.put("powderblue",             new Color(0x00b0e0e6));
699         m.put("purple",                 new Color(0x00800080));
700         m.put("red",                    new Color(0x00ff0000));
701         m.put("rosybrown",              new Color(0x00bc8f8f));
702         m.put("royalblue",              new Color(0x004169e1));
703         m.put("saddlebrown",            new Color(0x008b4513));
704         m.put("salmon",                 new Color(0x00fa8072));
705         m.put("sandybrown",             new Color(0x00f4a460));
706         m.put("seagreen",               new Color(0x002e8b57));
707         m.put("seashell",               new Color(0x00fff5ee));
708         m.put("sienna",                 new Color(0x00a0522d));
709         m.put("silver",                 new Color(0x00c0c0c0));
710         m.put("skyblue",                new Color(0x0087ceeb));
711         m.put("slateblue",              new Color(0x006a5acd));
712         m.put("slategray",              new Color(0x00708090));
713         m.put("slategrey",              new Color(0x00708090));
714         m.put("snow",                   new Color(0x00fffafa));
715         m.put("springgreen",            new Color(0x0000ff7f));
716         m.put("steelblue",              new Color(0x004682b4));
717         m.put("tan",                    new Color(0x00d2b48c));
718         m.put("teal",                   new Color(0x00008080));
719         m.put("thistle",                new Color(0x00d8bfd8));
720         m.put("tomato",                 new Color(0x00ff6347));
721         m.put("turquoise",              new Color(0x0040e0d0));
722         m.put("violet",                 new Color(0x00ee82ee));
723         m.put("wheat",                  new Color(0x00f5deb3));
724         m.put("white",                  new Color(0x00ffffff));
725         m.put("whitesmoke",             new Color(0x00f5f5f5));
726         m.put("yellow",                 new Color(0x00ffff00));
727         m.put("yellowgreen",            new Color(0x009acd32));
728         colorMap = Collections.unmodifiableMap(m);
729     }
730
731     private static int instances = 0;
732
733     private final String title; // window's title
734     private JFrame frame;       // the frame (window)
735     private DWPanel panel;      // the panel showing the image
736     private BufferedImage image; // the image we draw into
737     private Graphics2D graphics; // graphics associated with image
738     private Color bgColor;       // background color, for clearGraph()
739     private boolean isClosed;    // is the window closed ?
740     private Object mouseLock;       // lock for mouse events
741     private MouseEvent mouseEvent;  // last mouse event
742     private Point mousePos;         // last mouse position
743     private int mouseButton;        // last mouse click
744
745     // To be run on the Event Dispatching Thread
746     void createGUI() {
747         panel = new DWPanel();
748         frame = new JFrame(title);
749         frame.add(panel);
750         frame.pack();
751         frame.setResizable(false);
752         frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
753         frame.addWindowListener(new DWWindowHandler());
754         frame.addKeyListener(new DWKeyHandler());
755         panel.addMouseListener(new DWMouseHandler());
756         frame.setLocationByPlatform(true);
757         frame.setVisible(true);
758     }
759
760     private class DWWindowHandler extends WindowAdapter {
761         public void windowOpened(WindowEvent ev) {
762             DrawingWindow w = DrawingWindow.this;
763             DrawingWindow.instances++;
764             w.isClosed = false;
765         }
766
767         public void windowClosed(WindowEvent ev) {
768             DrawingWindow w = DrawingWindow.this;
769             if (!w.isClosed) {
770                 w.isClosed = true;
771                 if (DrawingWindow.instances <= 0)
772                     throw new AssertionError("Bad instance counter: " +
773                                              DrawingWindow.instances);
774                 DrawingWindow.instances--;
775                 if (DrawingWindow.instances == 0)
776                     System.exit(0);
777             }
778         }
779     }
780
781     private class DWKeyHandler extends KeyAdapter {
782         public void keyPressed(KeyEvent ev) {
783             DrawingWindow w = DrawingWindow.this;
784             if (ev.getKeyCode() == KeyEvent.VK_ESCAPE) {
785                 w.closeGraph();
786             }
787         }
788     }
789
790     private class DWMouseHandler extends MouseAdapter {
791         public void mousePressed(MouseEvent ev) {
792             DrawingWindow w = DrawingWindow.this;
793             synchronized (w.mouseLock) {
794                 w.mouseEvent = ev;
795                 mouseLock.notifyAll();
796             }
797         }
798     }
799
800     private class DWPanel extends JPanel {
801         DWPanel() {
802             DrawingWindow w = DrawingWindow.this;
803             Dimension dimension = new Dimension(w.width, w.height);
804             super.setMinimumSize(dimension);
805             super.setMaximumSize(dimension);
806             super.setPreferredSize(dimension);
807         }
808
809         public void paint(Graphics g) {
810             DrawingWindow w = DrawingWindow.this;
811             synchronized (w.image) {
812                 g.drawImage(w.image, 0, 0, null);
813             }
814         }
815
816         private static final long serialVersionUID = 0;
817     }
818 }