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