001/* BevelBorder.java -- 
002   Copyright (C) 2003 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
038package javax.swing.border;
039
040import java.awt.Color;
041import java.awt.Component;
042import java.awt.Graphics;
043import 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 */
058public 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&#x2019;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}
585