001    /* 
002     * Copyright (c) Holger Pfaff - http://pfaff.ws
003     *
004     * This software maybe used for any purpose provided the
005     * above copyright notice is retained. It is supplied as is.
006     * No warranty expressed or implied - Use at your own risk.
007     */
008    
009    import java.awt.*;
010    import java.awt.event.*;
011    import java.util.*;
012    import java.io.*;
013    import java.net.*;
014    import java.applet.*;
015    
016    /**
017      * (#)Awt.java
018      * @author   Holger Pfaff
019      * @version  3.2 19-Mar-2004<br><br> 
020      *
021      * Implements some useful (static) AWT routines and acts as baseclass
022      * for several gui items
023      * <br>
024      * Overview of mode bits used in this class:
025      * <pre>
026      *    misc     effect   position
027      *  0 0 0 0   0 0 0 0   0 0 0 0
028      *  - - H F   U B S R   W S E N
029      *      r i   l u u a   e o a o
030      *      e l   i l n i   s u s r
031      *      f l   n k k s   t t t t
032      *            e   e e     h   h
033      *                n d
034      * </pre>
035      * This class also implements a simple text formating mechanism. Unicode
036      * escape sequences can be used to change fonts alignment, color etc. The
037      * syntax is pretty easy. A formatting directive is always constructed
038      * from <unicode esc> + <unicode format info>. The prefix for setting the
039      * font to serif and the color to white woud be Esc + Serif + Esc + White.<br>
040      * A summary of all attributes:<br>
041      *  <pre>
042      *  Font Family       FFnn
043      *  Serif             FF00
044      *  SansSerif         FF01
045      *  Monospaced        FF02
046      *  <br>
047      *  Font style        FBnn
048      *  Plain             FB00
049      *  Bold              FB01
050      *  Italic            FB02
051      *  Bolditalic        FB03
052      *  
053      *  Font Size rel.    F0nn
054      *  Smaller           F000     two pixel smaller
055      *  Larger            F001     two pixel larger
056      *  
057      *  Font Size Absolut F5nn     nn = size in pixel (hex)
058      *  
059      *  Text Alignment    A00n
060      *  North             A001
061      *  East              A002
062      *  South             A004
063      *  West              A008
064      *  NorthWest         A009
065      *  SouthWest         A00C
066      *  NorthEast         A003
067      *  SouthEast         A00A
068      *  Center            A00F
069      *  
070      *  Text Tabs         Dnnn     Left tabs only nnn = no of char to tab
071      *  Tab0              D000
072      *   
073      *  Effects           Ennn
074      *  Flat              E000     default effect
075      *  Raised            E001     makes objects looking raised
076      *  Sunken            E002     makes objects looking sunken
077      *  Bulk              E003     actually a drawing mode @see setBorderType()
078      *  Uline             E004     underline
079      *  Href              E0D1     start of a href
080      *  Hrefend           E0D0     end of a href
081      *  Darker            E0C1     make foreground color darker
082      *  Brighter          E0C2     make foreground color brighter
083      *  Background        E0C3     make foreground equal to current backgroundcolor
084      * 
085      *  Colors            Cnnn     nnn = rgb - eight bits per color
086      *  White             C000
087      *  Black             CFFF
088      *  </pre>
089      *
090      */
091       
092    public class Awt extends Panel implements MouseListener, MouseMotionListener, KeyListener, AdjustmentListener, FocusListener, ItemSelectable {
093      
094      /**   
095       * One special mode
096       */
097      public final static int NONE          = 0;
098      
099      /**   
100       *  positions / directions mask - the lowest 4 bits 0-3
101       */
102      public final static int POSITION_MASK = 0xF;
103    
104      /**   
105       *  North position
106       */
107      public final static int N             = 0x1 << 0;
108    
109      /**   
110       *  East position
111       */
112      public final static int E             = 0x1 << 1;
113    
114      /**   
115       *  South position
116       */
117      public final static int S             = 0x1 << 2;
118    
119      /**   
120       *  West position
121       */
122      public final static int W             = 0x1 << 3;
123      
124      /**   
125       *  NorthWest position
126       */
127      public final static int NW            = N|W;
128    
129      /**   
130       *  SouthWest position
131       */
132      public final static int SW            = S|W;
133    
134      /**   
135       *  NorthEast position
136       */
137      public final static int NE            = N|E;
138    
139      /**   
140       *  SouthEast position
141       */
142      public final static int SE            = S|E;
143    
144      /**   
145       *  Center position
146       */
147      public final static int C             = N|E|S|W;
148    
149      /**   
150       *  Center position
151       */
152      public final static int CENTER        = C;
153    
154      /**   
155       *  Top position
156       */
157      public final static int TOP           = N;
158    
159      /**   
160       *  Right position
161       */
162      public final static int RIGHT         = E;
163    
164      /**   
165       *  Bottom position
166       */
167      public final static int BOTTOM        = S;
168    
169      /**   
170       *  Left position
171       */
172      public final static int LEFT          = W;
173      
174      /**
175       * effect mask - bits 4-7
176       */
177      public final static int EFFECT_MASK   = 0xF << 4;
178    
179      /**
180       * a raised border
181       */
182      public final static int RAISED        = 0x1 << 4;
183    
184      /**
185       * a sunken border
186       */
187      public final static int SUNKEN        = 0x1 << 5;
188    
189      /**
190       * a highlighted border/text
191       */
192      public final static int BULK          = 0x1 << 6;
193      
194      /**
195       * underline effect
196       */
197      public final static int ULINE         = 0x1 << 7;
198        
199      /**
200       * mode mask - bits 8-11
201       */
202      public final static int MISC_MASK     = 0xF << 8;
203    
204      /**
205       * fill object (rectangle/triangle) - Outline is default
206       */
207      public final static int FILL          = 0x1 << 8;
208      
209      /**
210       * the following text is a href
211       */
212      public final static int HREF          = 0x1 << 9;
213      
214      /**
215       * Unicode replacement char. Used as indicator that the
216       * following char is interpreted as formatting information
217       */
218      public final static char Esc          = '\uFFFD';
219      
220      /**
221       * Unicode null char. Not a char at all. Used as placeholder
222       */
223      public final static char Null         = '\uFFFF';
224      
225      /**
226       * reset all styles to default 
227       */
228      public final static char None         = '\u0000';
229    
230      /**
231       * font family Serif
232       */
233      public final static char Serif        = '\uFF00';
234    
235      /**
236       * font family SansSerif
237       */
238      public final static char SansSerif    = '\uFF01';
239    
240      /**
241       * font family Monospaced
242       */
243      public final static char Monospaced   = '\uFF02';
244    
245      /**
246       * font style plain
247       */
248      public final static char Plain        = '\uFB00';
249    
250      /**
251       * font style bold
252       */
253      public final static char Bold         = '\uFB01';
254    
255      /**
256       * font style italic
257       */
258      public final static char Italic       = '\uFB02';
259    
260      /**
261       * font style bolditalic
262       */
263      public final static char Bolditalic   = '\uFB03';
264    
265      /**
266       * font size relative smaller
267       */
268      public final static char Smaller      = '\uF000';
269    
270      /**
271       * font size relative larger
272       */
273      public final static char Larger       = '\uF001';
274      
275      /**
276       * font sizes absolute
277       */
278      public final static char FontSize0    = '\uF500';
279    
280      /**
281       * Mask for computing fontsizes
282       */
283      public final static char FontMask     = '\uFF00';
284    
285      /**
286       * Mask for computing fontsizes
287       */
288      public final static char FontSizeMask = '\u00FF';
289      
290      /**
291       * North alignment 
292       */
293      public final static char North        = '\uA001';
294      
295      /**
296       * East alignment 
297       */
298      public final static char East         = '\uA002';
299      
300      /**
301       * South alignment 
302       */
303      public final static char South        = '\uA004';
304      
305      /**
306       * West alignment 
307       */
308      public final static char West         = '\uA008';
309      
310      /**
311       * NorthWest alignment 
312       */
313      public final static char NorthWest    = '\uA009';
314      
315      /**
316       * SouthWest alignment 
317       */
318      public final static char SouthWest    = '\uA00C';
319      
320      /**
321       * NorthEast alignment 
322       */
323      public final static char NorthEast    = '\uA003';
324      
325      /**
326       * SouthEast alignment 
327       */
328      public final static char SouthEast    = '\uA00A';
329    
330      /**
331       * Center alignment 
332       */
333      public final static char Center       = '\uA00F';
334      
335      /**
336       * Tab positions absolute; Format: Dnnn  nnn is the pos in hex ranging from 0 - 7FF
337       */
338      public final static char Tab0         = '\uD000';
339      
340      /**
341       * Tab mask to determine tabs
342       */
343      public final static char TabMask      = '\uF000';
344      
345      /**
346       * Tab mask to determine tab positions
347       */
348      public final static char TabSizeMask  = '\u07FF';
349      
350      /**
351       * the default 'effect'
352       */
353      public final static char Flat         = '\uE000';
354      
355      /**
356       * shadow effect to emphasize text
357       */
358      public final static char Raised       = '\uE001';
359      
360      /**
361       * shadow effect to make text look inaktive
362       */
363      public final static char Sunken       = '\uE002';
364      
365      /**
366       * Used to specify a border mode in property files. not used for text
367       */
368      public final static char Bulk         = '\uE003';
369      
370      /**
371       * Underline text
372       */
373      public final static char Uline        = '\uE004';
374      
375      /**
376       * start of a href
377       */
378      public final static char Href         = '\uE0D1';
379      
380      /**
381       * end of a href (or use flat/default)
382       */
383      public final static char Hrefend      = '\uE0D0';
384      
385      /**
386       * make fg color darker
387       */
388      public final static char Darker       = '\uE0C1';
389      
390      /**
391       * make fg color brighter
392       */
393      public final static char Brighter     = '\uE0C2';
394    
395      /**
396       * make fg color equal to fg color (same as setting it to default)
397       */
398      public final static char Foreground   = '\uE0C3';
399    
400      /**
401       * make fg color equal to bg color
402       */
403      public final static char Background   = '\uE0C4';
404    
405      /**
406       * colors use the format C<r><g><b>, where each color value can range from 0 to F. See examples below
407       */
408      public final static char ColorMask    = '\uF000';
409      
410      /**
411       * used to determine the red portion of this color spec
412       */
413      public final static char RedMask      = '\u0F00';
414      
415      /**
416       * used to determine the green portion of this color spec
417       */
418      public final static char GreenMask    = '\u00F0';
419      
420      /**
421       * used to determine the blue portion of this color spec
422       */
423      public final static char BlueMask     = '\u000F';
424    
425      /**
426       * the color white
427       */
428      public final static char White        = '\uC000';
429      
430      /**
431       * the color black
432       */
433      public final static char Black        = '\uCFFF';
434            
435      /**
436       * java version
437       */
438            protected final static String jversion  = System.getProperty("java.version");
439    
440      /**
441       * java vendor
442       */
443            protected final static String jvendor   = System.getProperty("java.vendor");
444            
445      /**
446       *  font cache 
447       */
448      protected final static Hashtable fonts  = new Hashtable();
449      
450      /**
451       *  insets around this comp
452       */
453      protected Insets    insets              = new Insets(0,0,0,0); // borders around the component
454      
455      /**
456       *  insets within this comp
457       */
458      protected Insets    innerInsets         = new Insets(0,0,0,0); // borders within the component
459     
460      /**
461       *  borderdepth - thickness of border
462       */
463      protected int       borderDepth         = 0;
464    
465      /**
466       *  bordertype - one of NONE, RAISED, SUNKEN, BULK, TOP
467       */
468      protected int       borderType          = NONE;
469    
470      /**
471       *  the dimension for comp
472       */
473      protected Dimension minimumDimension    = new Dimension();
474     
475      /**
476       *  ActionListener to notify clients
477       */
478      protected ActionListener actionListener = null;
479      
480      /**
481       *  ItemListener to notify clients
482       */
483      protected ItemListener   itemListener   = null;
484      
485      /**
486       *  save components states if this panel is set to disable
487       */
488      protected Hashtable     componentStates = new Hashtable();
489      
490      /**
491       *  our Background
492       */
493            protected Object bg = null;
494      
495      /**
496       *  backing image for flickerfree drawing
497       */
498      protected Image bim = null;
499      
500      /**
501       *  Grphics for bim
502       */
503      protected Graphics big = null;
504    
505      /**
506       * Creates a nicely formatted error popup.
507       *
508       * @param   m   the message; usually string or exception
509       * @param   c   comp used as parent for popup or null
510       */
511      public static void error(Object m, Component c) {
512        String s[] = Debug.stackline(1);
513        String e   = "" + Esc + Bold + Debug.msg(m);
514        if(s.length > 2) {
515          e += "\n" + Esc + Plain + "[" + s[1] + " at " + s[2] + "]";
516        }
517        if(m instanceof Exception) {
518          e += "\n\n" + Esc + Italic + Debug.stack((Exception) m).replace('\t',' ');
519        }
520        DialogBox.errorDialog(e).show(c);
521      }
522    
523      /**
524       * Creates an image from an InputStream
525       *
526       * @param   n   the name of the image.
527       */
528      public static Image loadImageFromStream(InputStream i) {
529        try {
530          ByteArrayOutputStream b = new ByteArrayOutputStream();
531          for(int c = i.read(); c >= 0; c = i.read()) {
532            b.write(c);
533          }
534          return Toolkit.getDefaultToolkit().createImage(b.toByteArray());
535        } catch(Exception x) {
536          Debug.debug(Awt.class, x);
537          return null;
538                    }
539      }
540      
541      /**
542       * Creates an image from a Archive
543       * At least this is what Netscape 4.x does.
544       * @link <a href="http://developer.netscape.com/docs/technote/java/getresource/getresource.html">Technote getresource</a>
545       * Returns <code>null</code> on failure.
546       *
547       * @param   n   the name of the image.
548       */
549      public static Image loadImageFromArchive(String n) {
550        return loadImageFromStream(Awt.class.getResourceAsStream(n));
551      }
552      
553      /**
554       * Creates an image from an url.
555       * Returns <code>null</code> on failure.
556       *
557       * @param   n   the name/url of the image.
558       */
559      public static Image loadImageFromURL(String n) {
560        try {
561          return loadImageFromStream(new URL(n).openStream());
562        } catch(Exception x) {
563          Debug.debug(Awt.class, x);
564          return null;
565                    }
566      }
567      
568      /**
569       * Creates an image either form URL or Archive
570       * Returns <code>null</code> on failure.
571       *
572       * @param   n   the name/url of the image.
573       */
574      public Image loadImage(String n) {
575        Image i = loadImageFromURL(n);
576        if(i == null) {
577          i = loadImageFromArchive(n);
578        }
579        return i;
580      }
581            
582      /**
583       * Check if this is a Netscape 4 JVM
584       *
585       * @return   true if this is a Netscape 4 JVM
586       */
587            public static boolean isNS4JVM() {
588              return jversion.equals("1.1.5") && jvendor.startsWith("Net");
589            }
590            
591      /**
592       * Check if this is a MS JVM
593       *
594       * @return   true if this is a MS JVM
595       */
596      public static boolean isMSJVM() {
597              return jversion.equals("1.1.4") && jvendor.startsWith("Mic");
598            }
599    
600      /**
601       * Center a window on screen. Reduces window size if larger
602       * than screen.
603       *
604       * @param   w   the window to center.
605       */
606      public static void centerWindow(Window w) {
607        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
608        // not larger than screen ...
609        w.setSize(Math.min(d.width, w.getSize().width), Math.min(d.height, w.getSize().height));
610        // Center Frame, Dialog or Window on screen
611        w.setLocation((d.width - w.getSize().width) / 2, (d.height - w.getSize().height) / 2);
612      }
613      
614      /**
615       * Set BG color of a (AWT) component
616       *
617       * @param   c Component
618       */
619      public static void setBackground(Component c) {
620        if(c.getParent().getBackground() != null) {
621          c.setBackground(c instanceof TextComponent && (isNS4JVM() || isMSJVM()) ? c.getParent().getBackground().darker() : c.getParent().getBackground());
622        }
623      }
624      
625      /**
626       * Set FG color of a (AWT) component
627       *
628       * @param   c Component
629       */
630      public static void setForeground(Component c) {
631        if(c.getParent().getForeground() != null) {
632          c.setForeground(c.getParent().getForeground());
633        }
634      }
635      
636      /**
637       * Set font of a (AWT) textcomponent
638       *
639       * @param   tc TextComponent
640       */
641      public static void setFont(TextComponent tc) {
642        if(tc.getParent().getFont() != null) {
643          tc.setFont(tc.getParent().getFont());
644        }
645      }
646    
647      /**
648       * Refresh (force new layout) starting from component up the tree
649       *
650       * @param   c   the component to refresh.
651       */
652            public static void refresh(Component c) {
653                    c.invalidate();
654                    c.validate();
655                    if(isMSJVM()) c.repaint();
656        if(c instanceof Container) {
657          Component[] cs = ((Container) c).getComponents();
658          for(int i = 0; i < cs.length; ++i) {
659            if(cs[i] instanceof TextComponent) {
660              setFont((TextComponent) cs[i]);
661            }
662            if(cs[i] instanceof TextComponent || cs[i] instanceof Scrollbar) {
663              setForeground(cs[i]);
664              setBackground(cs[i]);
665            }
666            refresh(cs[i]);
667          }
668        }
669                    if(c instanceof Window) {
670          ((Window) c).pack(); return;
671                    }
672      }
673        
674      /**
675       * Refresh (force new layout) starting from this component down the tree
676       */
677            public void refresh() {
678        refresh(this);
679            }
680      
681      /**
682       * Refresh (force new layout) starting from root component up the tree
683       */
684            public void refreshAll() {
685        refresh(getTopComponent(this));
686        refresh(getTopComponent(this));
687            }
688      
689      /**
690       * Gets the toplevel component (usually a frame)
691       *
692       * @param   c   the component to find the toplevel component for.
693       */
694      public static Component getTopComponent(Component c) {
695        return (Component) c.getParent() == null || (c instanceof Main && ((Main)c).isApplet()) ? c : getTopComponent(c.getParent());
696      }
697    
698      /**
699       * Gets the current applet context if any
700       *
701       * @param   c   the component to find the applet context for.
702       */
703      public static AppletContext getAppletContext(Component c) {
704        return getApplet(getTopComponent(c)).getAppletContext();
705      }
706      
707      /**
708       * Gets the current applet if any. Used internaly
709       *
710       * @param   c   the toplevel component to start with
711       */
712      private static Applet getApplet(Component c) {
713        if(c instanceof Applet) {
714          return (Applet) c;
715        }
716        if(c instanceof Container) {
717          Container C = (Container) c;
718          for(int i = 0; i < C.getComponentCount(); ++i) {
719            Applet a = getApplet(C.getComponent(i));
720            if(a != null) {
721              return a;
722            }
723          }
724        }
725        return null;
726      }
727    
728      /**
729       * Try to get a font. <code>obj</code> can be a 
730       * standard font name or already a font object.
731       *
732       * @return <code>null</code> on failure.
733       *
734       * @param   obj   color specification.
735       */
736      public static Font getFont(Object o) {
737        if(o == null) {
738          return null;
739        }
740        if(o instanceof Font) {
741          return (Font) o;
742        }
743        return Font.decode(o.toString());
744      }
745      
746      /**
747       * Try to get a font from our cache. Create a new one if none was found.
748       *
749       * @param   name   Font name/family
750       * @param   style  Font.PAIN Font.BOLD Font.ITALIC
751       * @param   size   Font size in points
752       */
753      public static Font getFont(String name, int style, int size) {
754        String idx = name + style + size;
755        if(fonts.containsKey(idx)) {
756          return (Font) fonts.get(idx);
757        } else {
758          Font font = new Font(name, style, size);
759          fonts.put(idx, font); getMetrics(font);
760          return font;
761        }
762      }
763      
764      /**
765       * Returns Metrics for font
766       * @param   f   font to use.
767       */
768      public static FontMetrics getMetrics(Font f) {
769        if(f != null) {
770          if(fonts.containsKey(f) == false) {
771            fonts.put(f, Toolkit.getDefaultToolkit().getFontMetrics(f));
772          }
773          return (FontMetrics) fonts.get(f);
774        }
775        return null;
776      }
777      
778      /**
779       * Returns a larger font with same family and style
780       * @param   f   use this as base font.
781       */
782      public static Font larger(Font f) {
783        return getFont(f.getName(), f.getStyle(), f.getSize() + 2);
784      }
785      
786      /**
787       * Returns a smaller font with same family and style
788       * @param   f   use this as base font.
789       */
790      public static Font smaller(Font f) {
791        return getFont(f.getName(), f.getStyle(), f.getSize() > 1 ? f.getSize() - 2 : f.getSize());
792      }
793      
794      /**
795       * Returns a serif font with same size and style
796       * @param   f   use this as base font.
797       */
798      public static Font serif(Font f) {
799        return getFont("Serif", f.getStyle(), f.getSize());
800      }
801      
802      /**
803       * Returns a sansserif font with same size and style
804       * @param   f   use this as base font.
805       */
806      public static Font sansserif(Font f) {
807        return getFont("SansSerif", f.getStyle(), f.getSize());
808      }
809      
810      /**
811       * Returns a monospaced font with same size and style
812       * @param   f   use this as base font.
813       */
814      public static Font monospaced(Font f) {
815        return getFont("Monospaced", f.getStyle(), f.getSize());
816      }
817      
818      /**
819       * Returns a bold font with same size and family
820       * @param   f   use this as base font.
821       */
822      public static Font bold(Font f) {
823        return getFont(f.getName(), Font.BOLD, f.getSize());
824      }
825      
826      /**
827       * Returns a italic font with same size and family
828       * @param   f   use this as base font.
829       */
830      public static Font italic(Font f) {
831        return getFont(f.getName(), Font.ITALIC, f.getSize());
832      }
833      
834      /**
835       * Returns a bolditalic font with same size and family
836       * @param   f   use this as base font.
837       */
838      public static Font bolditalic(Font f) {
839        return getFont(f.getName(), Font.BOLD | Font.ITALIC, f.getSize());
840      }
841      
842      /**
843       * Returns a plain font with same size and family
844       * @param   f   use this as base font.
845       */
846      public static Font plain(Font f) {
847        return getFont(f.getName(), Font.PLAIN, f.getSize());
848      }
849      
850      /**
851       * Returns the escape sequence for Color c
852       * @param   c   the color
853       */
854      public static String Color2Esc(Color c) {
855        if(c == null) {
856          return "";
857        }
858        int v = White + ((c.getRed() / 16) << 8) + ((c.getGreen() / 16) << 4) + ((c.getBlue() / 16) << 0);
859        char[] buf = {Esc, (char) v};
860        return String.valueOf(buf);
861      }
862      
863      /**
864       * Try to get color. <code>obj</code> can be color name 
865       * or a numeric value or already a color object.
866       *
867       * @return <code>null</code> on failure.
868       *
869       * @param   o   color specification.
870       */
871      public static Color getColor(Object o) {
872        if(o == null) {
873          return null;
874        }
875        if(o instanceof Color) {
876          return (Color) o;
877        }
878        try {
879          return Color.decode(o.toString());
880        } catch(Exception x) {
881          return null;
882        }
883      }
884      
885      /**
886       * Return a dimmed version of color <code>c</code>
887       *
888       * @param   c   original color
889       * @param   v   value to dim
890       */
891      public static Color dim(Color c, int v) {
892        return c == null ? Color.white :
893               new Color(Math.max(Math.min(c.getRed()   + v, 255),0),
894                         Math.max(Math.min(c.getGreen() + v, 255),0),
895                         Math.max(Math.min(c.getBlue()  + v, 255),0));
896      }
897      
898      /**
899       * Return a brighter version of color <code>c</code>
900       *
901       * @param   c   original color
902       */
903      public static Color brighter(Color c) {
904        return dim(c, 63);
905      }
906      
907      /**
908       * Return a darker version of color <code>c</code>
909       *
910       * @param   c   original color
911       */
912      public static Color darker(Color c) {
913        return dim(c, -63);
914      }
915      
916      /**
917       * Return Insets from an int array (used to set Insets from property file)
918       *
919       * @param   i   insets values (as in Insets constructor)
920       */
921      public static Insets Array2Insets(int[] i) {
922        return i != null && i.length == 4 ? new Insets(i[0], i[1], i[2], i[3]) : null;
923      }
924    
925      /**
926       * Draws a line between the given points
927       *
928       * @param   g      Graphics object to use
929       * @param   c      color to use as base for shadow, or null
930       * @param   x,y    x,y coords of first point
931       * @param   X,Y    x,y coords of second point
932       * @param   d      thickness of line in pixel
933       * @param   m      RAISED or SUNKEN
934       */
935      public static void drawLine(Graphics g, Color c, 
936                                  int x, int y, int X, int Y,
937                                  int d, int m) {
938        Color C = g.getColor();
939        g.setColor(c == null ? C : c);
940        
941        if(isBulk(m)) {
942          d = d/2;
943          drawLine(g, isSunken(m) ? brighter(c) : darker(c), x, y, X, Y, d, m & ~EFFECT_MASK);
944          drawLine(g, isSunken(m) ? darker(c) : brighter(c), x + d, y + d, X + d, Y + d, d, m & ~EFFECT_MASK);
945          return;
946        }
947        
948        g.drawLine(x, y, X, Y);
949        if(--d > 0) {
950          drawLine(g, c, x + 1, y + 1, X + 1, Y + 1, d, 0);
951        }
952        g.setColor(C);
953      }
954      
955      /**
956       * Draws a rectangle within the given box
957       *
958       * @param   g      Graphics object to use
959       * @param   c      color to use as base for shadow, or null
960       * @param   x      x-position of describing rect
961       * @param   y      y-position of describing rect
962       * @param   w      width of describing rect
963       * @param   h      height of describing rect
964       * @param   d      thickness of the line in pixel
965       * @param   m      combination of FILL RAISED SUNKEN
966       */
967      public static void drawRectangle(Graphics g, Color c, 
968                                       int x, int y, int w, int h,
969                                       int d, int m) {
970        if(w < 1 || h < 1 || d < 1) {
971          return;
972        }
973        
974                    g = g.create(x, y, w + 1, h + 1);
975        
976        if(c != null) {
977          g.setColor(c);
978        }
979        
980        if(isFill(m)) {
981          g.fillRect(0, 0, w, h);
982        }
983        
984        if(isBulk(m)) {
985          d = d/2;
986          drawRectangle(g, isSunken(m) ? brighter(c) : darker(c), 0, 0, w, h, d, m & ~EFFECT_MASK);
987          drawRectangle(g, isSunken(m) ? darker(c) : brighter(c), d, d, w - d - d, h - d - d, d, m & ~EFFECT_MASK);
988          g.dispose();
989          return;
990        }
991     
992        if(isEffect(m)) {
993          g.setColor(isRaised(m) ? brighter(c) : darker(c));
994        }
995        g.drawLine(0, 0, w, 0);
996        g.drawLine(0, 0, 0, h);
997        
998        if(isEffect(m)) {
999          g.setColor(isRaised(m) ? darker(c) : brighter(c));
1000        }
1001        g.drawLine(w, h, w, 0);
1002        g.drawLine(w, h, 0, h);
1003        
1004        if(--d > 0) {
1005          drawRectangle(g, c, 1, 1, w - 2, h - 2, d, m & ~FILL);
1006        }
1007        g.dispose();
1008      }
1009    
1010      /**
1011       * Draws a triangle within the given box
1012       *
1013       * @param   g      Graphics object to use
1014       * @param   c      color to use as base for shadow, or null
1015       * @param   x      x-position of describing rect
1016       * @param   y      y-position of describing rect
1017       * @param   w      width of describing rect
1018       * @param   h      height of describing rect
1019       * @param   d      thickness of the line in pixel
1020       * @param   m      combination of LINE FILL RAISED SUNKEN and Direction (N E S W etc)
1021       */
1022      public static void drawTriangle(Graphics g, Color c,
1023                                      int x, int y, int w, int h,
1024                                      int d, int m) {
1025        if(w < 1 || h < 1 || d < 1) {
1026          return;
1027        }
1028    
1029        int     X[] = new int[3];
1030        int     Y[] = new int[3];
1031        boolean drk = ((m & W) > 0 || getPosition(m) == N) == isSunken(m);
1032        
1033                    g = g.create(x, y, w + 1, h + 1);
1034    
1035        if(c != null) {
1036          g.setColor(c);
1037        }
1038        
1039        w--; h--;
1040        
1041        switch(getPosition(m)) {
1042          case N:
1043            X[0]=w/2; X[1]=w; Y[1]=h;   Y[2]=h; break;
1044          case W: 
1045            X[1]=w;   X[2]=w; Y[0]=h/2; Y[1]=h; break;
1046          case SW: 
1047            X[1]=w; Y[1]=h; break;
1048          case NW:
1049            X[1]=w; Y[2]=h; break;
1050          case S:
1051            X[0]=w/2; X[2]=w; Y[0]=h; break;
1052          case E:
1053            X[0]=w; Y[0]=h/2; Y[2]=h; break;
1054          case SE:
1055            X[0]=w; X[2]=w; Y[0]=h; Y[1]=h; break;
1056          case NE:
1057            X[0]=w; X[2]=w; Y[2]=h; break;
1058          default:
1059            g.dispose();
1060            return;
1061        }
1062        
1063        if(isEffect(m)) {
1064          g.setColor(c);
1065        }
1066        
1067        if(isFill(m)) {
1068          g.fillPolygon(X, Y, 3);
1069        }
1070        
1071        if(isEffect(m)) {
1072          g.setColor(drk ? brighter(c) : darker(c));
1073        }
1074        g.drawPolyline(X, Y, 3);
1075        
1076        if(isEffect(m)) {
1077          g.setColor(drk ? darker(c) : brighter(c));
1078        }
1079        g.drawLine(X[0], Y[0], X[2], Y[2]);
1080        
1081        if(isFill(m)) {
1082          g.dispose();
1083          return;
1084        }
1085        
1086        if(--d > 0) {
1087          drawTriangle(g, c, 1, 1, w - 1, h - 1, d, m & ~FILL);
1088        }
1089        g.dispose();
1090      }
1091      
1092      /**
1093       * 
1094       * Draws a string within the given box. 
1095       *
1096       * @param   g       Graphics object to use
1097       * @param   c       color to use or null
1098       * @param   f       font to use or null
1099       * @param   s       the string
1100       * @param   x       x-position of describing rect
1101       * @param   y       y-position of describing rect
1102       * @param   w       width of describing rect
1103       * @param   h       height of describing rect
1104       * @param   m       mode including position & effect & misc
1105       */
1106      public static Dimension drawString(Graphics g, Color c, Font f, String s, 
1107                                         int x, int y, int w, int h, int m) {
1108        return drawString(g, c, f, s, x, y, w, h, m, null);
1109      }
1110                                         
1111      /**
1112       * 
1113       * Draws a string within the given box. 
1114       *
1115       * @param   g       Graphics object to use
1116       * @param   c       color to use or null
1117       * @param   f       font to use or null
1118       * @param   s       the string
1119       * @param   x       x-position of describing rect
1120       * @param   y       y-position of describing rect
1121       * @param   w       width of describing rect
1122       * @param   h       height of describing rect
1123       * @param   m       mode including position & effect & misc
1124       * @param   awt     comp used as callback for hrefs
1125       */
1126      public static Dimension drawString(Graphics g, Color c, Font f, String s, 
1127                                         int x, int y, int w, int h, int m, Awt awt) {
1128                                    
1129        Dimension d = new Dimension();
1130                                    
1131        if(s == null) {
1132          return d;
1133        }
1134        
1135        if(f == null) {
1136          f = g.getFont();
1137        }
1138        
1139        if(c == null) {
1140          c = g.getColor();
1141        }
1142        
1143                    g = g.create(x, y, w, h);
1144        d = measureString(s, f);
1145        
1146        g.setFont(f);
1147        g.setColor(c);
1148        
1149        if(d.width > w) {
1150          d.width = w;
1151        }
1152        
1153        if(d.height > h) {
1154          d.height = h;
1155        }
1156        
1157        if(awt != null && isHref(m)) {
1158          awt.doHref(s, x, y, d.width, h);
1159          g.setColor(Color.blue);
1160          m |= ULINE;
1161        }
1162        
1163        if(isRaised(m) || isSunken(m)) {
1164          d = drawString(g, isRaised(m)  ? brighter(c) : darker(c), f, s, 0, 0, w, h, m & ~EFFECT_MASK);
1165          d = drawString(g, isRaised(m)  ? darker(c) : brighter(c), f, s, 1, 1, w, h, m & ~EFFECT_MASK);
1166          g.dispose();
1167          return d;
1168        }
1169        
1170        switch(getPosition(m)) {
1171          case  N:
1172          case  S:
1173          case  C: x =(w - d.width) / 2; break;
1174          case NE:
1175          case  E:
1176          case SE: x = w - d.width; break;
1177          default: x = 0;
1178        }
1179        
1180        switch(getPosition(m)) {
1181          case  E: 
1182          case  W:
1183          case  C: y =(h - d.height) / 2; break;
1184          case SW:
1185          case  S:
1186          case SE: y = h - d.height; break;
1187          default: y = 0;
1188        }
1189        
1190        g.drawString(exString(s), x, y + g.getFontMetrics().getAscent());
1191        
1192        if(isUline(m)) {
1193          g.drawLine(x, y + g.getFontMetrics().getAscent() + 1, d.width, y + g.getFontMetrics().getAscent() + 1);
1194        }
1195        
1196        g.dispose();
1197        return d;
1198      }
1199      
1200      /**
1201       * Calculates the box needed to draw the string
1202       *
1203       * @param   s       the string
1204       * @param   f       the base font to use
1205       */
1206      public static Dimension measureString(String s, Font f) {
1207        if(f == null) {
1208          return new Dimension();
1209        }
1210        FontMetrics m = getMetrics(f);
1211        return new Dimension(m.stringWidth(exString(s)), m.getHeight());
1212      }
1213      
1214      /**
1215       * 
1216       * Draws a caption within the given box. drawCaption recognizes the
1217       * escape sequences above for changing text characteristics and supports
1218       * multiple lines
1219       *
1220       * @param   g       Graphics object to use
1221       * @param   c       color to use or null
1222       * @param   f       font to use or null
1223       * @param   s       the string
1224       * @param   x       x-position of describing rect
1225       * @param   y       y-position of describing rect
1226       * @param   w       width of describing rect
1227       * @param   h       height of describing rect
1228       * @param   m       mode including position & effect
1229       * @param   dl      the line delimiter usually "\n" specify "" for a single line
1230       */
1231      public static void drawCaption(Graphics g, Color c, Font f, String s, 
1232                                     int x, int y, int w, int h, int m, String dl) {
1233        drawCaption(g, c, f, s, x, y, w, h, m, dl, null);
1234      }
1235      
1236      /**
1237       * 
1238       * Draws a caption within the given box. drawCaption recognizes the
1239       * escape sequences above for changing text characteristics and supports
1240       * multiple lines
1241       *
1242       * @param   g       Graphics object to use
1243       * @param   c       color to use or null
1244       * @param   f       font to use or null
1245       * @param   s       the string
1246       * @param   x       x-position of describing rect
1247       * @param   y       y-position of describing rect
1248       * @param   w       width of describing rect
1249       * @param   h       height of describing rect
1250       * @param   m       mode including position & effect
1251       * @param   dl      the line delimiter usually "\n" specify "" for a single line
1252       * @param   awt     comp used as callback for hrefs
1253       */
1254      public static void drawCaption(Graphics g, Color c, Font f, String s, 
1255                                     int x, int y, int w, int h, int m, String dl, Awt awt) {
1256    
1257        if(s == null) {
1258          return;
1259        }
1260        
1261        if(c == null) {
1262          c = g.getColor();
1263        }
1264        
1265        if(f == null) {
1266          f = g.getFont();
1267        }
1268    
1269        Color    dc = c;                            // default color
1270        Font     df = f;                            // default font
1271        int      dm = m;                            // default mode
1272        int       X = 0;                            // current X
1273        Dimension D = measureCaption(s, f, df, dl); // total dims
1274        
1275        if(D.width > w) {
1276          D.width = w;
1277        }
1278        
1279        if(D.height > h) {
1280          D.height = h;
1281        }
1282    
1283        switch(getPosition(m)) {
1284          case  E: 
1285          case  W:
1286          case  C: y += (h - D.height) / 2; break;
1287          case SW:
1288          case  S:
1289          case SE: y +=  h - D.height; break;
1290        }
1291    
1292        for(StringSplitter L = new StringSplitter(s, dl); L.hasMoreTokens();) {
1293          String    l = L.nextToken();                  // one line
1294          Dimension d = measureCaption(l, f, df, dl);   // lines dimensions
1295          boolean   p = false;                          // already started to paint ?
1296          if(d.width > w) {
1297            d.width = w;
1298          }
1299          if(d.height > h) {
1300            d.height = h;
1301          }
1302          for(StringSplitter T = new StringSplitter("" + Null + l, "" + Esc); T.hasMoreTokens();) {
1303            String t = T.nextToken(); // one token
1304            c = getColor(t.charAt(0), c, dc, awt == null ? dc : awt.getBackground());
1305            f = getFont(t.charAt(0), f, df);
1306            m = getMode(t.charAt(0), m, dm);
1307            X = getTab(t.charAt(0), f, X);
1308            if(t.length() > 1) {
1309              if(p == false) {
1310                switch(getPosition(m)) {
1311                  case  N:
1312                  case  S:
1313                  case  C: X = x + (w - d.width) / 2; break;
1314                  case NE:
1315                  case  E:
1316                  case SE: X = x +  w - d.width; break;
1317                  default: X = x; break;
1318                }
1319                p = true;
1320              }
1321              X += drawString(g, c, f, t.substring(1), X, y, w, d.height, (m & POSITION_MASK) == C ? m & ~POSITION_MASK | W : m & ~E | W, awt).width;
1322            }
1323          }
1324          y += d.height;
1325        }
1326      }
1327      
1328      /**
1329       * Calculates the box needed to draw the caption
1330       *
1331       * @param   s       the string
1332       * @param   f       the base font to use
1333       * @param   df      the default font to use
1334       * @param   dl      the line delimiter usually "\n" specify "" for a single line
1335       */
1336      public static Dimension measureCaption(String s, Font f, Font df, String dl) {
1337        Dimension D = s.length() == 0 ? measureString("", f) : new Dimension();
1338        for(StringSplitter L = new StringSplitter(s, dl); L.hasMoreTokens();) {
1339          int w = 0, h = 0;
1340          for(StringSplitter T = new StringSplitter("" + Null + L.nextToken(), "" + Esc); T.hasMoreTokens();) {
1341            String t = T.nextToken();
1342            f = getFont(t.charAt(0), f, df);
1343            w = Math.max(w, getTab(t.charAt(0), f, w));
1344            Dimension d = measureString(t.substring(1), f);
1345            w += d.width;
1346            if(w > 0 || T.hasMoreTokens() == false) {
1347              h  = Math.max(d.height, h);
1348            }
1349          }
1350          D.height += h;
1351          D.width   = Math.max(w, D.width);;
1352        }
1353        return D;
1354      }
1355      
1356      /**
1357       * Returns the visible (right) part of a string resp. href.
1358       * Parts are separated using the unicode null char
1359       *
1360       * @param   s       the orig. string
1361       */
1362      public static String exString(String s) {
1363        int i = s.indexOf(Null);
1364        return s.substring(i == -1 ? 0 : i + 1);
1365      }
1366      
1367      /**
1368       * Returns the invisible (left or href) part of a string resp. href.
1369       * Parts are separated using the unicode null char
1370       *
1371       * @param   s       the orig. string
1372       */
1373      public static String exHref(String s) {
1374        int i = s.indexOf(Null);
1375        return s.substring(0, i == -1 ? s.length() : i);
1376      }
1377    
1378      /**
1379       * Returns the modified font for escape char u
1380       * leaves font unchanged if u does not descibe any font characteristics
1381       *
1382       * @param   u       the escape char
1383       * @param   f       the base font to use
1384       * @param   df      the default font to use
1385       */
1386      public static Font getFont(char u, Font f, Font df) {
1387        if(f == null) {
1388          return null;
1389        }
1390        switch(u) {
1391          case       Bold: return bold(f);
1392          case      Plain: return plain(f);
1393          case     Italic: return italic(f);
1394          case Bolditalic: return bolditalic(f);
1395          case     Larger: return larger(f);
1396          case    Smaller: return smaller(f);
1397          case      Serif: return serif(f);
1398          case  SansSerif: return sansserif(f);
1399          case Monospaced: return monospaced(f);
1400          case       None: return df;
1401        }
1402        if((u & FontMask) == FontSize0) {
1403          return getFont(f.getName(), f.getStyle(), u & FontSizeMask);
1404        }
1405        return f;
1406      }
1407      
1408      /**
1409       * Returns the modified color for escape char u
1410       * leaves color unchanged if u does not descibe any color characteristics
1411       *
1412       * @param   u       the escape char
1413       * @param   c       the base color to use
1414       * @param   dc      the default color to use
1415       * @param   dc      the background color to use
1416       */
1417      public static Color getColor(char u, Color c, Color dc, Color bg) {
1418        switch(u) {
1419          case    Darker:     return darker(c);
1420          case    Brighter:   return brighter(c);
1421          case    Background: return bg;
1422          case    Foreground: return dc;
1423          case    None:       return dc;
1424        }
1425        if((u & ColorMask) == White) {
1426          return new Color(((u & RedMask) >> 8) * 17, ((u & GreenMask) >> 4) * 17, ((u & BlueMask) >> 0) * 17);
1427        }
1428        return c;
1429      }
1430    
1431      /**
1432       * Returns the tab position for escape char u
1433       * return default tab if u does not descibe any tab position
1434       *
1435       * @param   u       the escape char
1436       * @param   f       the base font to use
1437       * @param   dt      the default tab to use
1438       */
1439      public static int getTab(char u, Font f, int dt) {
1440        if((u & TabMask) == Tab0) {
1441          return (u & TabSizeMask) * getMetrics(f).charWidth('8');
1442        } else {
1443          return dt;
1444        }
1445      }
1446      
1447      /**
1448       * Returns the modified mode for escape char u
1449       * leaves mode unchanged if u does not descibe any mode characteristics
1450       *
1451       * @param   u       the escape char
1452       * @param   m       the base mode to use
1453       * @param   dm      the default mode to use
1454       */
1455      public static int getMode(char u, int m, int dm) {
1456        switch(u) {
1457          case     North: return m & ~POSITION_MASK | N;
1458          case      East: return m & ~POSITION_MASK | E;
1459          case     South: return m & ~POSITION_MASK | S;
1460          case      West: return m & ~POSITION_MASK | W;
1461          case NorthWest: return m & ~POSITION_MASK | NW;
1462          case SouthWest: return m & ~POSITION_MASK | SW;
1463          case NorthEast: return m & ~POSITION_MASK | NE;
1464          case SouthEast: return m & ~POSITION_MASK | SE;
1465          case    Center: return m & ~POSITION_MASK | C;
1466          
1467          case      Flat: return m & ~EFFECT_MASK;
1468          case    Raised: return m & ~(RAISED|SUNKEN) | RAISED;
1469          case    Sunken: return m & ~(RAISED|SUNKEN) | SUNKEN;
1470          case     Uline: return m | ULINE;
1471          
1472          case   Hrefend: return m & ~MISC_MASK;
1473          case      Href: return m & ~MISC_MASK | HREF;
1474          
1475          case      None: return dm;
1476        }
1477        return m;
1478      }
1479      
1480      /**
1481       * Exctract position bits from m. 
1482       *
1483       * @param   m       mode
1484       */
1485      public static int getPosition(int m) {
1486        return (m & POSITION_MASK) == 0 ? NW : m & POSITION_MASK;
1487      }
1488    
1489      /**
1490       * True if m describes an effect (see above). 
1491       *
1492       * @param   m       mode
1493       */
1494      public static boolean isEffect(int m) {
1495        return (m & EFFECT_MASK) > 0;
1496      }
1497      
1498      /**
1499       * True if m describes effect Raised. 
1500       *
1501       * @param   m       mode
1502       */
1503      public static boolean isRaised(int m) {
1504        return (m & RAISED) == RAISED;
1505      }
1506      
1507      /**
1508       * True if m describes effect Sunken. 
1509       *
1510       * @param   m       mode
1511       */
1512      public static boolean isSunken(int m) {
1513        return (m & SUNKEN) == SUNKEN;
1514      }
1515      
1516      /**
1517       * True if m describes effect Bulk. 
1518       *
1519       * @param   m       mode
1520       */
1521      public static boolean isBulk(int m) {
1522        return (m & BULK) == BULK;
1523      }
1524      
1525      /**
1526       * True if m describes effect Underline. 
1527       *
1528       * @param   m       mode
1529       */
1530      public static boolean isUline(int m) {
1531        return (m & ULINE) == ULINE;
1532      }
1533    
1534      /**
1535       * True if we are in Href mode. 
1536       *
1537       * @param   m       mode
1538       */
1539      public static boolean isHref(int m) {
1540        return (m & HREF) == HREF;
1541      }
1542      
1543      /**
1544       * True if m describes effect Fill. 
1545       *
1546       * @param   m       mode
1547       */
1548      public static boolean isFill(int m) {
1549        return (m & FILL) == FILL;
1550      }
1551      
1552      /**
1553       * Contructor for an Awt component
1554       */
1555      public Awt() {
1556        if(this instanceof BorderPanel == false && this instanceof TabbedPanel == false) super.setLayout(null);
1557        
1558      }
1559      
1560      /**
1561       * callback routine for dynamic caption content
1562       * should be overwritten by implementing classes 
1563       *
1564       * @param   s       the href string (including href & text)
1565       * @param   x       x pos of describing rect
1566       * @param   y       y pos of describing rect
1567       * @param   w       width of describing rect
1568       * @param   h       height of describing rect
1569       */
1570      public void doHref(String s, int x, int y, int w, int h) {
1571      }
1572      
1573      /**
1574       * Overwrite Component.setBounds() to recognize resizing
1575       */
1576      public void setBounds(int x, int y, int w, int h) {
1577        super.setBounds(x, y, w, h);
1578        bim = null;
1579        big = null;
1580      }
1581      
1582      /**
1583       * Return currently needed Dimension
1584       * Should be overridden by Awt Components
1585       */
1586      public Dimension measure() {
1587        return new Dimension(insets.left + borderDepth + innerInsets.left + innerInsets.right  + borderDepth + insets.right,
1588                             insets.top  + borderDepth + innerInsets.top  + innerInsets.bottom + borderDepth + insets.bottom);
1589      }
1590      
1591      /**
1592       * Utility function for Awt Components
1593       * re-measures an invalidates if dims changed
1594       */
1595      public void doMeasure() {
1596        Dimension nd = measure();
1597        if(minimumDimension.equals(nd) == false) {
1598          minimumDimension = nd;
1599          invalidate();
1600        }
1601      }
1602      
1603      /**
1604       * Get this comps rectangle
1605       * totalsize - insets
1606       */
1607      public Rectangle getRectangle() {
1608        return new Rectangle(insets.left,
1609                             insets.top, 
1610                             getBounds().width - insets.left - insets.right,
1611                             getBounds().height - insets.top - insets.bottom);
1612      }
1613      
1614      /**
1615       * Get this comps rectangle within border
1616       * totalsize - insets - borderDepth
1617       */
1618      public Rectangle getBorderRectangle() {
1619        Rectangle r = getRectangle();
1620        return new Rectangle(r.x + borderDepth,
1621                             r.y + borderDepth, 
1622                             r.width - borderDepth - borderDepth,
1623                             r.height - borderDepth - borderDepth);
1624      }
1625      
1626      /**
1627       * Get this comps inner rectangle
1628       * totalsize - insets - borderDepth - innerInsets
1629       */
1630      public Rectangle getInnerRectangle() {
1631        Rectangle r = getBorderRectangle();
1632        return new Rectangle(r.x + innerInsets.left, 
1633                             r.y + innerInsets.top, 
1634                             r.width - innerInsets.left - innerInsets.right,
1635                             r.height - innerInsets.top - innerInsets.bottom);
1636      }
1637      
1638      /**
1639       * Overwrite java.awt.component.invalidate() to do
1640       * measuring
1641       * .
1642       */
1643      public void invalidate() {
1644        doMeasure();
1645        super.invalidate();
1646      }
1647      
1648      /**
1649       * override java.awt.Panel.addNotify()
1650       * called shortly after instantiation
1651       * first chance to call measure()
1652       */
1653      public void addNotify() {
1654        super.addNotify();
1655        minimumDimension = measure();
1656      }
1657    
1658      /**
1659       * override java.awt.Panel.getPreferredSize() and return
1660       * our minimum size
1661       */
1662      public Dimension getPreferredSize() {
1663        return minimumDimension;
1664      }
1665      
1666      /**
1667       * override java.awt.Panel.getMinimumSize()
1668       * @see Awt#getPreferredSize()
1669       */
1670      public Dimension getMinimumSize() {
1671        return minimumDimension;
1672      }
1673    
1674      /**
1675       * Overwrite java.awt.component.update() to avoid flickering
1676       * .
1677       */
1678       public void update(Graphics g) {
1679         if(g == null) return;
1680         paint(g);
1681       }
1682    
1683      /**
1684       * Actually do the work ... this basic paint() routine just draws the
1685       * border. Should be overwritten.
1686       *
1687       * @param   g       Graphics object to use
1688       */
1689      public void paint(Graphics g) {
1690        if(borderDepth > 0) {
1691          Rectangle r = getRectangle();
1692          paintBorder(g, r.x, r.y, r.width - 1, r.height - 1);
1693        }
1694      }
1695    
1696      /**
1697       * Paint the border
1698       *
1699       * @param   g       Graphics object to use
1700       * @param   x       x-position of describing rect
1701       * @param   y       y-position of describing rect
1702       * @param   w       width of describing rect
1703       * @param   h       height of describing rect
1704       */
1705      public void paintBorder(Graphics g, int x, int y, int w, int h) {
1706        if(g == null) return;
1707        switch(borderType) {
1708                            case TOP:
1709            drawLine(g, getBackground(), x, y, x + w, y, borderDepth, BULK);
1710            break;
1711                            case BULK:
1712                            case NONE:
1713                      case RAISED:
1714                      case SUNKEN:
1715            drawRectangle(g, getBackground(), x, y, w, h, borderDepth, borderType);
1716                                    break;
1717        }
1718      }
1719    
1720      /**
1721       * Paint the Background 
1722       *
1723       * @param   g       Graphics object to use
1724       */
1725      public void paintBackground(Graphics g) {
1726        paintBackground(g, 0, 0, getBounds().width, getBounds().height);
1727      }
1728      
1729      /**
1730       * Paint the Background 
1731       *
1732       * @param   g       Graphics object to use
1733       * @param   x       x-position of describing rect
1734       * @param   y       y-position of describing rect
1735       * @param   w       width of describing rect
1736       * @param   h       height of describing rect
1737       */
1738      public void paintBackground(Graphics g, int x, int y, int w, int h) {
1739        if(g == null) return;
1740        Awt a = getBGParent();
1741        if(a == null || a.isShowing() == false || isShowing() == false || a.getBGImage() == null) {
1742          drawRectangle(g, getBackground(), x, y, w, h, 1, FILL);
1743        } else {
1744          int xo = getLocationOnScreen().x - a.getLocationOnScreen().x;
1745          int yo = getLocationOnScreen().y - a.getLocationOnScreen().y;
1746          g.drawImage(a.getBGImage(), 
1747                      x,      y,      x + w,      y + h, 
1748                      x + xo, y + yo, x + xo + w, y + yo + h, this);
1749        }
1750      }
1751      
1752      public void paintBim(Graphics g) {
1753        if(g == null || bim == null) return;
1754        g.drawImage(bim, 0, 0, this);
1755      }
1756      
1757      public void makeBim() {
1758        if(bim == null) {
1759          bim = createImage(getBounds().width, getBounds().height);
1760          big = bim.getGraphics();
1761        }
1762      }
1763        
1764      /**
1765       * set the BG for this comp. BG can be one of
1766       * null      - use parent BG (or none)
1767       * Image     - use this Image
1768       * String    - use this filename/URL to load an Image
1769       * Awt       - inherit BG from this Awt component
1770       */
1771      public void setBG(Object b) {
1772        bg = b;
1773      }
1774      
1775      /**
1776       * set the BG for this comp only if none is currently present
1777       * (even from parent)
1778       */
1779      public void setBGIfNull(Object b) {
1780        if(getBGParentImage() == null) bg = b;
1781      }
1782      
1783      /**
1784       * return the object currently used as BG for this comp
1785       * Attn: all BG's are converted to Image the first time used
1786       * So this routine usually returns Object type Image
1787       */
1788      public Object getBG() {
1789        return bg;
1790      }
1791    
1792      /**
1793       * get the BG Image if any
1794       */
1795      public Image getBGImage() {
1796        if(bg == null) {  // no image ? -> ok
1797          return null;
1798        }
1799        if(bg instanceof Image) {  // already an Image ? -> make sure it fits
1800          bg = tile((Image) bg, getSize());
1801          return (Image) bg;
1802        }
1803        if(bg instanceof String) {  // a String ? -> try to load image
1804          bg = waitForImage(loadImage((String) bg));
1805          return getBGImage();
1806        }
1807        if(bg instanceof Awt && bg != this) { // an Awt Component ? -> try to get image form there
1808          bg = ((Awt) bg).getBGParentImage();
1809          return getBGImage();
1810        }
1811        return null;
1812      }
1813      
1814      /**
1815       * walk up the component tree to find a parent with a BG != null
1816       *
1817       * @param   i      the image
1818       */
1819      public Awt getBGParent() {
1820        if(getBG() == null) {
1821          Container p = getParent();
1822          if(p != null && p instanceof Awt) {
1823            return ((Awt) p).getBGParent();
1824          }
1825          return null;
1826        }
1827        return this;
1828      }
1829      
1830      /**
1831       * walk up the component tree to find a BG != null
1832       *
1833       * @param   i      the image
1834       */
1835      public Image getBGParentImage() {
1836        Awt a = getBGParent();
1837        return a == null  ? null : a.getBGImage();
1838      }
1839      
1840      /**
1841       * wait for an Image to load completely
1842       *
1843       * @param   i      the image
1844       */
1845      public Image waitForImage(Image i) {
1846        if(i == null) {
1847          return null;
1848        }
1849        try {
1850          MediaTracker t = new MediaTracker(this);
1851          t.addImage(i, 0);
1852          t.waitForID(0);
1853        } catch(InterruptedException x){
1854          Debug.debug(this, x);
1855          return null;
1856        }
1857        return i;
1858      }
1859    
1860      /**
1861       * scale (grow only) an Image to specified size by just
1862       * filling it with the original from left to right, top down
1863       * w, h will be rounded to the next multiple of original size
1864       *
1865       * @param   i      orinigal image
1866       * @param   d      new dims
1867       */
1868      public Image tile(Image i, Dimension d) {
1869        return tile(i, d.width, d.height);
1870      }
1871      
1872      /**
1873       * scale (grow only) an Image to specified size by just
1874       * filling it with the original from left to right, top down
1875       * w, h will be rounded to the next multiple of original size
1876       *
1877       * @param   i      orinigal image
1878       * @param   w      new width
1879       * @param   h      new height
1880       */
1881      public Image tile(Image i, int w, int h) {
1882        if(i == null) {
1883          return null;
1884        }
1885        
1886        int iw = i.getWidth(this);
1887        int ih = i.getHeight(this);
1888        
1889        if(w > iw || h > ih) {
1890        
1891          w  = w / iw + 1;
1892          h  = h / ih + 1;
1893          w *= iw;
1894          h *= ih;
1895        
1896          Image I = createImage(w, h);
1897          for(int H = 0; H < h; H += i.getHeight(this)) {
1898            for(int W = 0; W < w; W += i.getWidth(this)) {
1899              I.getGraphics().drawImage(i, W, H, this);
1900            }
1901          }
1902          return waitForImage(I);
1903        } else {
1904          return i;
1905        }
1906      }
1907    
1908      /**
1909       * override java.awt.Panel.setEnabled()
1910       * just to recocnize state change
1911       *
1912       * @param   enabled      enabled?
1913       */
1914      public void setEnabled(boolean enabled) {
1915        if(enabled == super.isEnabled()) {
1916          return;
1917        }
1918        
1919        super.setEnabled(enabled);
1920        
1921        if(enabled) {
1922          for(Enumeration e = componentStates.keys(); e.hasMoreElements(); ) {
1923            ((Component) e.nextElement()).setEnabled(true);
1924          }
1925          componentStates.clear();
1926        } else {
1927          for(int i = 0; i < getComponents().length; ++i) {
1928            Component c = getComponents()[i];
1929            if(c.isEnabled()) {
1930              componentStates.put(c, this);
1931              c.setEnabled(false);
1932            }
1933          }
1934        }
1935        repaint();
1936      }
1937      
1938      /**
1939       * override java.awt.Panel.setVisible()
1940       * just to recocnize state change
1941       *
1942       * @param   v      visible?
1943       */
1944      public void setVisible(boolean v) {
1945        super.setVisible(v);
1946        repaint();
1947      }
1948    
1949      /**
1950       * sets a new border
1951       *
1952       * @param   i      the border around the component
1953       */
1954      public void setInsets(Insets i) {
1955        if(i != null && insets.equals(i) == false) {
1956          insets = i;
1957          doMeasure();
1958          repaint();
1959        }
1960      }
1961      
1962      /**
1963       * get current border
1964       */
1965      public Insets getInsets() {
1966        return insets;
1967      }
1968      
1969      /**
1970       * sets a new border
1971       *
1972       * @param   insets      the border within the component
1973       */
1974      public void setInnerInsets(Insets i) {
1975        if(i != null && innerInsets.equals(i) == false) {
1976          innerInsets = i;
1977          doMeasure();
1978          repaint();
1979        }
1980      }
1981      
1982      /**
1983       * get current inner border
1984       */
1985      public Insets getInnerInsets() {
1986        return innerInsets;
1987      }
1988      
1989      /**
1990       * set borderdepth
1991       * @param   d      depth in pixel
1992       */
1993      public void setBorderDepth(int d) {
1994        if(d >= 0 && borderDepth != d) {
1995          borderDepth = d;
1996          doMeasure();
1997          repaint();
1998        }
1999      }
2000      
2001      /**
2002       * get borderdepth
2003       */
2004      public int getBorderDepth() {
2005        return borderDepth;
2006      }
2007      
2008      /**
2009       * set bodertype
2010       * @param   t      bordertype [TOP NONE BULK RAISED SUNKEN
2011       */
2012      public void setBorderType(int t) {
2013        if(borderType != t) {
2014          borderType = t;
2015          doMeasure();
2016          repaint();
2017        }
2018      }
2019      
2020      /**
2021       * set bodertype - String version (attributes from property file)
2022       * @param   t      bordertype [North(Top) None Bulk Raised Sundekn]
2023       */
2024      public void setBorderType(String t) {
2025        if(t != null && t.length() == 2 && t.charAt(0) == Esc) {
2026          switch(t.charAt(1)) {
2027            case North:  setBorderType(TOP); break;
2028            case None:   setBorderType(NONE); break;
2029            case Bulk:   setBorderType(BULK); break;
2030            case Raised: setBorderType(RAISED); break;
2031            case Sunken: setBorderType(SUNKEN); break;
2032          }
2033        }
2034      }
2035    
2036      /**
2037       * get bodertype
2038       */
2039      public int getBorderType() {
2040        return borderType;
2041      }
2042      
2043      /**
2044       * override java.awt.Panel.setFont(Font font)
2045       * just to recognize the change
2046       */
2047      public void setFont(Font f) {
2048        if(f != null) {
2049          super.setFont(f);
2050          doMeasure();
2051          repaint();
2052        }
2053      }  
2054    
2055      /**
2056       * implement java.awt.event.MouseListener
2057       */
2058      public void mousePressed(MouseEvent e) {
2059      }
2060    
2061      /**
2062       * implement java.awt.event.MouseListener
2063       */
2064      public void mouseReleased(MouseEvent e) {
2065      }
2066    
2067      /**
2068       * implement java.awt.event.MouseListener
2069       */
2070      public void mouseExited(MouseEvent e) {
2071      }
2072    
2073      /**
2074       * implement java.awt.event.MouseListener
2075       */
2076      public void mouseEntered(MouseEvent e) {
2077      }
2078    
2079      /**
2080       * implement java.awt.event.MouseListener
2081       */
2082      public void mouseClicked(MouseEvent e) {
2083      }
2084    
2085      /**
2086       * implement java.awt.event.MouseListener
2087       */
2088      public void mouseMoved(MouseEvent e) {
2089      }
2090    
2091      /**
2092       * implement java.awt.event.MouseListener
2093       */
2094      public void mouseDragged(MouseEvent e) {
2095      }
2096      
2097      /**
2098       * implement java.awt.event.KeyListener
2099       */
2100      public void keyTyped(KeyEvent  e) {
2101      }
2102    
2103      /**
2104       * implement java.awt.event.KeyListener
2105       */
2106      public void keyPressed(KeyEvent  e) {
2107      }
2108    
2109      /**
2110       * implement java.awt.event.KeyListener
2111       */
2112      public void keyReleased(KeyEvent  e) {
2113      }
2114    
2115      /**
2116       * implement java.awt.event.AdjustmentListener
2117       */
2118      public void adjustmentValueChanged(AdjustmentEvent e) {
2119      }
2120      
2121      /**
2122       * implement java.awt.event.FocusListener
2123       */
2124      public void focusGained(FocusEvent e) {
2125      }
2126    
2127      /**
2128       * implement java.awt.event.FocusListener
2129       */
2130      public void focusLost(FocusEvent e) {
2131      }
2132    
2133      /**
2134       * implement java.awt.ItemSelectable
2135       */
2136      public Object[] getSelectedObjects() {
2137        return null;
2138      }
2139    
2140      /**
2141       * adds an actionListener 
2142       * @param   l       the actionListener to add
2143       */
2144      public void addActionListener(ActionListener l) {
2145        actionListener = AWTEventMulticaster.add(actionListener, l);
2146      }
2147    
2148      /**
2149       * removes an actionListener 
2150       * @param   l       the actionListener to remove
2151       */
2152      public void removeActionListener(ActionListener l) {
2153        actionListener = AWTEventMulticaster.remove(actionListener, l);
2154      }
2155    
2156      /**
2157       * notifies all actionListeners
2158       * @param   e       the event to post
2159       */
2160      protected synchronized void notifyActionListeners(ActionEvent e) {
2161        if(actionListener != null) actionListener.actionPerformed(e);
2162      }
2163      
2164      /**
2165       * adds an itemListener 
2166       * @param   l       the itemListener to add
2167       */
2168      public void addItemListener(ItemListener l) {
2169        itemListener = AWTEventMulticaster.add(itemListener, l);
2170      }
2171    
2172      /**
2173       * removes an itemListener 
2174       * @param   l       the itemListener to remove
2175       */
2176      public void removeItemListener(ItemListener l) {
2177        itemListener = AWTEventMulticaster.remove(itemListener, l);
2178      }
2179    
2180      /**
2181       * notifies all itemListeners
2182       * @param   e       the event to post
2183       */
2184      protected synchronized void notifyItemListeners(ItemEvent e) {
2185        if(itemListener != null) itemListener.itemStateChanged(e);
2186      }
2187    }