001    /* BasicBorders.java --
002       Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import java.awt.Color;
042    import java.awt.Component;
043    import java.awt.Graphics;
044    import java.awt.Insets;
045    import java.awt.Rectangle;
046    import java.io.Serializable;
047    
048    import javax.swing.AbstractButton;
049    import javax.swing.ButtonModel;
050    import javax.swing.JButton;
051    import javax.swing.JPopupMenu;
052    import javax.swing.JSplitPane;
053    import javax.swing.JToolBar;
054    import javax.swing.UIManager;
055    import javax.swing.border.AbstractBorder;
056    import javax.swing.border.BevelBorder;
057    import javax.swing.border.Border;
058    import javax.swing.plaf.BorderUIResource;
059    import javax.swing.plaf.UIResource;
060    import javax.swing.text.JTextComponent;
061    
062    /**
063     * Provides various borders for the Basic look and feel.
064     *
065     * @author Sascha Brawer (brawer@dandelis.ch)
066     */
067    public class BasicBorders
068    {
069      /**
070       * A MarginBorder that gets shared by multiple components.
071       * Created on demand by the private helper function {@link
072       * #getMarginBorder()}.
073       */
074      private static MarginBorder sharedMarginBorder;
075    
076    
077      /**
078       * Returns a border for drawing push buttons.
079       *
080       * <p>The colors of the border are retrieved from the
081       * <code>UIDefaults</code> of the currently active look and feel
082       * using the keys <code>&#x201c;Button.shadow&#x201d;</code>,
083       * <code>&#x201c;Button.darkShadow&#x201d;</code>,
084       * <code>&#x201c;Button.light&#x201d;</code>, and
085       * <code>&#x201c;Button.highlight&#x201d;</code>.
086       *
087       * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
088       * height="170" alt="[A screen shot of the returned border]" />
089       *
090       * @return a {@link
091       *         javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
092       *         whose outer border is a {@link ButtonBorder} and whose
093       *         inner border is a {@link MarginBorder}.
094       */
095      public static Border getButtonBorder()
096      {
097        Border outer;
098    
099        /* The keys for UIDefaults have been determined by writing a
100         * test program that dumps the UIDefaults to stdout; that program
101         * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
102         * the key "light" is usually called "highlight", and "highlight"
103         * is usually called "lightHighlight".
104         */
105        outer = new ButtonBorder(UIManager.getColor("Button.shadow"),
106                                 UIManager.getColor("Button.darkShadow"),
107                                 UIManager.getColor("Button.light"),
108                                 UIManager.getColor("Button.highlight"));
109    
110        /* While the inner border is shared between multiple buttons,
111         * we do not share the outer border because ButtonBorders store
112         * their border colors. We cannot guarantee that the colors
113         * (which come from UIDefaults) are unchanged between invocations
114         * of getButtonBorder. We could store the last colors, and share
115         * the button border if the colors are the same as in the last
116         * invocation, but it probably is not worth the effort.
117         */
118        return new BorderUIResource.CompoundBorderUIResource(
119          outer,
120          /* inner */ getMarginBorder());
121      }
122    
123    
124      /**
125       * Returns a border for drawing radio buttons.
126       *
127       * <p>The colors of the border are retrieved from the
128       * <code>UIDefaults</code> of the currently active look and feel
129       * using the keys <code>&#x201c;RadioButton.shadow&#x201d;</code>,
130       * <code>&#x201c;RadioButton.darkShadow&#x201d;</code>,
131       * <code>&#x201c;RadioButton.light&#x201d;</code>, and
132       * <code>&#x201c;RadioButton.highlight&#x201d;</code>.
133       *
134       * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
135       * height="135" alt="[A screen shot of the returned border]" />
136       *
137       * @return a {@link
138       *         javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
139       *         whose outer border is a {@link RadioButtonBorder} and whose
140       *         inner border is a {@link MarginBorder}.
141       */
142      public static Border getRadioButtonBorder()
143      {
144        Border outer;
145    
146        /* The keys for UIDefaults have been determined by writing a
147         * test program that dumps the UIDefaults to stdout; that program
148         * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
149         * the key "light" is usually called "highlight", and "highlight"
150         * is usually called "lightHighlight".
151         */
152        outer = new RadioButtonBorder(
153          UIManager.getColor("RadioButton.shadow"),
154          UIManager.getColor("RadioButton.darkShadow"),
155          UIManager.getColor("RadioButton.light"),
156          UIManager.getColor("RadioButton.highlight"));
157    
158        /* While the inner border is shared between multiple buttons, we
159         * do not share the outer border because RadioButtonBorders, being
160         * ButtonBorders, store their border colors. We cannot guarantee
161         * that the colors (which come from UIDefaults) are unchanged
162         * between invocations of getButtonBorder. We could store the last
163         * colors, and share the button border if the colors are the same
164         * as in the last invocation, but it probably is not worth the
165         * effort.
166         */
167        return new BorderUIResource.CompoundBorderUIResource(
168          outer,
169          /* inner */ getMarginBorder());
170      }
171    
172    
173      /**
174       * Returns a border for drawing toggle buttons.
175       *
176       * <p>The colors of the border are retrieved from the
177       * <code>UIDefaults</code> of the currently active look and feel
178       * using the keys <code>&#x201c;ToggleButton.shadow&#x201d;</code>,
179       * <code>&#x201c;ToggleButton.darkShadow&#x201d;</code>,
180       * <code>&#x201c;ToggleButton.light&#x201d;</code>, and
181       * <code>&#x201c;ToggleButton.highlight&#x201d;</code>.
182       *
183       * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png" width="270"
184       * height="135" alt="[A screen shot of the returned border]" />
185       *
186       * @return a {@link
187       *         javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
188       *         whose outer border is a {@link ToggleButtonBorder} and whose
189       *         inner border is a {@link MarginBorder}.
190       */
191      public static Border getToggleButtonBorder()
192      {
193        Border outer;
194    
195        /* The keys for UIDefaults have been determined by writing a
196         * test program that dumps the UIDefaults to stdout; that program
197         * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
198         * the key "light" is usually called "highlight", and "highlight"
199         * is usually called "lightHighlight".
200         */
201        outer = new ToggleButtonBorder(
202          UIManager.getColor("ToggleButton.shadow"),
203          UIManager.getColor("ToggleButton.darkShadow"),
204          UIManager.getColor("ToggleButton.light"),
205          UIManager.getColor("ToggleButton.highlight"));
206    
207        /* While the inner border is shared between multiple buttons, we
208         * do not share the outer border because ToggleButtonBorders, being
209         * ButtonBorders, store their border colors. We cannot guarantee
210         * that the colors (which come from UIDefaults) are unchanged
211         * between invocations of getButtonBorder. We could store the last
212         * colors, and share the button border if the colors are the same
213         * as in the last invocation, but it probably is not worth the
214         * effort.
215         */
216        return new BorderUIResource.CompoundBorderUIResource(
217          outer,
218          /* inner */ getMarginBorder());
219      }
220    
221    
222      /**
223       * Returns a border for drawing a two-pixel thick separator line
224       * below menu bars.
225       *
226       * <p>The colors of the border are retrieved from the
227       * <code>UIDefaults</code> of the currently active look and feel
228       * using the keys <code>&#x201c;MenuBar.shadow&#x201d;</code> and
229       * <code>&#x201c;MenuBar.highlight&#x201d;</code>.
230       *
231       * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
232       * height="140" alt="[A screen shot of a JMenuBar with this border]" />
233       *
234       * @return a {@link MenuBarBorder}.
235       *
236       * @see javax.swing.JMenuBar
237       */
238      public static Border getMenuBarBorder()
239      {
240        /* See comment in methods above for why this border is not shared. */
241        return new MenuBarBorder(UIManager.getColor("MenuBar.shadow"),
242                                 UIManager.getColor("MenuBar.highlight"));
243      }
244    
245    
246      /**
247       * Returns a border for drawing a one-pixel thick border around
248       * split panes that are interrupted where the divider joins the
249       * border.
250       *
251       * <p>The colors of the border are retrieved from the
252       * <code>UIDefaults</code> of the currently active look and feel
253       * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
254       * <code>&#x201c;SplitPane.highlight&#x201d;</code>.
255       *   
256       * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
257       * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
258       *
259       * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
260       * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
261       *
262       * @return a {@link SplitPaneBorder}.
263       *
264       * @see javax.swing.JSplitPane
265       * @see #getSplitPaneDividerBorder()
266       */
267      public static Border getSplitPaneBorder()
268      {
269        /* See comment in methods above for why this border is not shared. */
270        return new SplitPaneBorder(UIManager.getColor("SplitPane.highlight"),
271                                   UIManager.getColor("SplitPane.darkShadow"));
272      }
273    
274    
275      /**
276       * Returns a border for drawing a one-pixel thick border around
277       * the divider of split panes.
278       *
279       * <p>The colors of the edges that are adjacent to the child components
280       * of the <code>JSplitPane</code> are retrieved from the
281       * <code>UIDefaults</code> of the currently active look and feel
282       * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
283       * <code>&#x201c;SplitPane.highlight&#x201d;</code>. The color of the
284       * other two edges is the background color of the divider.
285       *
286       * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
287       * width="520" height="200" alt= 
288       * "[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
289       *
290       * @return an instance of <code>SplitPaneDividerBorder</code>, which is
291       *         not a public API class of this package.
292       *
293       * @see javax.swing.JSplitPane
294       * @see javax.swing.plaf.basic.BasicSplitPaneDivider
295       * @see #getSplitPaneBorder()
296       *
297       * @since 1.3
298       */
299      public static Border getSplitPaneDividerBorder()
300      {
301        /* See comment in methods above for why this border is not shared. */
302        return new SplitPaneDividerBorder();
303      }
304    
305    
306      /**
307       * Returns a border for drawing a border around a text field
308       * that makes the field appear as etched into the surface.
309       *
310       * <p>The colors of the border are retrieved from the
311       * <code>UIDefaults</code> of the currently active look and feel
312       * using the keys <code>&#x201c;TextField.shadow&#x201d;</code>,
313       * <code>&#x201c;TextField.darkShadow&#x201d;</code>,
314       * <code>&#x201c;TextField.light&#x201d;</code>, and
315       * <code>&#x201c;TextField.highlight&#x201d;</code>.
316       *
317       * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
318       * height="200" alt="[A screen shot of a border returned by
319       * this method]" />
320       *
321       * @return an instance of {@link FieldBorder}.
322       *
323       * @see javax.swing.JTextField
324       * @see javax.swing.text.JTextComponent
325       */
326      public static Border getTextFieldBorder()
327      {
328        /* See comment in methods above for why this border is not shared. */
329        return new FieldBorder(
330          UIManager.getColor("TextField.shadow"),
331          UIManager.getColor("TextField.darkShadow"),
332          UIManager.getColor("TextField.light"),
333          UIManager.getColor("TextField.highlight"));
334      }
335      
336    
337      /**
338       * Returns a two-pixel thick, green
339       * <code>LineBorderUIResource</code>.  This is so ugly that look and
340       * feels better use different borders for their progress bars, or
341       * they will look really terrible.
342       *
343       * <p><img src="doc-files/BasicBorders-1.png" width="120" height="80"
344       * alt="[A screen shot of a border returned by this method]" />
345       */
346      public static Border getProgressBarBorder()
347      {
348        /* There does not seem to exist a way to parametrize the color
349         * or thickness of the border through UIDefaults.
350         */
351        return new BorderUIResource.LineBorderUIResource(Color.green, 2);
352      }
353    
354    
355      /**
356       * Returns a border that is composed of a raised bevel border and a
357       * one-pixel thick line border.
358       *
359       * <p><img src="doc-files/BasicBorders-2.png" width="300" height="200"
360       * alt="[A screen shot of a border returned by this method]" />
361       *
362       * <p>The colors of the border are retrieved from the
363       * <code>UIDefaults</code> of the currently active look and feel
364       * using the keys <code>&#x201c;InternalFrame.borderShadow&#x201d;</code>,
365       * <code>&#x201c;InternalFrame.borderDarkShadow&#x201d;</code>,
366       * <code>&#x201c;InternalFrame.borderLight&#x201d;</code>,
367       * <code>&#x201c;InternalFrame.borderHighlight&#x201d;</code>, and
368       * (for the inner one-pixel thick line)
369       * <code>&#x201c;InternalFrame.borderColor&#x201d;</code>.
370       */
371      public static Border getInternalFrameBorder()
372      {
373        Color shadow, darkShadow, highlight, lightHighlight, line;
374    
375        /* See comment in methods above for why this border is not shared. */
376        shadow = UIManager.getColor("InternalFrame.borderShadow");
377        darkShadow = UIManager.getColor("InternalFrame.borderDarkShadow");
378        highlight = UIManager.getColor("InternalFrame.borderLight");
379        lightHighlight = UIManager.getColor("InternalFrame.borderHighlight");
380        line = UIManager.getColor("InternalFrame.borderColor");
381    
382        return new BorderUIResource.CompoundBorderUIResource(
383          /* outer border */
384          new BorderUIResource.BevelBorderUIResource(
385            BevelBorder.RAISED,
386            (highlight != null) ? highlight : Color.lightGray,
387            (lightHighlight != null) ? lightHighlight : Color.white,
388            (darkShadow != null) ? darkShadow : Color.black,
389            (shadow != null) ? shadow : Color.gray),
390    
391          /* inner border */
392          new BorderUIResource.LineBorderUIResource(
393            (line != null) ? line : Color.lightGray));
394      }
395    
396    
397      /**
398       * Returns a shared MarginBorder.
399       */
400      static Border getMarginBorder()  // intentionally not public
401      {
402        /* Swing is not designed to be thread-safe, so there is no
403         * need to synchronize the access to the global variable.
404         */
405        if (sharedMarginBorder == null)
406          sharedMarginBorder = new MarginBorder();
407    
408        return sharedMarginBorder;
409      }
410      
411      
412      /**
413       * A border whose appearance depends on the state of
414       * the enclosed button.
415       *
416       * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
417       * height="170" alt="[A screen shot of this border]" />
418       *
419       * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
420       *
421       * @author Sascha Brawer (brawer@dandelis.ch)
422       */
423      public static class ButtonBorder
424        extends AbstractBorder
425        implements Serializable, UIResource
426      {
427        /**
428         * Determined using the <code>serialver</code> tool
429         * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
430         */
431        static final long serialVersionUID = -157053874580739687L;
432        
433        
434        /**
435         * The color for drawing the shaded parts of the border.
436         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
437         */
438        protected Color shadow;
439        
440        
441        /**
442         * The color for drawing the dark shaded parts of the border.
443         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
444         */
445        protected Color darkShadow;
446        
447        
448        /**
449         * The color for drawing the highlighted parts of the border.
450         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
451         */
452        protected Color highlight;
453        
454        
455        /**
456         * The color for drawing the bright highlighted parts of the border.
457         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
458         */
459        protected Color lightHighlight;
460        
461        
462        /**
463         * Constructs a new border for drawing a button in the Basic
464         * look and feel.
465         *
466         * @param shadow the shadow color.
467         * @param darkShadow a darker variant of the shadow color.
468         * @param highlight the highlight color.
469         * @param lightHighlight a brighter variant of the highlight  color.
470         */
471        public ButtonBorder(Color shadow, Color darkShadow,
472                            Color highlight, Color lightHighlight)
473        {
474          /* These colors usually come from the UIDefaults of the current
475           * look and feel. Use fallback values if the colors are not
476           * supplied.  The API specification is silent about what
477           * behavior is expected for null colors, so users should not
478           * rely on this fallback (which is why it is not documented in
479           * the above Javadoc).
480           */
481          this.shadow = (shadow != null) ? shadow : Color.gray;
482          this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
483          this.highlight = (highlight != null) ? highlight : Color.lightGray;
484          this.lightHighlight = (lightHighlight != null)
485            ? lightHighlight
486            : Color.white;
487        }
488        
489    
490        /**
491         * Paints the ButtonBorder around a given component.
492         *
493         * @param c the component whose border is to be painted.
494         * @param g the graphics for painting.
495         * @param x the horizontal position for painting the border.
496         * @param y the vertical position for painting the border.
497         * @param width the width of the available area for painting the border.
498         * @param height the height of the available area for painting the border.
499         *
500         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
501         */
502        public void paintBorder(Component c, Graphics  g,
503                                int x, int y, int width, int height)
504        {
505          ButtonModel bmodel = null;
506          
507          if (c instanceof AbstractButton)
508            bmodel = ((AbstractButton) c).getModel();
509          
510          BasicGraphicsUtils.drawBezel(
511            g, x, y, width, height,
512            /* pressed */ (bmodel != null)
513                            && /* mouse button pressed */ bmodel.isPressed()
514                            && /* mouse inside */ bmodel.isArmed(),
515            /* default */ (c instanceof JButton)
516                            && ((JButton) c).isDefaultButton(),
517            shadow, darkShadow, highlight, lightHighlight);
518        }
519        
520        
521        /**
522         * Measures the width of this border.
523         *
524         * <p>Although the thickness of the actually painted border
525         * depends on the state of the enclosed component, this
526         * measurement always returns the same amount of pixels.  Indeed,
527         * it would be rather confusing if a button was appearing to
528         * change its size depending on whether it is pressed or not.
529         *
530         * @param c the component whose border is to be measured.
531         *
532         * @return an Insets object whose <code>left</code>,
533         *         <code>right</code>, <code>top</code> and
534         *         <code>bottom</code> fields indicate the width of the
535         *         border at the respective edge.
536         *
537         * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 
538         */
539        public Insets getBorderInsets(Component c)
540        {
541          /* There is no obvious reason for overriding this method, but we
542           * try to have exactly the same API as the Sun reference
543           * implementation.
544           */
545          return getBorderInsets(c, null);
546        }
547    
548        
549        /**
550         * Measures the width of this border, storing the results into a
551         * pre-existing Insets object.
552         *
553         * <p>Although the thickness of the actually painted border
554         * depends on the state of the enclosed component, this
555         * measurement always returns the same amount of pixels.  Indeed,
556         * it would be rather confusing if a button was appearing to
557         * change its size depending on whether it is pressed or not.
558         *
559         * @param insets an Insets object for holding the result values.
560         *        After invoking this method, the <code>left</code>,
561         *        <code>right</code>, <code>top</code> and
562         *        <code>bottom</code> fields indicate the width of the
563         *        border at the respective edge.
564         *
565         * @return the same object that was passed for <code>insets</code>.
566         *
567         * @see #getBorderInsets(Component)
568         */
569        public Insets getBorderInsets(Component c, Insets insets)
570        {
571          /* The exact amount has been determined using a test program
572           * that was run on the Sun reference implementation. With
573           * Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, the result is
574           * [3, 3, 3, 3]. With Sun JDK 1.4.1_01 on Linux/x86, the
575           * result is [2, 3, 3, 3]. We use the values from the 1.4.1_01
576           * release.
577           */
578          if (insets == null)
579            return new Insets(2, 3, 3, 3);
580    
581          insets.top = 2;
582          insets.bottom = insets.left = insets.right = 3;
583          return insets;
584        }
585      }
586      
587      
588      /**
589       * A border that makes its enclosed component appear as lowered
590       * into the surface. Typically used for text fields.
591       *
592       * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
593       * height="200" alt="[A screen shot of this border]" />
594       *
595       * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
596       *
597       * @author Sascha Brawer (brawer@dandelis.ch)
598       */
599      public static class FieldBorder
600        extends AbstractBorder
601        implements UIResource
602      {
603        /**
604         * Determined using the <code>serialver</code> tool
605         * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
606         */
607        static final long serialVersionUID = 949220756998454908L;
608    
609    
610        /**
611         * The color for drawing the outer half of the top and left
612         * edges.
613         */
614        protected Color shadow;
615    
616    
617        /**
618         * The color for drawing the inner half of the top and left
619         * edges.
620         */
621        protected Color darkShadow;
622    
623    
624        /**
625         * The color for drawing the inner half of the bottom and right
626         * edges.
627         */
628        protected Color highlight;
629    
630    
631        /**
632         * The color for drawing the outer half of the bottom and right
633         * edges.
634         */
635        protected Color lightHighlight;
636    
637    
638        /**
639         * Constructs a new border for drawing a text field in the Basic
640         * look and feel.
641         *
642         * @param shadow the color for drawing the outer half
643         *        of the top and left edges.
644         *
645         * @param darkShadow the color for drawing the inner half
646         *        of the top and left edges.
647         *
648         * @param highlight the color for drawing the inner half
649         *        of the bottom and right edges.
650         *
651         * @param lightHighlight the color for drawing the outer half
652         *        of the bottom and right edges.
653         */
654        public FieldBorder(Color shadow, Color darkShadow,
655                           Color highlight, Color lightHighlight)
656        {
657          /* These colors usually come from the UIDefaults of the current
658           * look and feel. Use fallback values if the colors are not
659           * supplied.  The API specification is silent about what
660           * behavior is expected for null colors, so users should not
661           * rely on this fallback (which is why it is not documented in
662           * the above Javadoc).
663           */
664          this.shadow = (shadow != null) ? shadow : Color.gray;
665          this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
666          this.highlight = (highlight != null) ? highlight : Color.lightGray;
667          this.lightHighlight = (lightHighlight != null)
668            ? lightHighlight : Color.white;
669        }
670    
671        
672        /**
673         * Paints the FieldBorder around a given component.
674         *
675         * @param c the component whose border is to be painted.
676         * @param g the graphics for painting.
677         * @param x the horizontal position for painting the border.
678         * @param y the vertical position for painting the border.
679         * @param width the width of the available area for painting the border.
680         * @param height the height of the available area for painting the border.
681         *
682         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
683         */
684        public void paintBorder(Component c, Graphics  g,
685                                int x, int y, int width, int height)
686        {
687          BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height,
688                                            shadow, darkShadow,
689                                            highlight, lightHighlight);
690        }
691        
692        
693        /**
694         * Measures the width of this border.
695         *
696         * @param c the component whose border is to be measured.
697         *        If <code>c</code> is an instance of {@link
698         *        javax.swing.text.JTextComponent}, its margin is
699         *        added to the border size.
700         *
701         * @return an Insets object whose <code>left</code>,
702         *         <code>right</code>, <code>top</code> and
703         *         <code>bottom</code> fields indicate the width of the
704         *         border at the respective edge.
705         *
706         * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
707         */
708        public Insets getBorderInsets(Component c)
709        {
710          return getBorderInsets(c, null);
711        }
712    
713    
714        /**
715         * Measures the width of this border, storing the results into a
716         * pre-existing Insets object.
717         *
718         * @param c the component whose border is to be measured.
719         *        If <code>c</code> is an instance of {@link
720         *        javax.swing.text.JTextComponent}, its margin is
721         *        added to the border size.
722         *
723         * @param insets an Insets object for holding the result values.
724         *        After invoking this method, the <code>left</code>,
725         *        <code>right</code>, <code>top</code> and
726         *        <code>bottom</code> fields indicate the width of the
727         *        border at the respective edge.
728         *
729         * @return the same object that was passed for <code>insets</code>.
730         *
731         * @see #getBorderInsets(Component)
732         */
733        public Insets getBorderInsets(Component c, Insets insets)
734        {
735          if (insets == null)
736            insets = new Insets(2, 2, 2, 2);
737          else
738            insets.top = insets.left = insets.bottom = insets.right = 2;
739    
740          if (c instanceof JTextComponent)
741          {
742            Insets margin = ((JTextComponent) c).getMargin();
743            insets.top += margin.top;
744            insets.left += margin.left;
745            insets.bottom += margin.bottom;
746            insets.right += margin.right;
747          }
748    
749          return insets;
750        }
751      }
752      
753      
754      /**
755       * An invisible, but spacing border whose margin is determined
756       * by calling the <code>getMargin()</code> method of the enclosed
757       * component.  If the enclosed component has no such method,
758       * this border will not occupy any space.
759       *
760       * <p><img src="doc-files/BasicBorders.MarginBorder-1.png" width="325"
761       * height="200" alt="[An illustration that shows how MarginBorder
762       * determines its borders]" />
763       *
764       * @author Sascha Brawer (brawer@dandelis.ch)
765       */
766      public static class MarginBorder
767        extends AbstractBorder
768        implements Serializable, UIResource
769      {
770        /**
771         * Determined using the <code>serialver</code> tool
772         * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
773         */
774        static final long serialVersionUID = -3035848353448896090L;
775        
776        
777        /**
778         * Constructs a new MarginBorder.
779         */
780        public MarginBorder()
781        {
782          // Nothing to do here.
783        }
784        
785        /**
786         * Measures the width of this border.
787         *
788         * @param c the component whose border is to be measured.
789         *
790         * @return an Insets object whose <code>left</code>, <code>right</code>,
791         *         <code>top</code> and <code>bottom</code> fields indicate the
792         *         width of the border at the respective edge.
793         *
794         * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
795         */
796        public Insets getBorderInsets(Component c)
797        {
798          return getBorderInsets(c, new Insets(0, 0, 0, 0));
799        }
800        
801        
802        /**
803         * Determines the insets of this border by calling the
804         * <code>getMargin()</code> method of the enclosed component.  The
805         * resulting margin will be stored into the the <code>left</code>,
806         * <code>right</code>, <code>top</code> and <code>bottom</code>
807         * fields of the passed <code>insets</code> parameter.
808         *
809         * <p>Unfortunately, <code>getMargin()</code> is not a method of
810         * {@link javax.swing.JComponent} or some other common superclass
811         * of things with margins. While reflection could be used to
812         * determine the existence of this method, this would be slow on
813         * many virtual machines. Therefore, the current implementation
814         * knows about {@link javax.swing.AbstractButton#getMargin()},
815         * {@link javax.swing.JPopupMenu#getMargin()}, {@link
816         * javax.swing.JToolBar#getMargin()}, and {@link
817         * javax.swing.text.JTextComponent}. If <code>c</code> is an
818         * instance of a known class, the respective
819         * <code>getMargin()</code> method is called to determine the
820         * correct margin. Otherwise, a zero-width margin is returned.
821         *
822         * @param c the component whose border is to be measured.
823         *
824         * @return the same object that was passed for <code>insets</code>,
825         *         but with changed fields.
826         */
827        public Insets getBorderInsets(Component c, Insets insets)
828        {
829          Insets margin = null;
830    
831          /* This is terrible object-oriented design. See the above Javadoc
832           * for an excuse.
833           */
834          if (c instanceof AbstractButton)
835            margin = ((AbstractButton) c).getMargin();
836          else if (c instanceof JPopupMenu)
837            margin = ((JPopupMenu) c).getMargin();
838          else if (c instanceof JToolBar)
839            margin = ((JToolBar) c).getMargin();
840          else if (c instanceof JTextComponent)
841            margin = ((JTextComponent) c).getMargin();
842          
843          if (margin == null)
844            insets.top = insets.left = insets.bottom = insets.right = 0;
845          else
846          {
847            insets.top = margin.top;
848            insets.left = margin.left;
849            insets.bottom = margin.bottom;
850            insets.right = margin.right;
851          }
852    
853          return insets;
854        }
855      }
856      
857    
858      /**
859       * A border for drawing a separator line below JMenuBar.
860       *
861       * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
862       * height="140" alt="[A screen shot of a JMenuBar with this border]" />
863       *
864       * @author Sascha Brawer (brawer@dandelis.ch)
865       */
866      public static class MenuBarBorder
867        extends AbstractBorder
868        implements UIResource
869      {
870        /**
871         * Determined using the <code>serialver</code> tool
872         * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
873         */
874        static final long serialVersionUID = -6909056571935227506L;
875        
876        
877        /**
878         * The shadow color, which is used for the upper line of the
879         * two-pixel thick bottom edge.
880         */
881        private Color shadow;
882    
883    
884        /**
885         * The highlight color, which is used for the lower line of the
886         * two-pixel thick bottom edge.
887         */
888        private Color highlight;
889    
890    
891        /**
892         * Constructs a new MenuBarBorder for drawing a JMenuBar in
893         * the Basic look and feel.
894         *
895         * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
896         * height="140" alt="[A screen shot of a JMenuBar with this
897         * border]" />
898         *
899         * @param shadow the shadow color, which is used for the upper
900         *        line of the two-pixel thick bottom edge.
901         *
902         * @param highlight the shadow color, which is used for the lower
903         *        line of the two-pixel thick bottom edge.
904         */
905        public MenuBarBorder(Color shadow, Color highlight)
906        {
907          /* These colors usually come from the UIDefaults of the current
908           * look and feel. Use fallback values if the colors are not
909           * supplied.  The API specification is silent about what
910           * behavior is expected for null colors, so users should not
911           * rely on this fallback (which is why it is not documented in
912           * the above Javadoc).
913           */
914          this.shadow = (shadow != null) ? shadow : Color.gray;
915          this.highlight = (highlight != null) ? highlight : Color.white;
916        }
917    
918    
919        /**
920         * Paints the MenuBarBorder around a given component.
921         *
922         * @param c the component whose border is to be painted, usually
923         *        an instance of {@link javax.swing.JMenuBar}.
924         *
925         * @param g the graphics for painting.
926         * @param x the horizontal position for painting the border.
927         * @param y the vertical position for painting the border.
928         * @param width the width of the available area for painting the border.
929         * @param height the height of the available area for painting the border.
930         */
931        public void paintBorder(Component c, Graphics  g,
932                                int x, int y, int width, int height)
933        {
934          Color oldColor;
935    
936          /* To understand this code, it might be helpful to look at the
937           * image "BasicBorders.MenuBarBorder-1.png" that is included
938           * with the JavaDoc. It is located in the "doc-files"
939           * subdirectory.
940           */
941          oldColor = g.getColor();
942          y = y + height - 2;
943          try
944          {
945            g.setColor(shadow);
946            g.drawLine(x, y, x + width - 2, y);
947            g.drawLine(x, y + 1, x, y + 1);
948            g.drawLine(x + width - 2, y + 1, x + width - 2, y + 1);
949    
950            g.setColor(highlight);
951            g.drawLine(x + 1, y + 1, x + width - 3, y + 1);
952            g.drawLine(x + width - 1, y, x + width - 1, y + 1);        
953          }
954          finally
955          {
956            g.setColor(oldColor);
957          }
958        }
959    
960    
961        /**
962         * Measures the width of this border.
963         *
964         * @param c the component whose border is to be measured.
965         *
966         * @return an Insets object whose <code>left</code>,
967         *         <code>right</code>, <code>top</code> and
968         *         <code>bottom</code> fields indicate the width of the
969         *         border at the respective edge.
970         *
971         * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
972         */
973        public Insets getBorderInsets(Component c)
974        {
975          /* There is no obvious reason for overriding this method, but we
976           * try to have exactly the same API as the Sun reference
977           * implementation.
978           */
979          return getBorderInsets(c, null);
980        }
981    
982    
983        /**
984         * Measures the width of this border, storing the results into a
985         * pre-existing Insets object.
986         *
987         * @param insets an Insets object for holding the result values.
988         *        After invoking this method, the <code>left</code>,
989         *        <code>right</code>, <code>top</code> and
990         *        <code>bottom</code> fields indicate the width of the
991         *        border at the respective edge.
992         *
993         * @return the same object that was passed for <code>insets</code>.
994         *
995         * @see #getBorderInsets(Component)
996         */
997        public Insets getBorderInsets(Component c, Insets insets)
998        {
999          /* The exact amount has been determined using a test program
1000           * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
1001           * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [0,0,2,0],
1002           * which was expected from looking at the screen shot.
1003           */
1004          if (insets == null)
1005            return new Insets(0, 0, 2, 0);
1006    
1007          insets.left = insets.right = insets.top = 0;
1008          insets.bottom = 2;
1009          return insets;
1010        }
1011      }
1012    
1013    
1014      /**
1015       * A border for drawing radio buttons in the Basic look and feel.
1016       *
1017       * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
1018       * height="135" alt="[A screen shot of this border]" />
1019       *
1020       * <p>Note about the screen shot: Normally, the
1021       * <code>borderPainted</code> property is <code>false</code> for
1022       * JRadioButtons. For this screen shot, it has been set to
1023       * <code>true</code> so the borders get drawn. Also, a
1024       * concretization of the Basic look and would typically provide
1025       * icons for the various states of radio buttons.
1026       *
1027       * <p>Note that the focus rectangle is invisible If the radio button
1028       * is currently selected. While it might be debatable whether this
1029       * makes a lot of sense, this behavior can be observed in the Sun
1030       * reference implementation (in JDK 1.3.1 and 1.4.1). The Classpath
1031       * implementation tries to exactly replicate the JDK appearance.
1032       *
1033       * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
1034       *
1035       * @author Sascha Brawer (brawer@dandelis.ch)
1036       */
1037      public static class RadioButtonBorder
1038        extends ButtonBorder
1039      {
1040        /**
1041         * Determined using the <code>serialver</code> tool
1042         * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
1043         */
1044        static final long serialVersionUID = 1596945751743747369L;
1045    
1046    
1047        /**
1048         * Constructs a new border for drawing a JRadioButton in
1049         * the Basic look and feel.
1050         *
1051         * @param shadow the shadow color.
1052         * @param darkShadow a darker variant of the shadow color.
1053         * @param highlight the highlight color.
1054         * @param lightHighlight a brighter variant of the highlight  color.
1055         */
1056        public RadioButtonBorder(Color shadow, Color darkShadow,
1057                                 Color highlight, Color lightHighlight)
1058        {
1059          /* The superclass ButtonBorder substitutes null arguments
1060           * with fallback colors.
1061           */
1062          super(shadow, darkShadow, highlight, lightHighlight);
1063        }
1064    
1065    
1066        /**
1067         * Paints the RadioButtonBorder around a given component.
1068         *
1069         * <p>The Sun implementation always seems to draw exactly
1070         * the same border, irrespective of the state of the button.
1071         * This is rather surprising, but GNU Classpath emulates the
1072         * observable behavior.
1073         *
1074         * @param c the component whose border is to be painted.
1075         * @param g the graphics for painting.
1076         * @param x the horizontal position for painting the border.
1077         * @param y the vertical position for painting the border.
1078         * @param width the width of the available area for painting the border.
1079         * @param height the height of the available area for painting the border.
1080         *
1081         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
1082         */
1083        public void paintBorder(Component c, Graphics  g,
1084                                int x, int y, int width, int height)
1085        {
1086          AbstractButton button = null;
1087          ButtonModel bmodel = null;
1088          boolean lowered = false;
1089          boolean focused = false;
1090    
1091          if (c instanceof AbstractButton)
1092          {
1093            button = (AbstractButton) c;
1094            bmodel = button.getModel();
1095          }
1096    
1097          if (bmodel != null)
1098          {
1099            lowered = button.isSelected()
1100              || (/* mouse inside */ bmodel.isArmed() && bmodel.isPressed());
1101            focused = button.hasFocus() && button.isFocusPainted();        
1102          }
1103    
1104          if (lowered)
1105            BasicGraphicsUtils.drawLoweredBezel(g, x, y, width, height,
1106                                                shadow, darkShadow,
1107                                                highlight, lightHighlight);
1108          else
1109            BasicGraphicsUtils.drawBezel(g, x, y, width, height,
1110                                         /* isPressed */ false,
1111                                         /* isPefault */ focused,
1112                                         shadow, darkShadow,
1113                                         highlight, lightHighlight);
1114        }
1115        
1116        
1117        /**
1118         * Measures the width of this border.
1119         *
1120         * @param c the component whose border is to be measured.
1121         *
1122         * @return an Insets object whose <code>left</code>,
1123         *         <code>right</code>, <code>top</code> and
1124         *         <code>bottom</code> fields indicate the width of the
1125         *         border at the respective edge.
1126         *
1127         * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 
1128         */
1129        public Insets getBorderInsets(Component c)
1130        {
1131          /* There is no obvious reason for overriding this method, but we
1132           * try to have exactly the same API as the Sun reference
1133           * implementation.
1134           */
1135          return getBorderInsets(c, null);
1136        }
1137    
1138        
1139        /**
1140         * Measures the width of this border, storing the results into a
1141         * pre-existing Insets object.
1142         *
1143         * @param insets an Insets object for holding the result values.
1144         *        After invoking this method, the <code>left</code>,
1145         *        <code>right</code>, <code>top</code> and
1146         *        <code>bottom</code> fields indicate the width of the
1147         *        border at the respective edge.
1148         *
1149         * @return the same object that was passed for <code>insets</code>.
1150         *
1151         * @see #getBorderInsets(Component)
1152         */
1153        public Insets getBorderInsets(Component c, Insets insets)
1154        {
1155          /* The exact amount has been determined using a test program
1156           * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
1157           * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
1158           */
1159          if (insets == null)
1160            return new Insets(2, 2, 2, 2);
1161    
1162          insets.left = insets.right = insets.top = insets.bottom = 2;
1163          return insets;
1164        }
1165      }
1166    
1167    
1168      /**
1169       * A one-pixel thick border for rollover buttons, for example in
1170       * tool bars.
1171       *
1172       * @since 1.4
1173       * @author Sascha Brawer (brawer@dandelis.ch)
1174       */
1175      public static class RolloverButtonBorder
1176        extends ButtonBorder
1177      {
1178        /**
1179         * Determined using the <code>serialver</code> tool
1180         * of Sun JDK 1.4.1_01 on GNU/Linux 2.4.20 for x86.
1181         */
1182        static final long serialVersionUID = 1976364864896996846L;
1183    
1184    
1185        /**
1186         * Constructs a new border for drawing a roll-over button
1187         * in the Basic look and feel.
1188         *
1189         * @param shadow the shadow color.
1190         * @param darkShadow a darker variant of the shadow color.
1191         * @param highlight the highlight color.
1192         * @param lightHighlight a brighter variant of the highlight  color.
1193         */
1194        public RolloverButtonBorder(Color shadow, Color darkShadow,
1195                                    Color highlight, Color lightHighlight)
1196        {
1197          super(shadow, darkShadow, highlight, lightHighlight);
1198        }
1199    
1200    
1201        /**
1202         * Paints the border around a rollover button.  If <code>c</code>
1203         * is not an {@link javax.swing.AbstractButton} whose model
1204         * returns <code>true</code> for {@link
1205         * javax.swing.ButtonModel#isRollover}, nothing gets painted at
1206         * all.
1207         *
1208         * @param c the button whose border is to be painted.
1209         * @param g the graphics for painting.
1210         * @param x the horizontal position for painting the border.
1211         * @param y the vertical position for painting the border.
1212         * @param width the width of the available area for painting the border.
1213         * @param height the height of the available area for painting the border.
1214         */
1215        public void paintBorder(Component c, Graphics  g,
1216                                int x, int y, int width, int height)
1217        {
1218          ButtonModel bmodel = null;
1219          boolean drawPressed;
1220          Color oldColor = g.getColor();
1221          int x2, y2;
1222    
1223          if (c instanceof AbstractButton)
1224            bmodel = ((AbstractButton) c).getModel();
1225    
1226          /* Draw nothing if c is not a rollover button. */
1227          if ((bmodel == null) || !bmodel.isRollover())
1228            return;
1229    
1230          /* Draw nothing if the mouse is pressed, but outside the button. */
1231          if (bmodel.isPressed() && !bmodel.isArmed())
1232            return;
1233    
1234          drawPressed = bmodel.isSelected() || bmodel.isPressed();
1235          x2 = x + width - 1;
1236          y2 = y + height - 1;
1237    
1238          try
1239          {
1240            g.setColor(drawPressed ? shadow : lightHighlight);
1241            g.drawLine(x, y, x2 - 1, y);     // top edge
1242            g.drawLine(x, y + 1, x, y2 - 1); // left edge
1243    
1244            g.setColor(drawPressed ? lightHighlight : shadow);
1245            g.drawLine(x, y2, x2, y2);       // bottom edge
1246            g.drawLine(x2, y, x2, y2 - 1);   // right edge
1247          }
1248          finally
1249          {
1250            g.setColor(oldColor);
1251          }
1252        }
1253      }
1254    
1255    
1256      /**
1257       * A border for JSplitPanes in the Basic look and feel. The divider
1258       * in the middle of the JSplitPane has its own border class, of which
1259       * an instance can be obtained with {@link #getSplitPaneDividerBorder()}.
1260       *
1261       * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
1262       * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
1263       *
1264       * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
1265       * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
1266       *
1267       * <p>In contrast to the other borders of the Basic look and feel,
1268       * this class is not serializable. While this might be unintended,
1269       * GNU Classpath follows the specification in order to be fully
1270       * compatible with the Sun reference implementation.
1271       *
1272       * <p>In the Sun JDK, the bottom edge of the divider also gets
1273       * painted if the orientation of the enclosed JSplitPane is
1274       * <code>JSplitPane.VERTICAL_SPLIT</code> (at least in versions
1275       * 1.3.1 and 1.4.1).  GNU Classpath does not replicate this bug. A
1276       * report has been filed with Sun (bug ID 4885629).
1277       *
1278       * <p>Note that the bottom left pixel of the border has a different
1279       * color depending on the orientation of the enclosed JSplitPane.
1280       * Although this is visually inconsistent, Classpath replicates the
1281       * appearance of the Sun reference implementation. A bug report has
1282       * been filed with Sun (review ID 188774).
1283       *
1284       * @see #getSplitPaneBorder()
1285       * @see #getSplitPaneDividerBorder()
1286       *
1287       * @author Sascha Brawer (brawer@dandelis.ch)
1288       */
1289      public static class SplitPaneBorder implements Border, UIResource
1290      {
1291        /**
1292         * Indicates that the top edge shall be not be painted
1293         * by {@link #paintRect}.
1294         */
1295        private static final int SUPPRESS_TOP = 1;
1296    
1297    
1298        /**
1299         * Indicates that the left edge shall be not be painted
1300         * by {@link #paintRect}.
1301         */
1302        private static final int SUPPRESS_LEFT = 2;
1303    
1304    
1305        /**
1306         * Indicates that the bottom edge shall be not be painted
1307         * by {@link #paintRect}.
1308         */
1309        private static final int SUPPRESS_BOTTOM = 4;
1310    
1311    
1312        /**
1313         * Indicates that the right edge shall be not be painted
1314         * by {@link #paintRect}.
1315         */
1316        private static final int SUPPRESS_RIGHT = 8;
1317    
1318    
1319        /**
1320         * The color for drawing the bottom and right edges of the border.
1321         */
1322        protected Color highlight;
1323    
1324    
1325        /**
1326         * The color for drawing the top and left edges of the border.
1327         */
1328        protected Color shadow;
1329    
1330    
1331        /**
1332         * Constructs a new border for drawing a JSplitPane in the Basic
1333         * look and feel.  The divider in the middle of the JSplitPane has
1334         * its own border class, <code>SplitPaneDividerBorder</code>.
1335         *
1336         * @param shadow the shadow color.
1337         * @param highlight the highlight color.
1338         */
1339        public SplitPaneBorder(Color highlight, Color shadow)
1340        {
1341          /* These colors usually come from the UIDefaults of the current
1342           * look and feel. Use fallback values if the colors are not
1343           * supplied.  The API specification is silent about what
1344           * behavior is expected for null colors, so users should not
1345           * rely on this fallback (which is why it is not documented in
1346           * the above Javadoc).
1347           */
1348          this.shadow = (shadow != null) ? shadow : Color.black;
1349          this.highlight = (highlight != null) ? highlight : Color.white;
1350        }
1351    
1352    
1353        /**
1354         * Paints the border around a <code>JSplitPane</code>.
1355         *
1356         * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
1357         * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
1358         *
1359         * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
1360         * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
1361         *
1362         * @param c the <code>JSplitPane</code> whose border is to be painted.
1363         * @param g the graphics for painting.
1364         * @param x the horizontal position for painting the border.
1365         * @param y the vertical position for painting the border.
1366         * @param width the width of the available area for painting the border.
1367         * @param height the height of the available area for painting the border.
1368         */
1369        public void paintBorder(Component c, Graphics  g,
1370                                int x, int y, int width, int height)
1371        {
1372          JSplitPane splitPane;
1373          Component content;
1374    
1375          if (!(c instanceof JSplitPane))
1376            return;
1377    
1378          splitPane = (JSplitPane) c;
1379          switch (splitPane.getOrientation())
1380          {
1381          case JSplitPane.HORIZONTAL_SPLIT:
1382            if ((content = splitPane.getLeftComponent()) != null)
1383              paintRect(g, SUPPRESS_RIGHT, true, x, y, content.getBounds());
1384            if ((content = splitPane.getRightComponent()) != null)
1385              paintRect(g, SUPPRESS_LEFT, true, x, y, content.getBounds());
1386            break;
1387    
1388          case JSplitPane.VERTICAL_SPLIT:
1389            if ((content = splitPane.getTopComponent()) != null)
1390              paintRect(g, SUPPRESS_BOTTOM, false, x, y, content.getBounds());
1391            if ((content = splitPane.getBottomComponent()) != null)
1392              paintRect(g, SUPPRESS_TOP, false, x, y, content.getBounds());
1393            break;
1394          }
1395        }
1396    
1397    
1398        /**
1399         * Paints a border around a child of a <code>JSplitPane</code>,
1400         * omitting some of the edges.
1401         *
1402         * @param g the graphics for painting.
1403         *
1404         * @param suppress a bit mask indicating the set of suppressed
1405         *        edges, for example <code>SUPPRESS_TOP | SUPPRESS_RIGHT</code>.
1406         *
1407         * @param x the x coordinate of the SplitPaneBorder.
1408         *
1409         * @param y the y coordinate of the SplitPaneBorder.
1410         *
1411         * @param shadeBottomLeftPixel <code>true</code> to paint the
1412         *        bottom left pixel in the shadow color,
1413         *        <code>false</code> for the highlight color. The Basic
1414         *        look and feel uses the highlight color for the bottom
1415         *        left pixel of the border of a JSplitPane whose
1416         *        orientation is VERTICAL_SPLIT, and the shadow color
1417         *        otherwise. While this might be a strange distinction,
1418         *        Classpath tries to look identical to the reference
1419         *        implementation. A bug report has been filed with Sun;
1420         *        its review ID is 188774. We currently replicate the
1421         *        Sun behavior.
1422         *
1423         * @param rect the bounds of the child of JSplitPane whose
1424         *        border is to be painted.
1425         */
1426        private void paintRect(Graphics g, int suppress,
1427                               boolean shadeBottomLeftPixel,
1428                               int x, int y,
1429                               Rectangle rect)
1430        {
1431          if (rect == null)
1432            return;
1433    
1434          /* On each edge, the border exceeds the enclosed child by one
1435           * pixel. See the image "BasicBorders.SplitPaneBorder-1.png" in
1436           * the directory "doc-files".
1437           */
1438          x += rect.x - 1;
1439          y += rect.y - 1;
1440          int right = x + rect.width + 1;
1441          int bottom = y + rect.height + 1;
1442          
1443          Color oldColor = g.getColor();
1444          try
1445          {
1446            g.setColor(shadow);
1447            if ((suppress & SUPPRESS_TOP) == 0)
1448              g.drawLine(x, y, right, y);
1449            if ((suppress & SUPPRESS_LEFT) == 0)
1450              g.drawLine(x, y, x, bottom);
1451            else
1452              g.drawLine(x, bottom, x, bottom); // one pixel
1453    
1454            g.setColor(highlight);
1455            if ((suppress & SUPPRESS_BOTTOM) == 0)
1456              g.drawLine(x + (shadeBottomLeftPixel ? 1 : 0), bottom, right, bottom);
1457            else if (!shadeBottomLeftPixel)
1458              g.drawLine(x, bottom, x, bottom); // one pixel
1459    
1460            if ((suppress & SUPPRESS_RIGHT) == 0)
1461              g.drawLine(right, y, right, bottom);
1462          }
1463          finally
1464          {
1465            g.setColor(oldColor);
1466          }
1467        }
1468    
1469        
1470        /**
1471         * Measures the width of this border.
1472         *
1473         * @param c the component whose border is to be measured, usually
1474         *        an instance of {@link javax.swing.JSplitPane}.
1475         *
1476         * @return an Insets object whose <code>left</code>,
1477         *         <code>right</code>, <code>top</code> and
1478         *         <code>bottom</code> fields indicate the width of the
1479         *         border at the respective edge.
1480         */
1481        public Insets getBorderInsets(Component c)
1482        {
1483          return new Insets(1, 1, 1, 1);
1484        }
1485    
1486    
1487        /**
1488         * Determines whether this border fills every pixel in its area
1489         * when painting.
1490         *
1491         * @return <code>false</code> because this border does not
1492         *         paint over the pixels where the divider joins
1493         *         the border.
1494         */
1495        public boolean isBorderOpaque()
1496        {
1497          /* Strangely, the Sun implementation (tested with JDK 1.3.1 and
1498           * 1.4.1_01) seems to always return true. It could be a bug,
1499           * but without knowing the details of their implementation, it is
1500           * hard to decide.
1501           */
1502          return false;
1503        }
1504      }
1505    
1506    
1507      /**
1508       * A border for the divider inside a JSplitPane.
1509       *
1510       * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
1511       * width="520" height="200" alt="[A screen shot of this border]" />
1512       *
1513       * @author Sascha Brawer (brawer@dandelis.ch)
1514       */
1515      private static class SplitPaneDividerBorder
1516        implements Border, UIResource, Serializable
1517      {
1518        /**
1519         * Constructs a new border for drawing the divider of a JSplitPane
1520         * in the Basic look and feel.  The outer parts of the JSplitPane have
1521         * their own border class, <code>SplitPaneBorder</code>.
1522         */
1523        public SplitPaneDividerBorder()
1524        {
1525          // Nothing to do here.
1526        }
1527    
1528        /**
1529         * Paints the border around the divider of a <code>JSplitPane</code>.
1530         *
1531         * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
1532         * width="520" height="200" alt="[A picture that shows which pixels
1533         * get painted in what color]" />
1534         *
1535         * @param c the <code>JSplitPane</code> whose divider&#x2019;s border
1536         *        is to be painted.
1537         * @param g the graphics for painting.
1538         * @param x the horizontal position for painting the border.
1539         * @param y the vertical position for painting the border.
1540         * @param width the width of the available area for painting the border.
1541         * @param height the height of the available area for painting the border.
1542         */
1543        public void paintBorder(Component c, Graphics  g,
1544                                int x, int y, int width, int height)
1545        {
1546          Color highlight = UIManager.getColor("SplitPane.highlight");
1547          Color shadow = UIManager.getColor("SplitPane.shadow");
1548          Color oldColor, dcol;
1549          int x2, y2;
1550          JSplitPane sp;
1551    
1552          sp = getSplitPane(c);
1553          if (sp == null)
1554            return;
1555    
1556          x2 = x + width - 1;
1557          y2 = y + height - 1;
1558          oldColor = g.getColor();
1559          dcol = c.getBackground();
1560          try
1561          {
1562            switch (sp.getOrientation())
1563            {
1564            case JSplitPane.HORIZONTAL_SPLIT:
1565              g.setColor(dcol);
1566              g.drawLine(x + 1, y, x2 - 1, y);
1567              g.drawLine(x + 1, y2, x2 - 1, y2);
1568              g.setColor(sp.getLeftComponent() != null ? highlight : dcol);
1569              g.drawLine(x, y, x, y2);
1570              g.setColor(sp.getRightComponent() != null ? shadow : dcol);
1571              g.drawLine(x2, y, x2, y2);
1572              break;
1573    
1574            case JSplitPane.VERTICAL_SPLIT:
1575              g.setColor(dcol);
1576              g.drawLine(x, y + 1, x, y2 - 1);
1577              g.drawLine(x2, y + 1, x2, y2 - 1);
1578              g.setColor(sp.getTopComponent() != null ? highlight : dcol);
1579              g.drawLine(x, y, x2, y);
1580              g.setColor(sp.getBottomComponent() != null ? shadow : dcol);
1581              g.drawLine(x, y2, x2, y2);
1582              break;
1583            }
1584          }
1585          finally
1586          {
1587            g.setColor(oldColor);
1588          }
1589        }
1590    
1591    
1592        /**
1593         * Measures the width of this border.
1594         *
1595         * @param c the component whose border is to be measured, usually
1596         *        an instance of {@link javax.swing.JSplitPane}.
1597         *
1598         * @return an Insets object whose <code>left</code>,
1599         *         <code>right</code>, <code>top</code> and
1600         *         <code>bottom</code> fields indicate the width of the
1601         *         border at the respective edge.
1602         */
1603        public Insets getBorderInsets(Component c)
1604        {
1605          return new Insets(1, 1, 1, 1);
1606        }
1607    
1608        /**
1609         * Determines whether this border fills every pixel in its area
1610         * when painting.
1611         *
1612         * @return <code>true</code>
1613         */
1614        public boolean isBorderOpaque()
1615        {
1616          return true;
1617        }
1618    
1619        
1620        /**
1621         * Determines the JSplitPane whose divider is being painted.
1622         *
1623         * @param c an instance of BasicSplitPaneDivider.
1624         *
1625         * @return a <code>JSplitPane</code>, or <code>null</code> if
1626         *         <code>c</code> is not an instance of {@link
1627         *         javax.swing.plaf.basic.BasicSplitPaneDivider}.
1628         */
1629        private JSplitPane getSplitPane(Component c)
1630        {
1631          if (c instanceof BasicSplitPaneDivider)
1632            return (((BasicSplitPaneDivider) c).getBasicSplitPaneUI())
1633              .getSplitPane();
1634          else
1635            return null;
1636        }
1637      }
1638    
1639    
1640      /**
1641       * A border for toggle buttons in the Basic look and feel.
1642       *
1643       * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png"
1644       * width="270" height="135" alt="[A screen shot of this border]" />
1645       *
1646       * <p>The Sun implementation always seems to draw exactly
1647       * the same border, irrespective of the state of the button.
1648       * This is rather surprising, but GNU Classpath emulates the
1649       * observable behavior.
1650       *
1651       * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
1652       *
1653       * @author Sascha Brawer (brawer@dandelis.ch)
1654       */
1655      public static class ToggleButtonBorder
1656        extends ButtonBorder
1657      {
1658        /**
1659         * Determined using the <code>serialver</code> tool
1660         * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
1661         */
1662        static final long serialVersionUID = -3528666548001058394L;
1663    
1664        
1665        /**
1666         * Constructs a new border for drawing a JToggleButton in
1667         * the Basic look and feel.
1668         *
1669         * @param shadow the shadow color.
1670         * @param darkShadow a darker variant of the shadow color.
1671         * @param highlight the highlight color.
1672         * @param lightHighlight a brighter variant of the highlight  color.
1673         */
1674        public ToggleButtonBorder(Color shadow, Color darkShadow,
1675                                  Color highlight, Color lightHighlight)
1676        {
1677          /* The superclass ButtonBorder substitutes null arguments
1678           * with fallback colors.
1679           */
1680          super(shadow, darkShadow, highlight, lightHighlight);
1681        }
1682    
1683    
1684        /**
1685         * Paints the ToggleButtonBorder around a given component.
1686         *
1687         * <p>The Sun implementation always seems to draw exactly
1688         * the same border, irrespective of the state of the button.
1689         * This is rather surprising, but GNU Classpath emulates the
1690         * observable behavior.
1691         *
1692         * @param c the component whose border is to be painted.
1693         * @param g the graphics for painting.
1694         * @param x the horizontal position for painting the border.
1695         * @param y the vertical position for painting the border.
1696         * @param width the width of the available area for painting the border.
1697         * @param height the height of the available area for painting the border.
1698         *
1699         * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
1700         */
1701        public void paintBorder(Component c, Graphics  g,
1702                                int x, int y, int width, int height)
1703        {
1704          /* The author of this code tried various variants for setting
1705           * the state of the enclosed JToggleButton, but it seems that
1706           * the drawn border is always identical. Weird, because this
1707           * means that the user does not see whether the JToggleButton
1708           * is selected or not.
1709           */
1710          BasicGraphicsUtils.drawBezel(g, x, y, width, height,
1711                                       /* pressed */ false, 
1712                                       /* default */ false,
1713                                       shadow, darkShadow,
1714                                       highlight, lightHighlight);
1715        }
1716    
1717    
1718        /**
1719         * Measures the width of this border.
1720         *
1721         * @param c the component whose border is to be measured.
1722         *
1723         * @return an Insets object whose <code>left</code>,
1724         *         <code>right</code>, <code>top</code> and
1725         *         <code>bottom</code> fields indicate the width of the
1726         *         border at the respective edge.
1727         *
1728         * @see #getBorderInsets(java.awt.Component, java.awt.Insets) 
1729         */
1730        public Insets getBorderInsets(Component c)
1731        {
1732          /* There is no obvious reason for overriding this method, but we
1733           * try to have exactly the same API as the Sun reference
1734           * implementation.
1735           */
1736          return getBorderInsets(c, null);
1737        }
1738    
1739        
1740        /**
1741         * Measures the width of this border, storing the results into a
1742         * pre-existing Insets object.
1743         *
1744         * @param insets an Insets object for holding the result values.
1745         *        After invoking this method, the <code>left</code>,
1746         *        <code>right</code>, <code>top</code> and
1747         *        <code>bottom</code> fields indicate the width of the
1748         *        border at the respective edge.
1749         *
1750         * @return the same object that was passed for <code>insets</code>.
1751         *
1752         * @see #getBorderInsets(Component)
1753         */
1754        public Insets getBorderInsets(Component c, Insets insets)
1755        {
1756          /* The exact amount has been determined using a test program
1757           * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
1758           * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
1759           */
1760          if (insets == null)
1761            return new Insets(2, 2, 2, 2);
1762    
1763          insets.left = insets.right = insets.top = insets.bottom = 2;
1764          return insets;
1765        }
1766      }
1767    
1768    }