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