1 /*
2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package javax.swing.plaf.basic;
26 
27 import javax.swing.*;
28 import java.awt.Component;
29 import java.awt.Color;
30 import java.awt.Dimension;
31 import java.awt.Font;
32 import java.awt.FontMetrics;
33 import java.awt.Graphics;
34 import java.awt.Graphics2D;
35 import java.awt.Insets;
36 import java.awt.Rectangle;
37 import java.awt.Toolkit;
38 import java.awt.event.InputEvent;
39 
40 import sun.swing.SwingUtilities2;
41 
42 
43 /**
44  * Convenient util class.
45  *
46  * @author Hans Muller
47  */
48 public class BasicGraphicsUtils
49 {
50 
51     private static final Insets GROOVE_INSETS = new Insets(2, 2, 2, 2);
52     private static final Insets ETCHED_INSETS = new Insets(2, 2, 2, 2);
53 
54     /**
55      * Constructs a {@code BasicGraphicsUtils}.
56      */
BasicGraphicsUtils()57     public BasicGraphicsUtils() {}
58 
59     /**
60      * Draws an etched rectangle.
61      *
62      * @param g an instance of {@code Graphics}
63      * @param x an X coordinate
64      * @param y an Y coordinate
65      * @param w a width
66      * @param h a height
67      * @param shadow a color of shadow
68      * @param darkShadow a color of dark shadow
69      * @param highlight a color highlighting
70      * @param lightHighlight a color of light highlighting
71      */
drawEtchedRect(Graphics g, int x, int y, int w, int h, Color shadow, Color darkShadow, Color highlight, Color lightHighlight)72     public static void drawEtchedRect(Graphics g, int x, int y, int w, int h,
73                                       Color shadow, Color darkShadow,
74                                       Color highlight, Color lightHighlight)
75     {
76         Color oldColor = g.getColor();  // Make no net change to g
77         g.translate(x, y);
78 
79         g.setColor(shadow);
80         g.drawLine(0, 0, w-1, 0);      // outer border, top
81         g.drawLine(0, 1, 0, h-2);      // outer border, left
82 
83         g.setColor(darkShadow);
84         g.drawLine(1, 1, w-3, 1);      // inner border, top
85         g.drawLine(1, 2, 1, h-3);      // inner border, left
86 
87         g.setColor(lightHighlight);
88         g.drawLine(w-1, 0, w-1, h-1);  // outer border, bottom
89         g.drawLine(0, h-1, w-1, h-1);  // outer border, right
90 
91         g.setColor(highlight);
92         g.drawLine(w-2, 1, w-2, h-3);  // inner border, right
93         g.drawLine(1, h-2, w-2, h-2);  // inner border, bottom
94 
95         g.translate(-x, -y);
96         g.setColor(oldColor);
97     }
98 
99 
100     /**
101      * Returns the amount of space taken up by a border drawn by
102      * <code>drawEtchedRect()</code>
103      *
104      * @return  the inset of an etched rect
105      */
getEtchedInsets()106     public static Insets getEtchedInsets() {
107         return ETCHED_INSETS;
108     }
109 
110 
111     /**
112      * Draws a groove.
113      *
114      * @param g an instance of {@code Graphics}
115      * @param x an X coordinate
116      * @param y an Y coordinate
117      * @param w a width
118      * @param h a height
119      * @param shadow a color of shadow
120      * @param highlight a color highlighting
121      */
drawGroove(Graphics g, int x, int y, int w, int h, Color shadow, Color highlight)122     public static void drawGroove(Graphics g, int x, int y, int w, int h,
123                                   Color shadow, Color highlight)
124     {
125         Color oldColor = g.getColor();  // Make no net change to g
126         g.translate(x, y);
127 
128         g.setColor(shadow);
129         g.drawRect(0, 0, w-2, h-2);
130 
131         g.setColor(highlight);
132         g.drawLine(1, h-3, 1, 1);
133         g.drawLine(1, 1, w-3, 1);
134 
135         g.drawLine(0, h-1, w-1, h-1);
136         g.drawLine(w-1, h-1, w-1, 0);
137 
138         g.translate(-x, -y);
139         g.setColor(oldColor);
140     }
141 
142     /**
143      * Returns the amount of space taken up by a border drawn by
144      * <code>drawGroove()</code>
145      *
146      * @return  the inset of a groove border
147      */
getGrooveInsets()148     public static Insets getGrooveInsets() {
149         return GROOVE_INSETS;
150     }
151 
152 
153     /**
154      * Draws a bezel.
155      *
156      * @param g an instance of {@code Graphics}
157      * @param x an X coordinate
158      * @param y an Y coordinate
159      * @param w a width
160      * @param h a height
161      * @param isPressed is component pressed
162      * @param isDefault is default drawing
163      * @param shadow a color of shadow
164      * @param darkShadow a color of dark shadow
165      * @param highlight a color highlighting
166      * @param lightHighlight a color of light highlighting
167      */
drawBezel(Graphics g, int x, int y, int w, int h, boolean isPressed, boolean isDefault, Color shadow, Color darkShadow, Color highlight, Color lightHighlight)168     public static void drawBezel(Graphics g, int x, int y, int w, int h,
169                                  boolean isPressed, boolean isDefault,
170                                  Color shadow, Color darkShadow,
171                                  Color highlight, Color lightHighlight)
172     {
173         Color oldColor = g.getColor();  // Make no net change to g
174         g.translate(x, y);
175 
176         if (isPressed && isDefault) {
177             g.setColor(darkShadow);
178             g.drawRect(0, 0, w - 1, h - 1);
179             g.setColor(shadow);
180             g.drawRect(1, 1, w - 3, h - 3);
181         } else if (isPressed) {
182             drawLoweredBezel(g, x, y, w, h,
183                              shadow, darkShadow, highlight, lightHighlight);
184         } else if (isDefault) {
185             g.setColor(darkShadow);
186             g.drawRect(0, 0, w-1, h-1);
187 
188             g.setColor(lightHighlight);
189             g.drawLine(1, 1, 1, h-3);
190             g.drawLine(2, 1, w-3, 1);
191 
192             g.setColor(highlight);
193             g.drawLine(2, 2, 2, h-4);
194             g.drawLine(3, 2, w-4, 2);
195 
196             g.setColor(shadow);
197             g.drawLine(2, h-3, w-3, h-3);
198             g.drawLine(w-3, 2, w-3, h-4);
199 
200             g.setColor(darkShadow);
201             g.drawLine(1, h-2, w-2, h-2);
202             g.drawLine(w-2, h-2, w-2, 1);
203         } else {
204             g.setColor(lightHighlight);
205             g.drawLine(0, 0, 0, h-1);
206             g.drawLine(1, 0, w-2, 0);
207 
208             g.setColor(highlight);
209             g.drawLine(1, 1, 1, h-3);
210             g.drawLine(2, 1, w-3, 1);
211 
212             g.setColor(shadow);
213             g.drawLine(1, h-2, w-2, h-2);
214             g.drawLine(w-2, 1, w-2, h-3);
215 
216             g.setColor(darkShadow);
217             g.drawLine(0, h-1, w-1, h-1);
218             g.drawLine(w-1, h-1, w-1, 0);
219         }
220         g.translate(-x, -y);
221         g.setColor(oldColor);
222     }
223 
224     /**
225      * Draws a lowered bezel.
226      *
227      * @param g an instance of {@code Graphics}
228      * @param x an X coordinate
229      * @param y an Y coordinate
230      * @param w a width
231      * @param h a height
232      * @param shadow a color of shadow
233      * @param darkShadow a color of dark shadow
234      * @param highlight a color highlighting
235      * @param lightHighlight a color of light highlighting
236      */
drawLoweredBezel(Graphics g, int x, int y, int w, int h, Color shadow, Color darkShadow, Color highlight, Color lightHighlight)237     public static void drawLoweredBezel(Graphics g, int x, int y, int w, int h,
238                                         Color shadow, Color darkShadow,
239                                         Color highlight, Color lightHighlight)  {
240         g.setColor(darkShadow);
241         g.drawLine(0, 0, 0, h-1);
242         g.drawLine(1, 0, w-2, 0);
243 
244         g.setColor(shadow);
245         g.drawLine(1, 1, 1, h-2);
246         g.drawLine(1, 1, w-3, 1);
247 
248         g.setColor(lightHighlight);
249         g.drawLine(0, h-1, w-1, h-1);
250         g.drawLine(w-1, h-1, w-1, 0);
251 
252         g.setColor(highlight);
253         g.drawLine(1, h-2, w-2, h-2);
254         g.drawLine(w-2, h-2, w-2, 1);
255      }
256 
257 
258     /**
259      * Draw a string with the graphics {@code g} at location (x,y)
260      * just like {@code g.drawString} would. The first occurrence
261      * of {@code underlineChar} in text will be underlined.
262      * The matching algorithm is not case sensitive.
263      *
264      * @param g an instance of {@code Graphics}
265      * @param text a text
266      * @param underlinedChar an underlined char
267      * @param x an X coordinate
268      * @param y an Y coordinate
269      */
drawString(Graphics g,String text,int underlinedChar,int x,int y)270     public static void drawString(Graphics g,String text,int underlinedChar,int x,int y) {
271         int index=-1;
272 
273         if (underlinedChar != '\0') {
274             char uc = Character.toUpperCase((char)underlinedChar);
275             char lc = Character.toLowerCase((char)underlinedChar);
276             int uci = text.indexOf(uc);
277             int lci = text.indexOf(lc);
278 
279             if(uci == -1) {
280                 index = lci;
281             }
282             else if(lci == -1) {
283                 index = uci;
284             }
285             else {
286                 index = (lci < uci) ? lci : uci;
287             }
288         }
289         drawStringUnderlineCharAt(g, text, index, x, y);
290     }
291 
292     /**
293      * Draw a string with the graphics <code>g</code> at location
294      * (<code>x</code>, <code>y</code>)
295      * just like <code>g.drawString</code> would.
296      * The character at index <code>underlinedIndex</code>
297      * in text will be underlined. If <code>index</code> is beyond the
298      * bounds of <code>text</code> (including &lt; 0), nothing will be
299      * underlined.
300      *
301      * @param g Graphics to draw with
302      * @param text String to draw
303      * @param underlinedIndex Index of character in text to underline
304      * @param x x coordinate to draw at
305      * @param y y coordinate to draw at
306      * @since 1.4
307      */
drawStringUnderlineCharAt(Graphics g, String text, int underlinedIndex, int x,int y)308     public static void drawStringUnderlineCharAt(Graphics g, String text,
309                            int underlinedIndex, int x,int y) {
310         SwingUtilities2.drawStringUnderlineCharAt(null, g, text,
311                 underlinedIndex, x, y);
312     }
313 
314     /**
315      * Draws dashed rectangle.
316      *
317      * @param g an instance of {@code Graphics}
318      * @param x an X coordinate
319      * @param y an Y coordinate
320      * @param width a width of rectangle
321      * @param height a height of rectangle
322      */
drawDashedRect(Graphics g,int x,int y,int width,int height)323     public static void drawDashedRect(Graphics g,int x,int y,int width,int height) {
324         int vx,vy;
325 
326         // draw upper and lower horizontal dashes
327         for (vx = x; vx < (x + width); vx+=2) {
328             g.fillRect(vx, y, 1, 1);
329             g.fillRect(vx, y + height-1, 1, 1);
330         }
331 
332         // draw left and right vertical dashes
333         for (vy = y; vy < (y + height); vy+=2) {
334             g.fillRect(x, vy, 1, 1);
335             g.fillRect(x+width-1, vy, 1, 1);
336         }
337     }
338 
339     /**
340      * Returns the preferred size of the button.
341      *
342      * @param b an instance of {@code AbstractButton}
343      * @param textIconGap a gap between text and icon
344      * @return the preferred size of the button
345      */
getPreferredButtonSize(AbstractButton b, int textIconGap)346     public static Dimension getPreferredButtonSize(AbstractButton b, int textIconGap)
347     {
348         if(b.getComponentCount() > 0) {
349             return null;
350         }
351 
352         Icon icon = b.getIcon();
353         String text = b.getText();
354 
355         Font font = b.getFont();
356         FontMetrics fm = b.getFontMetrics(font);
357 
358         Rectangle iconR = new Rectangle();
359         Rectangle textR = new Rectangle();
360         Rectangle viewR = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);
361 
362         SwingUtilities.layoutCompoundLabel(
363             b, fm, text, icon,
364             b.getVerticalAlignment(), b.getHorizontalAlignment(),
365             b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
366             viewR, iconR, textR, (text == null ? 0 : textIconGap)
367         );
368 
369         /* The preferred size of the button is the size of
370          * the text and icon rectangles plus the buttons insets.
371          */
372 
373         Rectangle r = iconR.union(textR);
374 
375         Insets insets = b.getInsets();
376         r.width += insets.left + insets.right;
377         r.height += insets.top + insets.bottom;
378 
379         return r.getSize();
380     }
381 
382     /*
383      * Convenience function for determining ComponentOrientation.  Helps us
384      * avoid having Munge directives throughout the code.
385      */
isLeftToRight( Component c )386     static boolean isLeftToRight( Component c ) {
387         return c.getComponentOrientation().isLeftToRight();
388     }
389 
isMenuShortcutKeyDown(InputEvent event)390     static boolean isMenuShortcutKeyDown(InputEvent event) {
391         return (event.getModifiersEx() &
392                 Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()) != 0;
393     }
394 
395     /**
396      * Draws the given string at the specified location using text properties
397      * and anti-aliasing hints from the provided component.
398      * Nothing is drawn for the null string.
399      *
400      * @param c the component that will display the string, may be null
401      * @param g the graphics context, must not be null
402      * @param string the string to display, may be null
403      * @param x the x coordinate to draw the text at
404      * @param y the y coordinate to draw the text at
405      * @throws NullPointerException if the specified {@code g} is {@code null}
406      *
407      * @since 9
408      */
drawString(JComponent c, Graphics2D g, String string, float x, float y)409     public static void drawString(JComponent c, Graphics2D g, String string,
410                                   float x, float y) {
411         SwingUtilities2.drawString(c, g, string, x, y, true);
412     }
413 
414     /**
415      * Draws the given string at the specified location underlining
416      * the specified character. The provided component is used to query text
417      * properties and anti-aliasing hints.
418      * <p>
419      * The {@code underlinedIndex} parameter points to a char value
420      * (Unicode code unit) in the given string.
421      * If the char value specified at the underlined index is in
422      * the high-surrogate range and the char value at the following index is in
423      * the low-surrogate range then the supplementary character corresponding
424      * to this surrogate pair is underlined.
425      * <p>
426      * No character is underlined if the index is negative or greater
427      * than the string length {@code (index < 0 || index >= string.length())}
428      * or if the char value specified at the given index
429      * is in the low-surrogate range.
430      *
431      * @param c the component that will display the string, may be null
432      * @param g the graphics context, must not be null
433      * @param string the string to display, may be null
434      * @param underlinedIndex index of a a char value (Unicode code unit)
435      *        in the string to underline
436      * @param x the x coordinate to draw the text at
437      * @param y the y coordinate to draw the text at
438      * @throws NullPointerException if the specified {@code g} is {@code null}
439      *
440      * @see #getStringWidth
441      *
442      * @since 9
443      */
drawStringUnderlineCharAt(JComponent c, Graphics2D g, String string, int underlinedIndex, float x, float y)444     public static void drawStringUnderlineCharAt(JComponent c, Graphics2D g,
445             String string, int underlinedIndex, float x, float y) {
446         SwingUtilities2.drawStringUnderlineCharAt(c, g, string, underlinedIndex,
447                                                   x, y, true);
448     }
449 
450     /**
451      * Clips the passed in string to the space provided.
452      * The provided component is used to query text properties and anti-aliasing hints.
453      * The unchanged string is returned if the space provided is greater than
454      * the string width.
455      *
456      * @param c the component, may be null
457      * @param fm the FontMetrics used to measure the string width, must be
458      *           obtained from the correct font and graphics. Must not be null.
459      * @param string the string to clip, may be null
460      * @param availTextWidth the amount of space that the string can be drawn in
461      * @return the clipped string that fits in the provided space, an empty
462      *         string if the given string argument is {@code null} or empty
463      * @throws NullPointerException if the specified {@code fm} is {@code null}
464      *
465      * @see #getStringWidth
466      *
467      * @since 9
468      */
getClippedString(JComponent c, FontMetrics fm, String string, int availTextWidth)469     public static String getClippedString(JComponent c, FontMetrics fm,
470                                           String string, int availTextWidth) {
471         return SwingUtilities2.clipStringIfNecessary(c, fm, string, availTextWidth);
472     }
473 
474     /**
475      * Returns the width of the passed in string using text properties
476      * and anti-aliasing hints from the provided component.
477      * If the passed string is {@code null}, returns zero.
478      *
479      * @param c the component, may be null
480      * @param fm the FontMetrics used to measure the advance string width, must
481      *           be obtained from the correct font and graphics. Must not be null.
482      * @param string the string to get the advance width of, may be null
483      * @return the advance width of the specified string, zero is returned for
484      *         {@code null} string
485      * @throws NullPointerException if the specified {@code fm} is {@code null}
486      *
487      * @since 9
488      */
getStringWidth(JComponent c, FontMetrics fm, String string)489     public static float getStringWidth(JComponent c, FontMetrics fm, String string) {
490         return SwingUtilities2.stringWidth(c, fm, string, true);
491     }
492 }
493