/[classpath]/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
ViewVC logotype

Diff of /classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by egagnon, Sun Aug 11 20:08:43 2002 UTC revision 1.4 by brawer, Wed Jun 18 12:24:33 2003 UTC
# Line 1  Line 1 
1  /* BasicGraphicsUtils.java  /* BasicGraphicsUtils.java
2     Copyright (C) 2002 Free Software Foundation, Inc.     Copyright (C) 2003 Free Software Foundation, Inc.
3    
4  This file is part of GNU Classpath.  This file is part of GNU Classpath.
5    
# Line 35  this exception to your version of the li Line 35  this exception to your version of the li
35  obligated to do so.  If you do not wish to do so, delete this  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */  exception statement from your version. */
37    
   
38  package javax.swing.plaf.basic;  package javax.swing.plaf.basic;
39    
40    import java.awt.Color;
41    import java.awt.Dimension;
42    import java.awt.Font;
43    import java.awt.FontMetrics;
44    import java.awt.Graphics;
45    import java.awt.Graphics2D;
46    import java.awt.Insets;
47    import java.awt.Rectangle;
48    
49    import java.awt.font.FontRenderContext;
50    import java.awt.font.LineMetrics;
51    import java.awt.font.TextLayout;
52    
53    import java.awt.geom.Rectangle2D;
54    
55    import javax.swing.AbstractButton;
56    import javax.swing.SwingUtilities;
57    
58    
59    /**
60     * A utility class providing commonly used drawing and measurement
61     * routines.
62     *
63     * @author Sascha Brawer (brawer@dandelis.ch)
64     */
65    public class BasicGraphicsUtils
66    {
67      /**
68       * Constructor. It is utterly unclear why this class should
69       * be constructable, but this is what the API specification
70       * says.
71       */
72      public BasicGraphicsUtils()
73      {
74      }
75    
76    
77      /**
78       * Draws a rectangle that appears etched into the surface, given
79       * four colors that are used for drawing.
80       *
81       * <p><img src="BasicGraphicsUtils-1.png" width="360"
82       * height="200" alt="[An illustration that shows which pixels
83       * get painted in what color]" />
84       *
85       * @param g the graphics into which the rectangle is drawn.
86       * @param x the x coordinate of the rectangle.
87       * @param y the y coordinate of the rectangle.
88       * @param width the width of the rectangle in pixels.
89       * @param height the height of the rectangle in pixels.
90       *
91       * @param shadow the color that will be used for painting
92       *        the outer side of the top and left edges.
93       *
94       * @param darkShadow the color that will be used for painting
95       *        the inner side of the top and left edges.
96       *
97       * @param highlight the color that will be used for painting
98       *        the inner side of the bottom and right edges.
99       *
100       * @param lightHighlight the color that will be used for painting
101       *        the outer side of the bottom and right edges.
102       *
103       * @see #getEtchedInsets()
104       * @see javax.swing.border.EtchedBorder
105       */
106      public static void drawEtchedRect(Graphics g,
107                                        int x, int y, int width, int height,
108                                        Color shadow, Color darkShadow,
109                                        Color highlight, Color lightHighlight)
110      {
111        Color oldColor;
112        int x2, y2;
113    
114        oldColor = g.getColor();
115        x2 = x + width - 1;
116        y2 = y + height - 1;
117    
118  import java.awt.*;      try
119  import javax.swing.*;      {
120          /* To understand this code, it might be helpful to look at the
121           * image "BasicGraphicsUtils-1.png" that is included with the
122           * JavaDoc. The file is located in the "doc-files" subdirectory.
123           *
124           * (x2, y2) is the coordinate of the most right and bottom pixel
125           * to be painted.
126           */
127          g.setColor(shadow);
128          g.drawLine(x, y, x2 - 1, y);                     // top, outer
129          g.drawLine(x, y + 1, x, y2 - 1);                 // left, outer
130    
131          g.setColor(darkShadow);
132          g.drawLine(x + 1, y + 1, x2 - 2, y + 1);         // top, inner
133          g.drawLine(x + 1, y + 2, x + 1, y2 - 2);         // left, inner
134          
135          g.setColor(highlight);
136          g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1);       // bottom, inner
137          g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2);       // right, inner
138    
139          g.setColor(lightHighlight);
140          g.drawLine(x, y2, x2, y2);                       // bottom, outer
141          g.drawLine(x2, y, x2, y2 - 1);                   // right, outer
142        }
143        finally
144        {
145          g.setColor(oldColor);
146        }
147      }
148      
149      
150      /**
151       * Determines the width of the border that gets painted by
152       * {@link #drawEtchedRect}.
153       *
154       * @return an <code>Insets</code> object whose <code>top</code>,
155       *         <code>left</code>, <code>bottom</code> and
156       *         <code>right</code> field contain the border width at the
157       *         respective edge in pixels.
158       */
159      public static Insets getEtchedInsets()
160      {
161        return new Insets(2, 2, 2, 2);
162      }
163    
164    
165      /**
166       * Draws a rectangle that appears etched into the surface, given
167       * two colors that are used for drawing.
168       *
169       * <p><img src="BasicGraphicsUtils-2.png" width="360"
170       * height="200" alt="[An illustration that shows which pixels
171       * get painted in what color]" />
172       *
173       * @param g the graphics into which the rectangle is drawn.
174       * @param x the x coordinate of the rectangle.
175       * @param y the y coordinate of the rectangle.
176       * @param width the width of the rectangle in pixels.
177       * @param height the height of the rectangle in pixels.
178       *
179       * @param shadow the color that will be used for painting the outer
180       *        side of the top and left edges, and for the inner side of
181       *        the bottom and right ones.
182       *
183       * @param highlight the color that will be used for painting the
184       *        inner side of the top and left edges, and for the outer
185       *        side of the bottom and right ones.
186       *
187       * @see #getGrooveInsets()
188       * @see javax.swing.border.EtchedBorder
189       */
190      public static void drawGroove(Graphics g,
191                                    int x, int y, int width, int height,
192                                    Color shadow, Color highlight)
193      {
194        /* To understand this, it might be helpful to look at the image
195         * "BasicGraphicsUtils-2.png" that is included with the JavaDoc,
196         * and to compare it with "BasicGraphicsUtils-1.png" which shows
197         * the pixels painted by drawEtchedRect.  These image files are
198         * located in the "doc-files" subdirectory.
199         */
200        drawEtchedRect(g, x, y, width, height,
201                       /* outer topLeft */     shadow,
202                       /* inner topLeft */     highlight,
203                       /* inner bottomRight */ shadow,
204                       /* outer bottomRight */ highlight);
205      }
206    
207    
208      /**
209       * Determines the width of the border that gets painted by
210       * {@link #drawGroove}.
211       *
212       * @return an <code>Insets</code> object whose <code>top</code>,
213       *         <code>left</code>, <code>bottom</code> and
214       *         <code>right</code> field contain the border width at the
215       *         respective edge in pixels.
216       */
217      public static Insets getGrooveInsets()
218      {
219        return new Insets(2, 2, 2, 2);
220      }
221      
222    
223      /**
224       * Draws a border that is suitable for buttons of the Basic look and
225       * feel.
226       *
227       * <p><img src="BasicGraphicsUtils-3.png" width="500"
228       * height="300" alt="[An illustration that shows which pixels
229       * get painted in what color]" />
230       *
231       * @param g the graphics into which the rectangle is drawn.
232       * @param x the x coordinate of the rectangle.
233       * @param y the y coordinate of the rectangle.
234       * @param width the width of the rectangle in pixels.
235       * @param height the height of the rectangle in pixels.
236       *
237       * @param isPressed <code>true</code> to draw the button border
238       *        with a pressed-in appearance; <code>false</code> for
239       *        normal (unpressed) appearance.
240       *
241       * @param isDefault <code>true</code> to draw the border with
242       *        the appearance it has when hitting the enter key in a
243       *        dialog will simulate a click to this button;
244       *        <code>false</code> for normal appearance.
245       *
246       * @param shadow the shadow color.
247       * @param darkShadow a darker variant of the shadow color.
248       * @param highlight the highlight color.
249       * @param lightHighlight a brighter variant of the highlight  color.
250       */
251      public static void drawBezel(Graphics g,
252                                   int x, int y, int width, int height,
253                                   boolean isPressed, boolean isDefault,
254                                   Color shadow, Color darkShadow,
255                                   Color highlight, Color lightHighlight)
256      {
257        Color oldColor = g.getColor();
258    
259        /* To understand this, it might be helpful to look at the image
260         * "BasicGraphicsUtils-3.png" that is included with the JavaDoc,
261         * and to compare it with "BasicGraphicsUtils-1.png" which shows
262         * the pixels painted by drawEtchedRect.  These image files are
263         * located in the "doc-files" subdirectory.
264         */
265        try
266        {
267          if ((isPressed == false) && (isDefault == false))
268          {
269            drawEtchedRect(g, x, y, width, height,
270                           lightHighlight, highlight,
271                           shadow, darkShadow);
272          }
273    
274          if ((isPressed == true) && (isDefault == false))
275          {
276            g.setColor(shadow);
277            g.drawRect(x + 1, y + 1, width - 2, height - 2);
278          }
279    
280          if ((isPressed == false) && (isDefault == true))
281          {
282            g.setColor(darkShadow);
283            g.drawRect(x, y, width - 1, height - 1);
284            drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2,
285                           lightHighlight, highlight,
286                           shadow, darkShadow);
287          }
288    
289          if ((isPressed == true) && (isDefault == true))
290          {
291            g.setColor(darkShadow);
292            g.drawRect(x, y, width - 1, height - 1);
293            g.setColor(shadow);
294            g.drawRect(x + 1, y + 1, width - 3, height - 3);
295          }
296        }
297        finally
298        {
299          g.setColor(oldColor);
300        }
301      }
302      
303      
304      /**
305       * Draws a rectangle that appears lowered into the surface, given
306       * four colors that are used for drawing.
307       *
308       * <p><img src="BasicGraphicsUtils-4.png" width="360"
309       * height="200" alt="[An illustration that shows which pixels
310       * get painted in what color]" />
311       *
312       * <p><strong>Compatibility with the Sun reference
313       * implementation:</strong> The Sun reference implementation seems
314       * to ignore the <code>x</code> and <code>y</code> arguments, at
315       * least in JDK 1.3.1 and 1.4.1_01.  The method always draws the
316       * rectangular area at location (0, 0). A bug report has been filed
317       * with Sun; its &#x201c;bug ID&#x201d; is 4880003.  The GNU Classpath
318       * implementation behaves correctly, thus not replicating this bug.
319       *
320       * @param g the graphics into which the rectangle is drawn.
321       * @param x the x coordinate of the rectangle.
322       * @param y the y coordinate of the rectangle.
323       * @param width the width of the rectangle in pixels.
324       * @param height the height of the rectangle in pixels.
325       *
326       * @param shadow the color that will be used for painting
327       *        the inner side of the top and left edges.
328       *
329       * @param darkShadow the color that will be used for painting
330       *        the outer side of the top and left edges.
331       *
332       * @param highlight the color that will be used for painting
333       *        the inner side of the bottom and right edges.
334       *
335       * @param lightHighlight the color that will be used for painting
336       *        the outer side of the bottom and right edges.
337       */
338      public static void drawLoweredBezel(Graphics g,
339                                          int x, int y, int width, int height,
340                                          Color shadow, Color darkShadow,
341                                          Color highlight, Color lightHighlight)
342      {
343        /* Like drawEtchedRect, but swapping darkShadow and shadow.
344         *
345         * To understand this, it might be helpful to look at the image
346         * "BasicGraphicsUtils-4.png" that is included with the JavaDoc,
347         * and to compare it with "BasicGraphicsUtils-1.png" which shows
348         * the pixels painted by drawEtchedRect.  These image files are
349         * located in the "doc-files" subdirectory.
350         */
351        drawEtchedRect(g, x, y, width, height,
352                       darkShadow, shadow,
353                       highlight, lightHighlight);
354      }
355      
356      
357      /**
358       * Draws a String at the given location, underlining the first
359       * occurence of a specified character. The algorithm for determining
360       * the underlined position is not sensitive to case. If the
361       * character is not part of <code>text</code>, the text will be
362       * drawn without underlining. Drawing is performed in the current
363       * color and font of <code>g</code>.
364       *
365       * <p><img src="BasicGraphicsUtils-5.png" width="500"
366       * height="100" alt="[An illustration showing how to use the
367       * method]" />
368       *
369       * @param g the graphics into which the String is drawn.
370       *
371       * @param text the String to draw.
372       *
373       * @param underlinedChar the character whose first occurence in
374       *        <code>text</code> will be underlined. It is not clear
375       *        why the API specification declares this argument to be
376       *        of type <code>int</code> instead of <code>char</code>.
377       *        While this would allow to pass Unicode characters outside
378       *        Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least
379       *        the GNU Classpath implementation does not underline
380       *        anything if <code>underlinedChar</code> is outside
381       *        the range of <code>char</code>.
382       *        
383       * @param x the x coordinate of the text, as it would be passed to
384       *        {@link java.awt.Graphics#drawString(java.lang.String,
385       *        int, int)}.
386       *
387       * @param y the y coordinate of the text, as it would be passed to
388       *        {@link java.awt.Graphics#drawString(java.lang.String,
389       *        int, int)}.
390       */
391      public static void drawString(Graphics g, String text,
392                                    int underlinedChar, int x, int y)
393      {
394        int index = -1;
395    
396        /* It is intentional that lower case is used. In some languages,
397         * the set of lowercase characters is larger than the set of
398         * uppercase ones. Therefore, it is good practice to use lowercase
399         * for such comparisons (which really means that the author of this
400         * code can vaguely remember having read some Unicode techreport
401         * with this recommendation, but is too lazy to look for the URL).
402         */
403        if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
404          index = text.toLowerCase().indexOf(
405            Character.toLowerCase((char) underlinedChar));
406    
407        drawStringUnderlineCharAt(g, text, index, x, y);
408      }
409    
410    
411      /**
412       * Draws a String at the given location, underlining the character
413       * at the specified index. Drawing is performed in the current color
414       * and font of <code>g</code>.
415       *
416       * <p><img src="BasicGraphicsUtils-5.png" width="500"
417       * height="100" alt="[An illustration showing how to use the
418       * method]" />
419       *
420       * @param g the graphics into which the String is drawn.
421       *
422       * @param text the String to draw.
423       *
424       * @param underlinedIndex the index of the underlined character in
425       *        <code>text</code>.  If <code>underlinedIndex</code> falls
426       *        outside the range <code>[0, text.length() - 1]</code>, the
427       *        text will be drawn without underlining anything.
428       *        
429       * @param x the x coordinate of the text, as it would be passed to
430       *        {@link java.awt.Graphics#drawString(java.lang.String,
431       *        int, int)}.
432       *
433       * @param y the y coordinate of the text, as it would be passed to
434       *        {@link java.awt.Graphics#drawString(java.lang.String,
435       *        int, int)}.
436       *
437       * @since 1.4
438       */
439      public static void drawStringUnderlineCharAt(Graphics g, String text,
440                                                   int underlinedIndex,
441                                                   int x, int y)
442      {
443        Graphics2D g2;
444        Rectangle2D.Double underline;
445        FontRenderContext frc;
446        FontMetrics fmet;
447        LineMetrics lineMetrics;
448        Font font;
449        TextLayout layout;
450        double underlineX1, underlineX2;
451        boolean drawUnderline;
452        int textLength;
453    
454        textLength = text.length();
455        if (textLength == 0)
456          return;
457    
458        drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
459    
460  public class BasicGraphicsUtils      if (!(g instanceof Graphics2D))
 {    
     public static Dimension getPreferredSize(JComponent b,  
                                              int gap,  
                                              String text,  
                                              Icon icon,  
                                              int va,  
                                              int ha,  
                                              int htp,  
                                              int vtp)  
     {  
         JComponent c = b;  
         // this is a staight copy from BasicButtonUI.paint()  
         //  
         Rectangle tr = new Rectangle();  
         Rectangle ir = new Rectangle();  
         Rectangle vr = new Rectangle();  
   
         Font f = c.getFont();  
   
         FontMetrics fm = SwingUtilities.getFontMetrics(f);  
   
         Insets i = c.getInsets();  
   
         vr.x      = i.left;  
         vr.y      = i.top;  
         vr.width  = b.getWidth()  - (i.right  +  i.left);  
         vr.height = b.getHeight() - (i.bottom +  i.top);  
   
         //      System.out.println("              VIEW-RECT-BUTTON="+vr+", insets="+i);  
   
         String tt = SwingUtilities.layoutCompoundLabel(b,  
                                                        fm,  
                                                        text,  
                                                        icon,  
                                                        va,  
                                                        ha,  
                                                        vtp,  
                                                        htp,  
                                                        vr,  
                                                        ir,  
                                                        tr,  
                                                        gap);  
           
         Rectangle r = ir.union(tr);  
           
         Insets insets = b.getInsets();  
         r.width  += insets.left + insets.right;  
         r.height += insets.top  + insets.bottom;  
   
         //      System.out.println("COMPUTED SIZE FOR PREF_SIZE="+r);  
   
         return r.getSize();  
     }  
   
     public static void drawString(Graphics g,  
                                   String text,  
                                   int underlinedChar,  
                                   int x,  
                                   int y)  
461      {      {
462          g.drawString(text, x, y);        /* Fall-back. This is likely to produce garbage for any text
463           * containing right-to-left (Hebrew or Arabic) characters, even
464           * if the underlined character is left-to-right.
465           */
466          g.drawString(text, x, y);
467          if (drawUnderline)
468          {
469            fmet = g.getFontMetrics();
470            g.fillRect(
471              /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
472              /* y */ y + fmet.getDescent() - 1,
473              /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
474              /* height */ 1);
475          }
476    
477          return;
478      }      }
 }  
479    
480        g2 = (Graphics2D) g;
481        font = g2.getFont();
482        frc = g2.getFontRenderContext();
483        lineMetrics = font.getLineMetrics(text, frc);
484        layout = new TextLayout(text, font, frc);
485    
486        /* Draw the text. */
487        layout.draw(g2, x, y);
488        if (!drawUnderline)
489          return;
490    
491        underlineX1 = x + layout.getLogicalHighlightShape(
492         underlinedIndex, underlinedIndex).getBounds2D().getX();
493        underlineX2 = x + layout.getLogicalHighlightShape(
494         underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
495    
496        underline = new Rectangle2D.Double();
497        if (underlineX1 < underlineX2)
498        {
499          underline.x = underlineX1;
500          underline.width = underlineX2 - underlineX1;
501        }
502        else
503        {
504          underline.x = underlineX2;
505          underline.width = underlineX1 - underlineX2;
506        }
507    
508        
509        underline.height = lineMetrics.getUnderlineThickness();
510        underline.y = lineMetrics.getUnderlineOffset();
511        if (underline.y == 0)
512        {
513          /* Some fonts do not specify an underline offset, although they
514           * actually should do so. In that case, the result of calling
515           * lineMetrics.getUnderlineOffset() will be zero. Since it would
516           * look very ugly if the underline was be positioned immediately
517           * below the baseline, we check for this and move the underline
518           * below the descent, as shown in the following ASCII picture:
519           *
520           *   #####       ##### #
521           *  #     #     #     #
522           *  #     #     #     #
523           *  #     #     #     #
524           *   #####       ######        ---- baseline (0)
525           *                    #
526           *                    #
527           * ------------------###----------- lineMetrics.getDescent()
528           */
529          underline.y = lineMetrics.getDescent();
530        }
531    
532        underline.y += y;
533        g2.fill(underline);
534      }
535    
536    
537      /**
538       * Draws a rectangle, simulating a dotted stroke by painting only
539       * every second pixel along the one-pixel thick edge. The color of
540       * those pixels is the current color of the Graphics <code>g</code>.
541       * Any other pixels are left unchanged.
542       *
543       * <p><img src="BasicGraphicsUtils-7.png" width="360"
544       * height="200" alt="[An illustration that shows which pixels
545       * get painted]" />
546       *
547       * @param g the graphics into which the rectangle is drawn.
548       * @param x the x coordinate of the rectangle.
549       * @param y the y coordinate of the rectangle.
550       * @param width the width of the rectangle in pixels.
551       * @param height the height of the rectangle in pixels.
552       */
553      public static void drawDashedRect(Graphics g,
554                                        int x, int y, int width, int height)
555      {
556        int right = x + width - 1;
557        int bottom = y + height - 1;
558    
559        /* Draw the top and bottom edge of the dotted rectangle. */
560        for (int i = x; i <= right; i += 2)
561        {
562          g.drawLine(i, y, i, y);
563          g.drawLine(i, bottom, i, bottom);
564        }
565    
566        /* Draw the left and right edge of the dotted rectangle. */
567        for (int i = y; i <= bottom; i += 2)
568        {
569          g.drawLine(x, i, x, i);
570          g.drawLine(right, i, right, i);
571        }
572      }
573    
574    
575      /**
576       * Determines the preferred width and height of an AbstractButton,
577       * given the gap between the button&#x2019;s text and icon.
578       *
579       * @param b the button whose preferred size is determined.
580       *
581       * @param textIconGap the gap between the button&#x2019;s text and
582       *        icon.
583       *
584       * @return a <code>Dimension</code> object whose <code>width</code>
585       *         and <code>height</code> fields indicate the preferred
586       *         extent in pixels.
587       *
588       * @see javax.swing.SwingUtilities#layoutCompoundLabel
589       */
590      public static Dimension getPreferredButtonSize(AbstractButton b,
591                                                     int textIconGap)
592      {
593        Rectangle contentRect;
594        Rectangle viewRect;
595        Rectangle iconRect = new Rectangle();
596        Rectangle textRect = new Rectangle();
597        Insets insets = b.getInsets();
598        
599        /* For determining the ideal size, do not assume a size restriction. */
600        viewRect = new Rectangle(0, 0,
601                                 /* width */ Integer.MAX_VALUE,
602                                 /* height */ Integer.MAX_VALUE);
603    
604         /* java.awt.Toolkit.getFontMetrics is deprecated. However, it
605         * seems not obvious how to get to the correct FontMetrics object
606         * otherwise. The real problem probably is that the method
607         * javax.swing.SwingUtilities.layoutCompundLabel should take a
608         * LineMetrics, not a FontMetrics argument. But fixing this that
609         * would change the public API.
610         */
611        SwingUtilities.layoutCompoundLabel(
612          b, // for the component orientation
613          b.getToolkit().getFontMetrics(b.getFont()), // see comment above
614          b.getText(),
615          b.getIcon(),
616          b.getVerticalAlignment(),
617          b.getHorizontalAlignment(),
618          b.getVerticalTextPosition(),
619          b.getHorizontalTextPosition(),
620          viewRect, iconRect, textRect,
621          textIconGap);
622    
623    
624        /*  +------------------------+       +------------------------+
625         *  |                        |       |                        |
626         *  | ICON                   |       | CONTENTCONTENTCONTENT  |
627         *  |          TEXTTEXTTEXT  |  -->  | CONTENTCONTENTCONTENT  |
628         *  |          TEXTTEXTTEXT  |       | CONTENTCONTENTCONTENT  |
629         *  +------------------------+       +------------------------+
630         */
631        contentRect = textRect.union(iconRect);
632    
633        return new Dimension(insets.left + contentRect.width + insets.right,
634                             insets.top + contentRect.height + insets.bottom);
635      }
636    }

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26