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