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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 381 static boolean isLeftToRight( Component c ) { 382 return c.getComponentOrientation().isLeftToRight(); 383 } 384 385 @SuppressWarnings("deprecation") 386 static boolean isMenuShortcutKeyDown(InputEvent event) { 387 return (event.getModifiers() & 388 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0; 389 } 390 391 /** 392 * Draws the given string at the specified location using text properties 393 * and anti-aliasing hints from the provided component. 394 * Nothing is drawn for the null string. 395 * 396 * @param c the component that will display the string, may be null 397 * @param g the graphics context, must not be null 398 * @param string the string to display, may be null 399 * @param x the x coordinate to draw the text at 400 * @param y the y coordinate to draw the text at 401 * @throws NullPointerException if the specified {@code g} is {@code null} 402 * 403 * @since 9 404 */ 405 public static void drawString(JComponent c, Graphics2D g, String string, 406 float x, float y) { 407 SwingUtilities2.drawString(c, g, string, x, y, true); 408 } 409 410 /** 411 * Draws the given string at the specified location underlining 412 * the specified character. The provided component is used to query text 413 * properties and anti-aliasing hints. 414 * <p> 415 * The {@code underlinedIndex} parameter points to a char value 416 * (Unicode code unit) in the given string. 417 * If the char value specified at the underlined index is in 418 * the high-surrogate range and the char value at the following index is in 419 * the low-surrogate range then the supplementary character corresponding 420 * to this surrogate pair is underlined. 421 * <p> 422 * No character is underlined if the index is negative or greater 423 * than the string length {@code (index < 0 || index >= string.length())} 424 * or if the char value specified at the given index 425 * is in the low-surrogate range. 426 * 427 * @param c the component that will display the string, may be null 428 * @param g the graphics context, must not be null 429 * @param string the string to display, may be null 430 * @param underlinedIndex index of a a char value (Unicode code unit) 431 * in the string to underline 432 * @param x the x coordinate to draw the text at 433 * @param y the y coordinate to draw the text at 434 * @throws NullPointerException if the specified {@code g} is {@code null} 435 * 436 * @see #getStringWidth 437 * 438 * @since 9 439 */ 440 public static void drawStringUnderlineCharAt(JComponent c, Graphics2D g, 441 String string, int underlinedIndex, float x, float y) { 442 SwingUtilities2.drawStringUnderlineCharAt(c, g, string, underlinedIndex, 443 x, y, true); 444 } 445 446 /** 447 * Clips the passed in string to the space provided. 448 * The provided component is used to query text properties and anti-aliasing hints. 449 * The unchanged string is returned if the space provided is greater than 450 * the string width. 451 * 452 * @param c the component, may be null 453 * @param fm the FontMetrics used to measure the string width, must be 454 * obtained from the correct font and graphics. Must not be null. 455 * @param string the string to clip, may be null 456 * @param availTextWidth the amount of space that the string can be drawn in 457 * @return the clipped string that fits in the provided space, an empty 458 * string if the given string argument is {@code null} or empty 459 * @throws NullPointerException if the specified {@code fm} is {@code null} 460 * 461 * @see #getStringWidth 462 * 463 * @since 9 464 */ 465 public static String getClippedString(JComponent c, FontMetrics fm, 466 String string, int availTextWidth) { 467 return SwingUtilities2.clipStringIfNecessary(c, fm, string, availTextWidth); 468 } 469 470 /** 471 * Returns the width of the passed in string using text properties 472 * and anti-aliasing hints from the provided component. 473 * If the passed string is {@code null}, returns zero. 474 * 475 * @param c the component, may be null 476 * @param fm the FontMetrics used to measure the advance string width, must 477 * be obtained from the correct font and graphics. Must not be null. 478 * @param string the string to get the advance width of, may be null 479 * @return the advance width of the specified string, zero is returned for 480 * {@code null} string 481 * @throws NullPointerException if the specified {@code fm} is {@code null} 482 * 483 * @since 9 484 */ 485 public static float getStringWidth(JComponent c, FontMetrics fm, String string) { 486 return SwingUtilities2.stringWidth(c, fm, string, true); 487 } 488 }