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&#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    }