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>“Button.shadow”</code>,
083 * <code>“Button.darkShadow”</code>,
084 * <code>“Button.light”</code>, and
085 * <code>“Button.highlight”</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>“RadioButton.shadow”</code>,
130 * <code>“RadioButton.darkShadow”</code>,
131 * <code>“RadioButton.light”</code>, and
132 * <code>“RadioButton.highlight”</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>“ToggleButton.shadow”</code>,
179 * <code>“ToggleButton.darkShadow”</code>,
180 * <code>“ToggleButton.light”</code>, and
181 * <code>“ToggleButton.highlight”</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>“MenuBar.shadow”</code> and
229 * <code>“MenuBar.highlight”</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>“SplitPane.darkShadow”</code> and
254 * <code>“SplitPane.highlight”</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>“SplitPane.darkShadow”</code> and
283 * <code>“SplitPane.highlight”</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>“TextField.shadow”</code>,
313 * <code>“TextField.darkShadow”</code>,
314 * <code>“TextField.light”</code>, and
315 * <code>“TextField.highlight”</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>“InternalFrame.borderShadow”</code>,
365 * <code>“InternalFrame.borderDarkShadow”</code>,
366 * <code>“InternalFrame.borderLight”</code>,
367 * <code>“InternalFrame.borderHighlight”</code>, and
368 * (for the inner one-pixel thick line)
369 * <code>“InternalFrame.borderColor”</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’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 }