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