001 /* BevelBorder.java --
002 Copyright (C) 2003 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 package javax.swing.border;
039
040 import java.awt.Color;
041 import java.awt.Component;
042 import java.awt.Graphics;
043 import java.awt.Insets;
044
045
046 /**
047 * A rectangular, two pixel thick border that causes the enclosed area
048 * to appear as if it was raising out of or lowered into the screen. Some
049 * LookAndFeels use this kind of border for rectangular buttons.
050 *
051 * <p>A BevelBorder has a highlight and a shadow color. In the raised
052 * variant, the highlight color is used for the top and left edges,
053 * and the shadow color is used for the bottom and right edge. For an
054 * image, see the documentation of the individual constructors.
055 *
056 * @author Sascha Brawer (brawer@dandelis.ch)
057 */
058 public class BevelBorder extends AbstractBorder
059 {
060 /**
061 * Determined using the <code>serialver</code> tool
062 * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
063 */
064 static final long serialVersionUID = -1034942243356299676L;
065
066
067 /**
068 * Indicates that the BevelBorder looks like if the enclosed area was
069 * raising out of the screen.
070 */
071 public static final int RAISED = 0;
072
073
074 /**
075 * Indicates that the BevelBorder looks like if the enclosed area was
076 * pressed into the screen.
077 */
078 public static final int LOWERED = 1;
079
080
081 /**
082 * The type of this BevelBorder, which is either {@link #RAISED}
083 * or {@link #LOWERED}.
084 */
085 protected int bevelType;
086
087
088 /**
089 * The outer highlight color, or <code>null</code> to indicate that
090 * the color shall be derived from the background of the component
091 * whose border is being painted.
092 */
093 protected Color highlightOuter;
094
095
096 /**
097 * The inner highlight color, or <code>null</code> to indicate that
098 * the color shall be derived from the background of the component
099 * whose border is being painted.
100 */
101 protected Color highlightInner;
102
103
104 /**
105 * The outer shadow color, or <code>null</code> to indicate that the
106 * color shall be derived from the background of the component whose
107 * border is being painted.
108 */
109 protected Color shadowOuter;
110
111
112 /**
113 * The inner shadow color, or <code>null</code> to indicate that the
114 * color shall be derived from the background of the component whose
115 * border is being painted.
116 */
117 protected Color shadowInner;
118
119
120 /**
121 * Constructs a BevelBorder whose colors will be derived from the
122 * background of the enclosed component. The background color is
123 * retrieved each time the border is painted, so a BevelBorder
124 * constructed by this method will automatically reflect a change
125 * to the component’s background color.
126 *
127 * <p><img src="doc-files/BevelBorder-1.png" width="500" height="150"
128 * alt="[An illustration showing raised and lowered BevelBorders]" />
129 *
130 * @param bevelType the desired appearance of the border. The value
131 * must be either {@link #RAISED} or {@link #LOWERED}.
132 *
133 * @throws IllegalArgumentException if <code>bevelType</code> has
134 * an unsupported value.
135 */
136 public BevelBorder(int bevelType)
137 {
138 if ((bevelType != RAISED) && (bevelType != LOWERED))
139 throw new IllegalArgumentException();
140
141 this.bevelType = bevelType;
142 }
143
144
145 /**
146 * Constructs a BevelBorder given its appearance type and two colors
147 * for its highlight and shadow.
148 *
149 * <p><img src="doc-files/BevelBorder-2.png" width="500" height="150"
150 * alt="[An illustration showing BevelBorders that were constructed
151 * with this method]" />
152 *
153 * @param bevelType the desired appearance of the border. The value
154 * must be either {@link #RAISED} or {@link #LOWERED}.
155 *
156 * @param highlight the color that will be used for the inner
157 * side of the highlighted edges (top and left if
158 * if <code>bevelType</code> is {@link #RAISED}; bottom
159 * and right otherwise). The color for the outer side
160 * is a brightened version of this color.
161 *
162 * @param shadow the color that will be used for the outer
163 * side of the shadowed edges (bottom and right
164 * if <code>bevelType</code> is {@link #RAISED}; top
165 * and left otherwise). The color for the inner side
166 * is a brightened version of this color.
167 *
168 * @throws IllegalArgumentException if <code>bevelType</code> has
169 * an unsupported value.
170 *
171 * @throws NullPointerException if <code>highlight</code> or
172 * <code>shadow</code> is <code>null</code>.
173 *
174 * @see java.awt.Color#brighter()
175 */
176 public BevelBorder(int bevelType, Color highlight, Color shadow)
177 {
178 this(bevelType,
179 /* highlightOuter */ highlight.brighter(),
180 /* highlightInner */ highlight,
181 /* shadowOuter */ shadow,
182 /* shadowInner */ shadow.brighter());
183 }
184
185
186 /**
187 * Constructs a BevelBorder given its appearance type and all
188 * colors.
189 *
190 * <p><img src="doc-files/BevelBorder-3.png" width="500" height="150"
191 * alt="[An illustration showing BevelBorders that were constructed
192 * with this method]" />
193 *
194 * @param bevelType the desired appearance of the border. The value
195 * must be either {@link #RAISED} or {@link #LOWERED}.
196 *
197 * @param highlightOuter the color that will be used for the outer
198 * side of the highlighted edges (top and left if
199 * <code>bevelType</code> is {@link #RAISED}; bottom and
200 * right otherwise).
201 *
202 * @param highlightInner the color that will be used for the inner
203 * side of the highlighted edges.
204 *
205 * @param shadowOuter the color that will be used for the outer
206 * side of the shadowed edges (bottom and right
207 * if <code>bevelType</code> is {@link #RAISED}; top
208 * and left otherwise).
209 *
210 * @param shadowInner the color that will be used for the inner
211 * side of the shadowed edges.
212 *
213 * @throws IllegalArgumentException if <code>bevelType</code> has
214 * an unsupported value.
215 *
216 * @throws NullPointerException if one of the passed colors
217 * is <code>null</code>.
218 */
219 public BevelBorder(int bevelType,
220 Color highlightOuter, Color highlightInner,
221 Color shadowOuter, Color shadowInner)
222 {
223 this(bevelType); // checks the validity of bevelType
224
225 if ((highlightOuter == null) || (highlightInner == null)
226 || (shadowOuter == null) || (shadowInner == null))
227 throw new NullPointerException();
228
229 this.highlightOuter = highlightOuter;
230 this.highlightInner = highlightInner;
231 this.shadowOuter = shadowOuter;
232 this.shadowInner = shadowInner;
233 }
234
235
236 /**
237 * Paints the border for a given component.
238 *
239 * @param c the component whose border is to be painted.
240 * @param g the graphics for painting.
241 * @param x the horizontal position for painting the border.
242 * @param y the vertical position for painting the border.
243 * @param width the width of the available area for painting the border.
244 * @param height the height of the available area for painting the border.
245 */
246 public void paintBorder(Component c, Graphics g,
247 int x, int y, int width, int height)
248 {
249 switch (bevelType)
250 {
251 case RAISED:
252 paintRaisedBevel(c, g, x, y, width, height);
253 break;
254
255 case LOWERED:
256 paintLoweredBevel(c, g, x, y, width, height);
257 break;
258 }
259 }
260
261
262 /**
263 * Measures the width of this border.
264 *
265 * @param c the component whose border is to be measured.
266 *
267 * @return an Insets object whose <code>left</code>, <code>right</code>,
268 * <code>top</code> and <code>bottom</code> fields indicate the
269 * width of the border at the respective edge.
270 *
271 * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
272 */
273 public Insets getBorderInsets(Component c)
274 {
275 return new Insets(2, 2, 2, 2);
276 }
277
278
279 /**
280 * Measures the width of this border, storing the results into a
281 * pre-existing Insets object.
282 *
283 * @param insets an Insets object for holding the result values.
284 * After invoking this method, the <code>left</code>,
285 * <code>right</code>, <code>top</code> and
286 * <code>bottom</code> fields indicate the width of the
287 * border at the respective edge.
288 *
289 * @return the same object that was passed for <code>insets</code>.
290 *
291 * @see #getBorderInsets(Component)
292 */
293 public Insets getBorderInsets(Component c, Insets insets)
294 {
295 insets.left = insets.right = insets.top = insets.bottom = 2;
296 return insets;
297 }
298
299
300 /**
301 * Determines the color that will be used for the outer side of
302 * highlighted edges when painting the border. If a highlight color
303 * has been specified upon constructing the border, that color is
304 * returned. Otherwise, the inner highlight color is brightened.
305 *
306 * @param c the component enclosed by this border.
307 *
308 * @return The color.
309 *
310 * @see #getHighlightInnerColor(java.awt.Component)
311 * @see java.awt.Color#brighter()
312 */
313 public Color getHighlightOuterColor(Component c)
314 {
315 if (highlightOuter != null)
316 return highlightOuter;
317 else
318 return getHighlightInnerColor(c).brighter();
319 }
320
321
322 /**
323 * Determines the color that will be used for the inner side of
324 * highlighted edges when painting the border. If a highlight color
325 * has been specified upon constructing the border, that color is
326 * returned. Otherwise, the background color of the enclosed
327 * component is brightened.
328 *
329 * @param c the component enclosed by this border.
330 *
331 * @return The color.
332 *
333 * @see java.awt.Component#getBackground()
334 * @see java.awt.Color#brighter()
335 */
336 public Color getHighlightInnerColor(Component c)
337 {
338 if (highlightInner != null)
339 return highlightInner;
340 else
341 return c.getBackground().brighter();
342 }
343
344
345 /**
346 * Determines the color that will be used for the inner side of
347 * shadowed edges when painting the border. If a shadow color has
348 * been specified upon constructing the border, that color is
349 * returned. Otherwise, the background color of the enclosed
350 * component is darkened.
351 *
352 * @param c the component enclosed by this border.
353 *
354 * @return The color.
355 *
356 * @see java.awt.Component#getBackground()
357 * @see java.awt.Color#darker()
358 */
359 public Color getShadowInnerColor(Component c)
360 {
361 if (shadowInner != null)
362 return shadowInner;
363 else
364 return c.getBackground().darker();
365 }
366
367
368 /**
369 * Determines the color that will be used for the outer side of
370 * shadowed edges when painting the border. If a shadow color
371 * has been specified upon constructing the border, that color is
372 * returned. Otherwise, the inner shadow color is darkened.
373 *
374 * @param c the component enclosed by this border.
375 *
376 * @return The color.
377 *
378 * @see #getShadowInnerColor(java.awt.Component)
379 * @see java.awt.Color#darker()
380 */
381 public Color getShadowOuterColor(Component c)
382 {
383 if (shadowOuter != null)
384 return shadowOuter;
385 else
386 return getShadowInnerColor(c).darker();
387 }
388
389
390 /**
391 * Returns the color that will be used for the outer side of
392 * highlighted edges when painting the border, or <code>null</code>
393 * if that color will be derived from the background of the enclosed
394 * Component.
395 *
396 * @return The color (possibly <code>null</code>).
397 */
398 public Color getHighlightOuterColor()
399 {
400 return highlightOuter;
401 }
402
403
404 /**
405 * Returns the color that will be used for the inner side of
406 * highlighted edges when painting the border, or <code>null</code>
407 * if that color will be derived from the background of the enclosed
408 * Component.
409 *
410 * @return The color (possibly <code>null</code>).
411 */
412 public Color getHighlightInnerColor()
413 {
414 return highlightInner;
415 }
416
417
418 /**
419 * Returns the color that will be used for the inner side of
420 * shadowed edges when painting the border, or <code>null</code> if
421 * that color will be derived from the background of the enclosed
422 * Component.
423 *
424 * @return The color (possibly <code>null</code>).
425 */
426 public Color getShadowInnerColor()
427 {
428 return shadowInner;
429 }
430
431
432 /**
433 * Returns the color that will be used for the outer side of
434 * shadowed edges when painting the border, or <code>null</code> if
435 * that color will be derived from the background of the enclosed
436 * Component.
437 *
438 * @return The color (possibly <code>null</code>).
439 */
440 public Color getShadowOuterColor()
441 {
442 return shadowOuter;
443 }
444
445
446 /**
447 * Returns the appearance of this border, which is either {@link
448 * #RAISED} or {@link #LOWERED}.
449 *
450 * @return The bevel type ({@link #RAISED} or {@link #LOWERED}).
451 */
452 public int getBevelType()
453 {
454 return bevelType;
455 }
456
457
458 /**
459 * Determines whether this border fills every pixel in its area
460 * when painting.
461 *
462 * <p>If the border colors are derived from the background color of
463 * the enclosed component, the result is <code>true</code> because
464 * the derivation method always returns opaque colors. Otherwise,
465 * the result depends on the opacity of the individual colors.
466 *
467 * @return <code>true</code> if the border is fully opaque, or
468 * <code>false</code> if some pixels of the background
469 * can shine through the border.
470 */
471 public boolean isBorderOpaque()
472 {
473 /* If the colors are to be drived from the enclosed Component's
474 * background color, the border is guaranteed to be fully opaque
475 * because Color.brighten() and Color.darken() always return an
476 * opaque color.
477 */
478 return
479 ((highlightOuter == null) || (highlightOuter.getAlpha() == 255))
480 && ((highlightInner == null) || (highlightInner.getAlpha() == 255))
481 && ((shadowInner == null) || (shadowInner.getAlpha() == 255))
482 && ((shadowOuter == null) || (shadowOuter.getAlpha() == 255));
483 }
484
485
486 /**
487 * Paints a raised bevel border around a component.
488 *
489 * @param c the component whose border is to be painted.
490 * @param g the graphics for painting.
491 * @param x the horizontal position for painting the border.
492 * @param y the vertical position for painting the border.
493 * @param width the width of the available area for painting the border.
494 * @param height the height of the available area for painting the border.
495 */
496 protected void paintRaisedBevel(Component c, Graphics g,
497 int x, int y, int width, int height)
498 {
499 paintBevel(g, x, y, width, height,
500 getHighlightOuterColor(c), getHighlightInnerColor(c),
501 getShadowInnerColor(c), getShadowOuterColor(c));
502 }
503
504
505 /**
506 * Paints a lowered bevel border around a component.
507 *
508 * @param c the component whose border is to be painted.
509 * @param g the graphics for painting.
510 * @param x the horizontal position for painting the border.
511 * @param y the vertical position for painting the border.
512 * @param width the width of the available area for painting the border.
513 * @param height the height of the available area for painting the border.
514 */
515 protected void paintLoweredBevel(Component c, Graphics g,
516 int x, int y, int width, int height)
517 {
518 paintBevel(g, x, y, width, height,
519 getShadowInnerColor(c), getShadowOuterColor(c),
520 getHighlightInnerColor(c), getHighlightOuterColor(c));
521 }
522
523
524 /**
525 * Paints a two-pixel bevel in four colors.
526 *
527 * <pre>
528 * ++++++++++++
529 * +..........# + = color a
530 * +. X# . = color b
531 * +. X# X = color c
532 * +.XXXXXXXXX# # = color d
533 * ############</pre>
534 *
535 * @param g the graphics for painting.
536 * @param x the horizontal position for painting the border.
537 * @param y the vertical position for painting the border.
538 * @param width the width of the available area for painting the border.
539 * @param height the height of the available area for painting the border.
540 * @param a the color for the outer side of the top and left edges.
541 * @param b the color for the inner side of the top and left edges.
542 * @param c the color for the inner side of the bottom and right edges.
543 * @param d the color for the outer side of the bottom and right edges.
544 */
545 private static void paintBevel(Graphics g,
546 int x, int y, int width, int height,
547 Color a, Color b, Color c, Color d)
548 {
549 Color oldColor;
550
551 oldColor = g.getColor();
552 g.translate(x, y);
553 width = width - 1;
554 height = height - 1;
555
556 try
557 {
558 /* To understand this code, it might be helpful to look at the
559 * images that are included with the JavaDoc. They are located
560 * in the "doc-files" subdirectory.
561 */
562 g.setColor(a);
563 g.drawLine(0, 0, width, 0); // a, horizontal
564 g.drawLine(0, 1, 0, height); // a, vertical
565
566 g.setColor(b);
567 g.drawLine(1, 1, width - 1, 1); // b, horizontal
568 g.drawLine(1, 2, 1, height - 1); // b, vertical
569
570 g.setColor(c);
571 g.drawLine(2, height - 1, width - 1, height - 1); // c, horizontal
572 g.drawLine(width - 1, 2, width - 1, height - 2); // c, vertical
573
574 g.setColor(d);
575 g.drawLine(1, height, width, height); // d, horizontal
576 g.drawLine(width, 1, width, height - 1); // d, vertical
577 }
578 finally
579 {
580 g.translate(-x, -y);
581 g.setColor(oldColor);
582 }
583 }
584 }