Logo AND Algorithmique Numérique Distribuée

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