1 /* 2 * Copyright (c) 1997, 2008, 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 com.sun.java.swing.plaf.motif; 26 27 import sun.swing.SwingUtilities2; 28 29 import javax.swing.*; 30 import java.awt.Color; 31 import java.awt.Dimension; 32 import java.awt.Graphics; 33 import java.awt.Font; 34 import java.awt.FontMetrics; 35 import java.awt.Rectangle; 36 import java.awt.Component; 37 import java.awt.Insets; 38 import java.awt.event.KeyEvent; 39 import java.awt.Container; 40 41 import javax.swing.plaf.basic.*; 42 import javax.swing.text.View; 43 44 /* 45 * @author Jeff Dinkins 46 * @author Dave Kloba 47 */ 48 49 public class MotifGraphicsUtils implements SwingConstants 50 { 51 /* Client Property keys for text and accelerator text widths */ 52 private static final String MAX_ACC_WIDTH = "maxAccWidth"; 53 54 /** 55 * Draws the point (<b>x</b>, <b>y</b>) in the current color. 56 */ 57 static void drawPoint(Graphics g, int x, int y) { 58 g.drawLine(x, y, x, y); 59 } 60 61 /* 62 * Convenience method for drawing a grooved line 63 * 64 */ 65 public static void drawGroove(Graphics g, int x, int y, int w, int h, 66 Color shadow, Color highlight) 67 { 68 Color oldColor = g.getColor(); // Make no net change to g 69 g.translate(x, y); 70 71 g.setColor(shadow); 72 g.drawRect(0, 0, w-2, h-2); 73 74 g.setColor(highlight); 75 g.drawLine(1, h-3, 1, 1); 76 g.drawLine(1, 1, w-3, 1); 77 78 g.drawLine(0, h-1, w-1, h-1); 79 g.drawLine(w-1, h-1, w-1, 0); 80 81 g.translate(-x, -y); 82 g.setColor(oldColor); 83 } 84 85 /** Draws <b>aString</b> in the rectangle defined by 86 * (<b>x</b>, <b>y</b>, <b>width</b>, <b>height</b>). 87 * <b>justification</b> specifies the text's justification, one of 88 * LEFT, CENTER, or RIGHT. 89 * <b>drawStringInRect()</b> does not clip to the rectangle, but instead 90 * uses this rectangle and the desired justification to compute the point 91 * at which to begin drawing the text. 92 * @see #drawString 93 */ 94 public static void drawStringInRect(Graphics g, String aString, int x, int y, 95 int width, int height, int justification) { 96 drawStringInRect(null, g, aString, x, y, width, height, justification); 97 } 98 99 static void drawStringInRect(JComponent c, Graphics g, String aString, 100 int x, int y, int width, int height, 101 int justification) { 102 FontMetrics fontMetrics; 103 int drawWidth, startX, startY, delta; 104 105 if (g.getFont() == null) { 106 // throw new InconsistencyException("No font set"); 107 return; 108 } 109 fontMetrics = SwingUtilities2.getFontMetrics(c, g); 110 if (fontMetrics == null) { 111 // throw new InconsistencyException("No metrics for Font " + font()); 112 return; 113 } 114 115 if (justification == CENTER) { 116 drawWidth = SwingUtilities2.stringWidth(c, fontMetrics, aString); 117 if (drawWidth > width) { 118 drawWidth = width; 119 } 120 startX = x + (width - drawWidth) / 2; 121 } else if (justification == RIGHT) { 122 drawWidth = SwingUtilities2.stringWidth(c, fontMetrics, aString); 123 if (drawWidth > width) { 124 drawWidth = width; 125 } 126 startX = x + width - drawWidth; 127 } else { 128 startX = x; 129 } 130 131 delta = (height - fontMetrics.getAscent() - fontMetrics.getDescent()) / 2; 132 if (delta < 0) { 133 delta = 0; 134 } 135 136 startY = y + height - delta - fontMetrics.getDescent(); 137 138 SwingUtilities2.drawString(c, g, aString, startX, startY); 139 } 140 141 /** 142 * @deprecated This method is not being used to paint menu item since 143 * 6.0 This code left for compatibility only. Do not use or 144 * override it, this will not cause any visible effect. 145 */ 146 @Deprecated(since = "9") 147 public static void paintMenuItem(Graphics g, JComponent c, 148 Icon checkIcon, Icon arrowIcon, 149 Color background, Color foreground, 150 int defaultTextIconGap) 151 { 152 153 JMenuItem b = (JMenuItem) c; 154 ButtonModel model = b.getModel(); 155 156 Dimension size = b.getSize(); 157 Insets i = c.getInsets(); 158 159 Rectangle viewRect = new Rectangle(size); 160 161 viewRect.x += i.left; 162 viewRect.y += i.top; 163 viewRect.width -= (i.right + viewRect.x); 164 viewRect.height -= (i.bottom + viewRect.y); 165 166 Rectangle iconRect = new Rectangle(); 167 Rectangle textRect = new Rectangle(); 168 Rectangle acceleratorRect = new Rectangle(); 169 Rectangle checkRect = new Rectangle(); 170 Rectangle arrowRect = new Rectangle(); 171 172 Font holdf = g.getFont(); 173 Font f = c.getFont(); 174 g.setFont(f); 175 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g, f); 176 FontMetrics fmAccel = SwingUtilities2.getFontMetrics( 177 c, g, UIManager.getFont("MenuItem.acceleratorFont")); 178 179 if (c.isOpaque()) { 180 if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) { 181 g.setColor(background); 182 } else { 183 g.setColor(c.getBackground()); 184 } 185 g.fillRect(0,0, size.width, size.height); 186 } 187 188 // get Accelerator text 189 KeyStroke accelerator = b.getAccelerator(); 190 String acceleratorText = ""; 191 if (accelerator != null) { 192 int modifiers = accelerator.getModifiers(); 193 if (modifiers > 0) { 194 acceleratorText = KeyEvent.getKeyModifiersText(modifiers); 195 acceleratorText += "+"; 196 } 197 acceleratorText += KeyEvent.getKeyText(accelerator.getKeyCode()); 198 } 199 200 // layout the text and icon 201 String text = layoutMenuItem(c, fm, b.getText(), fmAccel, 202 acceleratorText, b.getIcon(), 203 checkIcon, arrowIcon, 204 b.getVerticalAlignment(), 205 b.getHorizontalAlignment(), 206 b.getVerticalTextPosition(), 207 b.getHorizontalTextPosition(), 208 viewRect, iconRect, 209 textRect, acceleratorRect, 210 checkRect, arrowRect, 211 b.getText() == null 212 ? 0 : defaultTextIconGap, 213 defaultTextIconGap 214 ); 215 216 // Paint the Check 217 Color holdc = g.getColor(); 218 if (checkIcon != null) { 219 if(model.isArmed() || (c instanceof JMenu && model.isSelected())) 220 g.setColor(foreground); 221 checkIcon.paintIcon(c, g, checkRect.x, checkRect.y); 222 g.setColor(holdc); 223 } 224 225 // Paint the Icon 226 if(b.getIcon() != null) { 227 Icon icon; 228 if(!model.isEnabled()) { 229 icon = b.getDisabledIcon(); 230 } else if(model.isPressed() && model.isArmed()) { 231 icon = b.getPressedIcon(); 232 if(icon == null) { 233 // Use default icon 234 icon = b.getIcon(); 235 } 236 } else { 237 icon = b.getIcon(); 238 } 239 240 if (icon!=null) { 241 icon.paintIcon(c, g, iconRect.x, iconRect.y); 242 } 243 } 244 245 // Draw the Text 246 if(text != null && !text.equals("")) { 247 // Once BasicHTML becomes public, use BasicHTML.propertyKey 248 // instead of the hardcoded string below! 249 View v = (View) c.getClientProperty("html"); 250 if (v != null) { 251 v.paint(g, textRect); 252 } else { 253 int mnemIndex = b.getDisplayedMnemonicIndex(); 254 255 if(!model.isEnabled()) { 256 // *** paint the text disabled 257 g.setColor(b.getBackground().brighter()); 258 SwingUtilities2.drawStringUnderlineCharAt(b, g,text, 259 mnemIndex, 260 textRect.x, textRect.y + fmAccel.getAscent()); 261 g.setColor(b.getBackground().darker()); 262 SwingUtilities2.drawStringUnderlineCharAt(b, g,text, 263 mnemIndex, 264 textRect.x - 1, textRect.y + fmAccel.getAscent() - 1); 265 266 } else { 267 // *** paint the text normally 268 if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) { 269 g.setColor(foreground); 270 } else { 271 g.setColor(b.getForeground()); 272 } 273 SwingUtilities2.drawStringUnderlineCharAt(b, g,text, 274 mnemIndex, 275 textRect.x, 276 textRect.y + fm.getAscent()); 277 } 278 } 279 } 280 281 // Draw the Accelerator Text 282 if(acceleratorText != null && !acceleratorText.equals("")) { 283 284 //Get the maxAccWidth from the parent to calculate the offset. 285 int accOffset = 0; 286 Container parent = b.getParent(); 287 if (parent != null && parent instanceof JComponent) { 288 JComponent p = (JComponent) parent; 289 Integer maxValueInt = (Integer) p.getClientProperty(MotifGraphicsUtils.MAX_ACC_WIDTH); 290 int maxValue = maxValueInt != null ? 291 maxValueInt.intValue() : acceleratorRect.width; 292 293 //Calculate the offset, with which the accelerator texts will be drawn with. 294 accOffset = maxValue - acceleratorRect.width; 295 } 296 297 g.setFont( UIManager.getFont("MenuItem.acceleratorFont") ); 298 if(!model.isEnabled()) { 299 // *** paint the acceleratorText disabled 300 g.setColor(b.getBackground().brighter()); 301 SwingUtilities2.drawString(c, g,acceleratorText, 302 acceleratorRect.x - accOffset, acceleratorRect.y + fm.getAscent()); 303 g.setColor(b.getBackground().darker()); 304 SwingUtilities2.drawString(c, g,acceleratorText, 305 acceleratorRect.x - accOffset - 1, acceleratorRect.y + fm.getAscent() - 1); 306 } else { 307 // *** paint the acceleratorText normally 308 if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) 309 { 310 g.setColor(foreground); 311 } else { 312 g.setColor(b.getForeground()); 313 } 314 SwingUtilities2.drawString(c, g,acceleratorText, 315 acceleratorRect.x - accOffset, 316 acceleratorRect.y + fmAccel.getAscent()); 317 } 318 } 319 320 // Paint the Arrow 321 if (arrowIcon != null) { 322 if(model.isArmed() || (c instanceof JMenu && model.isSelected())) 323 g.setColor(foreground); 324 if( !(b.getParent() instanceof JMenuBar) ) 325 arrowIcon.paintIcon(c, g, arrowRect.x, arrowRect.y); 326 } 327 328 g.setColor(holdc); 329 g.setFont(holdf); 330 } 331 332 333 /** 334 * Compute and return the location of the icons origin, the 335 * location of origin of the text baseline, and a possibly clipped 336 * version of the compound labels string. Locations are computed 337 * relative to the viewR rectangle. 338 */ 339 340 private static String layoutMenuItem( 341 JComponent c, 342 FontMetrics fm, 343 String text, 344 FontMetrics fmAccel, 345 String acceleratorText, 346 Icon icon, 347 Icon checkIcon, 348 Icon arrowIcon, 349 int verticalAlignment, 350 int horizontalAlignment, 351 int verticalTextPosition, 352 int horizontalTextPosition, 353 Rectangle viewR, 354 Rectangle iconR, 355 Rectangle textR, 356 Rectangle acceleratorR, 357 Rectangle checkIconR, 358 Rectangle arrowIconR, 359 int textIconGap, 360 int menuItemGap 361 ) 362 { 363 364 SwingUtilities.layoutCompoundLabel(c, 365 fm, 366 text, 367 icon, 368 verticalAlignment, 369 horizontalAlignment, 370 verticalTextPosition, 371 horizontalTextPosition, 372 viewR, 373 iconR, 374 textR, 375 textIconGap); 376 377 /* Initialize the acceelratorText bounds rectangle textR. If a null 378 * or and empty String was specified we substitute "" here 379 * and use 0,0,0,0 for acceleratorTextR. 380 */ 381 if( (acceleratorText == null) || acceleratorText.equals("") ) { 382 acceleratorR.width = acceleratorR.height = 0; 383 acceleratorText = ""; 384 } 385 else { 386 acceleratorR.width 387 = SwingUtilities2.stringWidth(c, fmAccel, acceleratorText); 388 acceleratorR.height = fmAccel.getHeight(); 389 } 390 391 /* Initialize the checkIcon bounds rectangle checkIconR. 392 */ 393 394 if (checkIcon != null) { 395 checkIconR.width = checkIcon.getIconWidth(); 396 checkIconR.height = checkIcon.getIconHeight(); 397 } 398 else { 399 checkIconR.width = checkIconR.height = 0; 400 } 401 402 /* Initialize the arrowIcon bounds rectangle arrowIconR. 403 */ 404 405 if (arrowIcon != null) { 406 arrowIconR.width = arrowIcon.getIconWidth(); 407 arrowIconR.height = arrowIcon.getIconHeight(); 408 } 409 else { 410 arrowIconR.width = arrowIconR.height = 0; 411 } 412 413 414 Rectangle labelR = iconR.union(textR); 415 if( MotifGraphicsUtils.isLeftToRight(c) ) { 416 textR.x += checkIconR.width + menuItemGap; 417 iconR.x += checkIconR.width + menuItemGap; 418 419 // Position the Accelerator text rect 420 acceleratorR.x = viewR.x + viewR.width - arrowIconR.width 421 - menuItemGap - acceleratorR.width; 422 423 // Position the Check and Arrow Icons 424 checkIconR.x = viewR.x; 425 arrowIconR.x = viewR.x + viewR.width - menuItemGap 426 - arrowIconR.width; 427 } else { 428 textR.x -= (checkIconR.width + menuItemGap); 429 iconR.x -= (checkIconR.width + menuItemGap); 430 431 // Position the Accelerator text rect 432 acceleratorR.x = viewR.x + arrowIconR.width + menuItemGap; 433 434 // Position the Check and Arrow Icons 435 checkIconR.x = viewR.x + viewR.width - checkIconR.width; 436 arrowIconR.x = viewR.x + menuItemGap; 437 } 438 439 // Align the accelertor text and the check and arrow icons vertically 440 // with the center of the label rect. 441 acceleratorR.y = labelR.y + (labelR.height/2) - (acceleratorR.height/2); 442 arrowIconR.y = labelR.y + (labelR.height/2) - (arrowIconR.height/2); 443 checkIconR.y = labelR.y + (labelR.height/2) - (checkIconR.height/2); 444 445 /* 446 System.out.println("Layout: v=" +viewR+" c="+checkIconR+" i="+ 447 iconR+" t="+textR+" acc="+acceleratorR+" a="+arrowIconR); 448 */ 449 return text; 450 } 451 452 private static void drawMenuBezel(Graphics g, Color background, 453 int x, int y, 454 int width, int height) 455 { 456 // shadowed button region 457 g.setColor(background); 458 g.fillRect(x,y,width,height); 459 460 g.setColor(background.brighter().brighter()); 461 g.drawLine(x+1, y+height-1, x+width-1, y+height-1); 462 g.drawLine(x+width-1, y+height-2, x+width-1, y+1); 463 464 g.setColor(background.darker().darker()); 465 g.drawLine(x, y, x+width-2, y); 466 g.drawLine(x, y+1, x, y+height-2); 467 468 } 469 470 /* 471 * Convenience function for determining ComponentOrientation. Helps us 472 * avoid having Munge directives throughout the code. 473 */ 474 static boolean isLeftToRight( Component c ) { 475 return c.getComponentOrientation().isLeftToRight(); 476 } 477 }