001/* BasicBorders.java --
002   Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.plaf.basic;
040
041import java.awt.Color;
042import java.awt.Component;
043import java.awt.Graphics;
044import java.awt.Insets;
045import java.awt.Rectangle;
046import java.io.Serializable;
047
048import javax.swing.AbstractButton;
049import javax.swing.ButtonModel;
050import javax.swing.JButton;
051import javax.swing.JPopupMenu;
052import javax.swing.JSplitPane;
053import javax.swing.JToolBar;
054import javax.swing.UIManager;
055import javax.swing.border.AbstractBorder;
056import javax.swing.border.BevelBorder;
057import javax.swing.border.Border;
058import javax.swing.plaf.BorderUIResource;
059import javax.swing.plaf.UIResource;
060import 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 */
067public 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}