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