1. /*
  2. * @(#)SynthGraphicsUtils.java 1.16 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 javax.swing.plaf.synth;
  8. import com.sun.java.swing.SwingUtilities2;
  9. import java.awt.*;
  10. import javax.swing.*;
  11. import javax.swing.plaf.basic.BasicHTML;
  12. import javax.swing.text.*;
  13. import sun.swing.plaf.synth.*;
  14. /**
  15. * Wrapper for primitive graphics calls.
  16. *
  17. * @version 1.16, 12/19/03
  18. * @since 1.5
  19. * @author Scott Violet
  20. */
  21. public class SynthGraphicsUtils {
  22. // These are used in the text painting code to avoid allocating a bunch of
  23. // garbage.
  24. private Rectangle paintIconR = new Rectangle();
  25. private Rectangle paintTextR = new Rectangle();
  26. private Rectangle paintViewR = new Rectangle();
  27. private Insets paintInsets = new Insets(0, 0, 0, 0);
  28. // These Rectangles/Insets are used in the text size calculation to avoid a
  29. // a bunch of garbage.
  30. private Rectangle iconR = new Rectangle();
  31. private Rectangle textR = new Rectangle();
  32. private Rectangle viewR = new Rectangle();
  33. private Insets viewSizingInsets = new Insets(0, 0, 0, 0);
  34. /**
  35. * Creates a <code>SynthGraphicsUtils</code>.
  36. */
  37. public SynthGraphicsUtils() {
  38. }
  39. /**
  40. * Draws a line between the two end points.
  41. *
  42. * @param context Identifies hosting region.
  43. * @param paintKey Identifies the portion of the component being asked
  44. * to paint, may be null.
  45. * @param g Graphics object to paint to
  46. * @param x1 x origin
  47. * @param y1 y origin
  48. * @param x2 x destination
  49. * @param y2 y destination
  50. */
  51. public void drawLine(SynthContext context, Object paintKey,
  52. Graphics g, int x1, int y1, int x2, int y2) {
  53. g.drawLine(x1, y1, x2, y2);
  54. }
  55. /**
  56. * Lays out text and an icon returning, by reference, the location to
  57. * place the icon and text.
  58. *
  59. * @param ss SynthContext
  60. * @param fm FontMetrics for the Font to use, this may be ignored
  61. * @param text Text to layout
  62. * @param icon Icon to layout
  63. * @param hAlign horizontal alignment
  64. * @param vAlign vertical alignment
  65. * @param hTextPosition horizontal text position
  66. * @param vTextPosition vertical text position
  67. * @param viewR Rectangle to layout text and icon in.
  68. * @param iconR Rectangle to place icon bounds in
  69. * @param textR Rectangle to place text in
  70. * @param iconTextGap gap between icon and text
  71. */
  72. public String layoutText(SynthContext ss, FontMetrics fm,
  73. String text, Icon icon, int hAlign,
  74. int vAlign, int hTextPosition,
  75. int vTextPosition, Rectangle viewR,
  76. Rectangle iconR, Rectangle textR, int iconTextGap) {
  77. if (icon instanceof SynthIcon) {
  78. SynthIconWrapper wrapper = SynthIconWrapper.get((SynthIcon)icon,
  79. ss);
  80. String formattedText = SwingUtilities.layoutCompoundLabel(
  81. ss.getComponent(), fm, text, wrapper, vAlign, hAlign,
  82. vTextPosition, hTextPosition, viewR, iconR, textR,
  83. iconTextGap);
  84. SynthIconWrapper.release(wrapper);
  85. return formattedText;
  86. }
  87. return SwingUtilities.layoutCompoundLabel(
  88. ss.getComponent(), fm, text, icon, vAlign, hAlign,
  89. vTextPosition, hTextPosition, viewR, iconR, textR,
  90. iconTextGap);
  91. }
  92. /**
  93. * Returns the size of the passed in string.
  94. *
  95. * @param ss SynthContext
  96. * @param font Font to use
  97. * @param metrics FontMetrics, may be ignored
  98. * @param text Text to get size of.
  99. */
  100. public int computeStringWidth(SynthContext ss, Font font,
  101. FontMetrics metrics, String text) {
  102. return SwingUtilities2.stringWidth(ss.getComponent(), metrics,
  103. text);
  104. }
  105. /**
  106. * Returns the minimum size needed to properly render an icon and text.
  107. *
  108. * @param ss SynthContext
  109. * @param font Font to use
  110. * @param text Text to layout
  111. * @param icon Icon to layout
  112. * @param hAlign horizontal alignment
  113. * @param vAlign vertical alignment
  114. * @param hTextPosition horizontal text position
  115. * @param vTextPosition vertical text position
  116. * @param iconTextGap gap between icon and text
  117. * @param mnemonicIndex Index into text to render the mnemonic at, -1
  118. * indicates no mnemonic.
  119. */
  120. public Dimension getMinimumSize(SynthContext ss, Font font, String text,
  121. Icon icon, int hAlign, int vAlign, int hTextPosition,
  122. int vTextPosition, int iconTextGap, int mnemonicIndex) {
  123. JComponent c = ss.getComponent();
  124. Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
  125. vAlign, hTextPosition, vTextPosition,
  126. iconTextGap, mnemonicIndex);
  127. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  128. if (v != null) {
  129. size.width -= v.getPreferredSpan(View.X_AXIS) -
  130. v.getMinimumSpan(View.X_AXIS);
  131. }
  132. return size;
  133. }
  134. /**
  135. * Returns the maximum size needed to properly render an icon and text.
  136. *
  137. * @param ss SynthContext
  138. * @param font Font to use
  139. * @param text Text to layout
  140. * @param icon Icon to layout
  141. * @param hAlign horizontal alignment
  142. * @param vAlign vertical alignment
  143. * @param hTextPosition horizontal text position
  144. * @param vTextPosition vertical text position
  145. * @param iconTextGap gap between icon and text
  146. * @param mnemonicIndex Index into text to render the mnemonic at, -1
  147. * indicates no mnemonic.
  148. */
  149. public Dimension getMaximumSize(SynthContext ss, Font font, String text,
  150. Icon icon, int hAlign, int vAlign, int hTextPosition,
  151. int vTextPosition, int iconTextGap, int mnemonicIndex) {
  152. JComponent c = ss.getComponent();
  153. Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
  154. vAlign, hTextPosition, vTextPosition,
  155. iconTextGap, mnemonicIndex);
  156. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  157. if (v != null) {
  158. size.width += v.getMaximumSpan(View.X_AXIS) -
  159. v.getPreferredSpan(View.X_AXIS);
  160. }
  161. return size;
  162. }
  163. /**
  164. * Returns the maximum height of the the Font from the passed in
  165. * SynthContext.
  166. *
  167. * @param context SynthContext used to determine font.
  168. * @return maximum height of the characters for the font from the passed
  169. * in context.
  170. */
  171. public int getMaximumCharHeight(SynthContext context) {
  172. FontMetrics fm = context.getComponent().getFontMetrics(
  173. context.getStyle().getFont(context));
  174. return (fm.getAscent() + fm.getDescent());
  175. }
  176. /**
  177. * Returns the preferred size needed to properly render an icon and text.
  178. *
  179. * @param ss SynthContext
  180. * @param font Font to use
  181. * @param text Text to layout
  182. * @param icon Icon to layout
  183. * @param hAlign horizontal alignment
  184. * @param vAlign vertical alignment
  185. * @param hTextPosition horizontal text position
  186. * @param vTextPosition vertical text position
  187. * @param iconTextGap gap between icon and text
  188. * @param mnemonicIndex Index into text to render the mnemonic at, -1
  189. * indicates no mnemonic.
  190. */
  191. public Dimension getPreferredSize(SynthContext ss, Font font, String text,
  192. Icon icon, int hAlign, int vAlign, int hTextPosition,
  193. int vTextPosition, int iconTextGap, int mnemonicIndex) {
  194. JComponent c = ss.getComponent();
  195. Insets insets = c.getInsets(viewSizingInsets);
  196. int dx = insets.left + insets.right;
  197. int dy = insets.top + insets.bottom;
  198. if (icon == null && (text == null || font == null)) {
  199. return new Dimension(dx, dy);
  200. }
  201. else if ((text == null) || ((icon != null) && (font == null))) {
  202. return new Dimension(SynthIcon.getIconWidth(icon, ss) + dx,
  203. SynthIcon.getIconHeight(icon, ss) + dy);
  204. }
  205. else {
  206. FontMetrics fm = c.getFontMetrics(font);
  207. iconR.x = iconR.y = iconR.width = iconR.height = 0;
  208. textR.x = textR.y = textR.width = textR.height = 0;
  209. viewR.x = dx;
  210. viewR.y = dy;
  211. viewR.width = viewR.height = Short.MAX_VALUE;
  212. layoutText(ss, fm, text, icon, hAlign, vAlign,
  213. hTextPosition, vTextPosition, viewR, iconR, textR,
  214. iconTextGap);
  215. int x1 = Math.min(iconR.x, textR.x);
  216. int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
  217. int y1 = Math.min(iconR.y, textR.y);
  218. int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
  219. Dimension rv = new Dimension(x2 - x1, y2 - y1);
  220. rv.width += dx;
  221. rv.height += dy;
  222. return rv;
  223. }
  224. }
  225. /**
  226. * Paints text at the specified location. This will not attempt to
  227. * render the text as html nor will it offset by the insets of the
  228. * component.
  229. *
  230. * @param ss SynthContext
  231. * @param g Graphics used to render string in.
  232. * @param text Text to render
  233. * @param bounds Bounds of the text to be drawn.
  234. * @param mnemonicIndex Index to draw string at.
  235. */
  236. public void paintText(SynthContext ss, Graphics g, String text,
  237. Rectangle bounds, int mnemonicIndex) {
  238. paintText(ss, g, text, bounds.x, bounds.y, mnemonicIndex);
  239. }
  240. /**
  241. * Paints text at the specified location. This will not attempt to
  242. * render the text as html nor will it offset by the insets of the
  243. * component.
  244. *
  245. * @param ss SynthContext
  246. * @param g Graphics used to render string in.
  247. * @param text Text to render
  248. * @param x X location to draw text at.
  249. * @param y Upper left corner to draw text at.
  250. * @param mnemonicIndex Index to draw string at.
  251. */
  252. public void paintText(SynthContext ss, Graphics g, String text,
  253. int x, int y, int mnemonicIndex) {
  254. if (text != null) {
  255. JComponent c = ss.getComponent();
  256. SynthStyle style = ss.getStyle();
  257. FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
  258. y += fm.getAscent();
  259. SwingUtilities2.drawString(c, g, text, x, y);
  260. if (mnemonicIndex >= 0 && mnemonicIndex < text.length()) {
  261. int underlineX = x + SwingUtilities2.stringWidth(
  262. c, fm, text.substring(0, mnemonicIndex));
  263. int underlineY = y;
  264. int underlineWidth = fm.charWidth(text.charAt(mnemonicIndex));
  265. int underlineHeight = 1;
  266. g.fillRect(underlineX, underlineY + fm.getDescent() - 1,
  267. underlineWidth, underlineHeight);
  268. }
  269. }
  270. }
  271. /**
  272. * Paints an icon and text. This will render the text as html, if
  273. * necessary, and offset the location by the insets of the component.
  274. *
  275. * @param ss SynthContext
  276. * @param g Graphics to render string and icon into
  277. * @param text Text to layout
  278. * @param icon Icon to layout
  279. * @param hAlign horizontal alignment
  280. * @param vAlign vertical alignment
  281. * @param hTextPosition horizontal text position
  282. * @param vTextPosition vertical text position
  283. * @param iconTextGap gap between icon and text
  284. * @param mnemonicIndex Index into text to render the mnemonic at, -1
  285. * indicates no mnemonic.
  286. * @param textOffset Amount to offset the text when painting
  287. */
  288. public void paintText(SynthContext ss, Graphics g, String text,
  289. Icon icon, int hAlign, int vAlign, int hTextPosition,
  290. int vTextPosition, int iconTextGap, int mnemonicIndex,
  291. int textOffset) {
  292. if ((icon == null) && (text == null)) {
  293. return;
  294. }
  295. JComponent c = ss.getComponent();
  296. FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
  297. Insets insets = SynthLookAndFeel.getPaintingInsets(ss, paintInsets);
  298. paintViewR.x = insets.left;
  299. paintViewR.y = insets.top;
  300. paintViewR.width = c.getWidth() - (insets.left + insets.right);
  301. paintViewR.height = c.getHeight() - (insets.top + insets.bottom);
  302. paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
  303. paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
  304. String clippedText =
  305. layoutText(ss, fm, text, icon, hAlign, vAlign,
  306. hTextPosition, vTextPosition, paintViewR, paintIconR,
  307. paintTextR, iconTextGap);
  308. if (icon != null) {
  309. Color color = g.getColor();
  310. paintIconR.x += textOffset;
  311. paintIconR.y += textOffset;
  312. SynthIcon.paintIcon(icon, ss, g, paintIconR.x, paintIconR.y,
  313. paintIconR.width, paintIconR.height);
  314. g.setColor(color);
  315. }
  316. if (text != null) {
  317. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  318. if (v != null) {
  319. v.paint(g, paintTextR);
  320. } else {
  321. paintTextR.x += textOffset;
  322. paintTextR.y += textOffset;
  323. paintText(ss, g, clippedText, paintTextR, mnemonicIndex);
  324. }
  325. }
  326. }
  327. /**
  328. * Wraps a SynthIcon around the Icon interface, forwarding calls to
  329. * the SynthIcon with a given SynthContext.
  330. */
  331. private static class SynthIconWrapper implements Icon {
  332. private static final java.util.List CACHE = new java.util.ArrayList(1);
  333. private SynthIcon synthIcon;
  334. private SynthContext context;
  335. static SynthIconWrapper get(SynthIcon icon, SynthContext context) {
  336. synchronized(CACHE) {
  337. int size = CACHE.size();
  338. if (size > 0) {
  339. SynthIconWrapper wrapper = (SynthIconWrapper)CACHE.remove(
  340. size - 1);
  341. wrapper.reset(icon, context);
  342. return wrapper;
  343. }
  344. }
  345. return new SynthIconWrapper(icon, context);
  346. }
  347. static void release(SynthIconWrapper wrapper) {
  348. wrapper.reset(null, null);
  349. synchronized(CACHE) {
  350. CACHE.add(wrapper);
  351. }
  352. }
  353. SynthIconWrapper(SynthIcon icon, SynthContext context) {
  354. reset(icon, context);
  355. }
  356. void reset(SynthIcon icon, SynthContext context) {
  357. synthIcon = icon;
  358. this.context = context;
  359. }
  360. public void paintIcon(Component c, Graphics g, int x, int y) {
  361. // This is a noop as this should only be for sizing calls.
  362. }
  363. public int getIconWidth() {
  364. return synthIcon.getIconWidth(context);
  365. }
  366. public int getIconHeight() {
  367. return synthIcon.getIconHeight(context);
  368. }
  369. }
  370. }