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 < 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