Logo AND Algorithmique Numérique Distribuée

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