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