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.getTextUIDrawing(c) 117 .getStringWidth(c, fontMetrics, aString); 118 if (drawWidth > width) { 119 drawWidth = width; 120 } 121 startX = x + (width - drawWidth) / 2; 122 } else if (justification == RIGHT) { 123 drawWidth = SwingUtilities2.getTextUIDrawing(c) 124 .getStringWidth(c, fontMetrics, aString); 125 if (drawWidth > width) { 126 drawWidth = width; 127 } 128 startX = x + width - drawWidth; 129 } else { 130 startX = x; 131 } 132 133 delta = (height - fontMetrics.getAscent() - fontMetrics.getDescent()) / 2; 134 if (delta < 0) { 135 delta = 0; 136 } 137 138 startY = y + height - delta - fontMetrics.getDescent(); 139 140 SwingUtilities2.getTextUIDrawing(c) 141 .drawString(c, g, aString, startX, startY); 142 } 143 144 /** 145 * This method is not being used to paint menu item since 146 * 6.0 This code left for compatibility only. Do not use or 147 * override it, this will not cause any visible effect. 148 */ 149 public static void paintMenuItem(Graphics g, JComponent c, 150 Icon checkIcon, Icon arrowIcon, 151 Color background, Color foreground, 152 int defaultTextIconGap) 153 { 154 155 JMenuItem b = (JMenuItem) c; 156 ButtonModel model = b.getModel(); 157 158 Dimension size = b.getSize(); 159 Insets i = c.getInsets(); 160 161 Rectangle viewRect = new Rectangle(size); 162 163 viewRect.x += i.left; 164 viewRect.y += i.top; 165 viewRect.width -= (i.right + viewRect.x); 166 viewRect.height -= (i.bottom + viewRect.y); 167 168 Rectangle iconRect = new Rectangle(); 169 Rectangle textRect = new Rectangle(); 170 Rectangle acceleratorRect = new Rectangle(); 171 Rectangle checkRect = new Rectangle(); 172 Rectangle arrowRect = new Rectangle(); 173 174 Font holdf = g.getFont(); 175 Font f = c.getFont(); 176 g.setFont(f); 177 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g, f); 178 FontMetrics fmAccel = SwingUtilities2.getFontMetrics( 179 c, g, UIManager.getFont("MenuItem.acceleratorFont")); 180 181 if (c.isOpaque()) { 182 if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) { 183 g.setColor(background); 184 } else { 185 g.setColor(c.getBackground()); 186 } 187 g.fillRect(0,0, size.width, size.height); 188 } 189 190 // get Accelerator text 191 KeyStroke accelerator = b.getAccelerator(); 192 String acceleratorText = ""; 193 if (accelerator != null) { 194 int modifiers = accelerator.getModifiers(); 195 if (modifiers > 0) { 196 acceleratorText = KeyEvent.getKeyModifiersText(modifiers); 197 acceleratorText += "+"; 198 } 199 acceleratorText += KeyEvent.getKeyText(accelerator.getKeyCode()); 200 } 201 202 // layout the text and icon 203 String text = layoutMenuItem(c, fm, b.getText(), fmAccel, 204 acceleratorText, b.getIcon(), 205 checkIcon, arrowIcon, 206 b.getVerticalAlignment(), 207 b.getHorizontalAlignment(), 208 b.getVerticalTextPosition(), 209 b.getHorizontalTextPosition(), 210 viewRect, iconRect, 211 textRect, acceleratorRect, 212 checkRect, arrowRect, 213 b.getText() == null 214 ? 0 : defaultTextIconGap, 215 defaultTextIconGap 216 ); 217 218 // Paint the Check 219 Color holdc = g.getColor(); 220 if (checkIcon != null) { 221 if(model.isArmed() || (c instanceof JMenu && model.isSelected())) 222 g.setColor(foreground); 223 checkIcon.paintIcon(c, g, checkRect.x, checkRect.y); 224 g.setColor(holdc); 225 } 226 227 // Paint the Icon 228 if(b.getIcon() != null) { 229 Icon icon; 230 if(!model.isEnabled()) { 231 icon = b.getDisabledIcon(); 232 } else if(model.isPressed() && model.isArmed()) { 233 icon = b.getPressedIcon(); 234 if(icon == null) { 235 // Use default icon 236 icon = b.getIcon(); 237 } 238 } else { 239 icon = b.getIcon(); 240 } 241 242 if (icon!=null) { 243 icon.paintIcon(c, g, iconRect.x, iconRect.y); 244 } 245 } 246 247 // Draw the Text 248 if(text != null && !text.equals("")) { 249 // Once BasicHTML becomes public, use BasicHTML.propertyKey 250 // instead of the hardcoded string below! 251 View v = (View) c.getClientProperty("html"); 252 if (v != null) { 253 v.paint(g, textRect); 254 } else { 255 int mnemIndex = b.getDisplayedMnemonicIndex(); 256 257 if(!model.isEnabled()) { 258 // *** paint the text disabled 259 g.setColor(b.getBackground().brighter()); 260 SwingUtilities2.getTextUIDrawing(c).drawStringUnderlineCharAt(b, g,text, 261 mnemIndex, 262 textRect.x, textRect.y + fmAccel.getAscent()); 263 g.setColor(b.getBackground().darker()); 264 SwingUtilities2.getTextUIDrawing(c). 265 drawStringUnderlineCharAt(b, g,text, 266 mnemIndex, 267 textRect.x - 1, textRect.y + fmAccel.getAscent() - 1); 268 269 } else { 270 // *** paint the text normally 271 if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) { 272 g.setColor(foreground); 273 } else { 274 g.setColor(b.getForeground()); 275 } 276 SwingUtilities2.getTextUIDrawing(c) 277 .drawStringUnderlineCharAt(b, g,text, 278 mnemIndex, 279 textRect.x, 280 textRect.y + fm.getAscent()); 281 } 282 } 283 } 284 285 // Draw the Accelerator Text 286 if(acceleratorText != null && !acceleratorText.equals("")) { 287 288 //Get the maxAccWidth from the parent to calculate the offset. 289 int accOffset = 0; 290 Container parent = b.getParent(); 291 if (parent != null && parent instanceof JComponent) { 292 JComponent p = (JComponent) parent; 293 Integer maxValueInt = (Integer) p.getClientProperty(MotifGraphicsUtils.MAX_ACC_WIDTH); 294 int maxValue = maxValueInt != null ? 295 maxValueInt.intValue() : acceleratorRect.width; 296 297 //Calculate the offset, with which the accelerator texts will be drawn with. 298 accOffset = maxValue - acceleratorRect.width; 299 } 300 301 g.setFont( UIManager.getFont("MenuItem.acceleratorFont") ); 302 if(!model.isEnabled()) { 303 // *** paint the acceleratorText disabled 304 g.setColor(b.getBackground().brighter()); 305 SwingUtilities2.getTextUIDrawing(c).drawString(c, g,acceleratorText, 306 acceleratorRect.x - accOffset, acceleratorRect.y + fm.getAscent()); 307 g.setColor(b.getBackground().darker()); 308 SwingUtilities2.getTextUIDrawing(c).drawString(c, g,acceleratorText, 309 acceleratorRect.x - accOffset - 1, acceleratorRect.y + fm.getAscent() - 1); 310 } else { 311 // *** paint the acceleratorText normally 312 if (model.isArmed()|| (c instanceof JMenu && model.isSelected())) 313 { 314 g.setColor(foreground); 315 } else { 316 g.setColor(b.getForeground()); 317 } 318 SwingUtilities2.getTextUIDrawing(c).drawString(c, g,acceleratorText, 319 acceleratorRect.x - accOffset, 320 acceleratorRect.y + fmAccel.getAscent()); 321 } 322 } 323 324 // Paint the Arrow 325 if (arrowIcon != null) { 326 if(model.isArmed() || (c instanceof JMenu && model.isSelected())) 327 g.setColor(foreground); 328 if( !(b.getParent() instanceof JMenuBar) ) 329 arrowIcon.paintIcon(c, g, arrowRect.x, arrowRect.y); 330 } 331 332 g.setColor(holdc); 333 g.setFont(holdf); 334 } 335 336 337 /** 338 * Compute and return the location of the icons origin, the 339 * location of origin of the text baseline, and a possibly clipped 340 * version of the compound labels string. Locations are computed 341 * relative to the viewR rectangle. 342 */ 343 344 private static String layoutMenuItem( 345 JComponent c, 346 FontMetrics fm, 347 String text, 348 FontMetrics fmAccel, 349 String acceleratorText, 350 Icon icon, 351 Icon checkIcon, 352 Icon arrowIcon, 353 int verticalAlignment, 354 int horizontalAlignment, 355 int verticalTextPosition, 356 int horizontalTextPosition, 357 Rectangle viewR, 358 Rectangle iconR, 359 Rectangle textR, 360 Rectangle acceleratorR, 361 Rectangle checkIconR, 362 Rectangle arrowIconR, 363 int textIconGap, 364 int menuItemGap 365 ) 366 { 367 368 SwingUtilities.layoutCompoundLabel(c, 369 fm, 370 text, 371 icon, 372 verticalAlignment, 373 horizontalAlignment, 374 verticalTextPosition, 375 horizontalTextPosition, 376 viewR, 377 iconR, 378 textR, 379 textIconGap); 380 381 /* Initialize the acceelratorText bounds rectangle textR. If a null 382 * or and empty String was specified we substitute "" here 383 * and use 0,0,0,0 for acceleratorTextR. 384 */ 385 if( (acceleratorText == null) || acceleratorText.equals("") ) { 386 acceleratorR.width = acceleratorR.height = 0; 387 acceleratorText = ""; 388 } 389 else { 390 acceleratorR.width 391 = SwingUtilities2.getTextUIDrawing(c) 392 .getStringWidth(c, fmAccel, acceleratorText); 393 acceleratorR.height = fmAccel.getHeight(); 394 } 395 396 /* Initialize the checkIcon bounds rectangle checkIconR. 397 */ 398 399 if (checkIcon != null) { 400 checkIconR.width = checkIcon.getIconWidth(); 401 checkIconR.height = checkIcon.getIconHeight(); 402 } 403 else { 404 checkIconR.width = checkIconR.height = 0; 405 } 406 407 /* Initialize the arrowIcon bounds rectangle arrowIconR. 408 */ 409 410 if (arrowIcon != null) { 411 arrowIconR.width = arrowIcon.getIconWidth(); 412 arrowIconR.height = arrowIcon.getIconHeight(); 413 } 414 else { 415 arrowIconR.width = arrowIconR.height = 0; 416 } 417 418 419 Rectangle labelR = iconR.union(textR); 420 if( MotifGraphicsUtils.isLeftToRight(c) ) { 421 textR.x += checkIconR.width + menuItemGap; 422 iconR.x += checkIconR.width + menuItemGap; 423 424 // Position the Accelerator text rect 425 acceleratorR.x = viewR.x + viewR.width - arrowIconR.width 426 - menuItemGap - acceleratorR.width; 427 428 // Position the Check and Arrow Icons 429 checkIconR.x = viewR.x; 430 arrowIconR.x = viewR.x + viewR.width - menuItemGap 431 - arrowIconR.width; 432 } else { 433 textR.x -= (checkIconR.width + menuItemGap); 434 iconR.x -= (checkIconR.width + menuItemGap); 435 436 // Position the Accelerator text rect 437 acceleratorR.x = viewR.x + arrowIconR.width + menuItemGap; 438 439 // Position the Check and Arrow Icons 440 checkIconR.x = viewR.x + viewR.width - checkIconR.width; 441 arrowIconR.x = viewR.x + menuItemGap; 442 } 443 444 // Align the accelertor text and the check and arrow icons vertically 445 // with the center of the label rect. 446 acceleratorR.y = labelR.y + (labelR.height/2) - (acceleratorR.height/2); 447 arrowIconR.y = labelR.y + (labelR.height/2) - (arrowIconR.height/2); 448 checkIconR.y = labelR.y + (labelR.height/2) - (checkIconR.height/2); 449 450 /* 451 System.out.println("Layout: v=" +viewR+" c="+checkIconR+" i="+ 452 iconR+" t="+textR+" acc="+acceleratorR+" a="+arrowIconR); 453 */ 454 return text; 455 } 456 457 private static void drawMenuBezel(Graphics g, Color background, 458 int x, int y, 459 int width, int height) 460 { 461 // shadowed button region 462 g.setColor(background); 463 g.fillRect(x,y,width,height); 464 465 g.setColor(background.brighter().brighter()); 466 g.drawLine(x+1, y+height-1, x+width-1, y+height-1); 467 g.drawLine(x+width-1, y+height-2, x+width-1, y+1); 468 469 g.setColor(background.darker().darker()); 470 g.drawLine(x, y, x+width-2, y); 471 g.drawLine(x, y+1, x, y+height-2); 472 473 } 474 475 /* 476 * Convenience function for determining ComponentOrientation. Helps us 477 * avoid having Munge directives throughout the code. 478 */ 479 static boolean isLeftToRight( Component c ) { 480 return c.getComponentOrientation().isLeftToRight(); 481 } 482 }