blob: 7d8bade8675b244e798292a8b0e4e5fda3e7ea9f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing.plaf.synth;
26
27import sun.swing.SwingUtilities2;
28import java.awt.*;
29import javax.swing.*;
30import javax.swing.plaf.basic.BasicHTML;
31import javax.swing.text.*;
32import sun.swing.plaf.synth.*;
33
34/**
35 * Wrapper for primitive graphics calls.
36 *
37 * @since 1.5
38 * @author Scott Violet
39 */
40public class SynthGraphicsUtils {
41 // These are used in the text painting code to avoid allocating a bunch of
42 // garbage.
43 private Rectangle paintIconR = new Rectangle();
44 private Rectangle paintTextR = new Rectangle();
45 private Rectangle paintViewR = new Rectangle();
46 private Insets paintInsets = new Insets(0, 0, 0, 0);
47
48 // These Rectangles/Insets are used in the text size calculation to avoid a
49 // a bunch of garbage.
50 private Rectangle iconR = new Rectangle();
51 private Rectangle textR = new Rectangle();
52 private Rectangle viewR = new Rectangle();
53 private Insets viewSizingInsets = new Insets(0, 0, 0, 0);
54
55 /**
56 * Creates a <code>SynthGraphicsUtils</code>.
57 */
58 public SynthGraphicsUtils() {
59 }
60
61 /**
62 * Draws a line between the two end points.
63 *
64 * @param context Identifies hosting region.
65 * @param paintKey Identifies the portion of the component being asked
66 * to paint, may be null.
67 * @param g Graphics object to paint to
68 * @param x1 x origin
69 * @param y1 y origin
70 * @param x2 x destination
71 * @param y2 y destination
72 */
73 public void drawLine(SynthContext context, Object paintKey,
74 Graphics g, int x1, int y1, int x2, int y2) {
75 g.drawLine(x1, y1, x2, y2);
76 }
77
78 /**
79 * Draws a line between the two end points.
80 * <p>This implementation supports only one line style key,
81 * <code>"dashed"</code>. The <code>"dashed"</code> line style is applied
82 * only to vertical and horizontal lines.
83 * <p>Specifying <code>null</code> or any key different from
84 * <code>"dashed"</code> will draw solid lines.
85 *
86 * @param context identifies hosting region
87 * @param paintKey identifies the portion of the component being asked
88 * to paint, may be null
89 * @param g Graphics object to paint to
90 * @param x1 x origin
91 * @param y1 y origin
92 * @param x2 x destination
93 * @param y2 y destination
94 * @param styleKey identifies the requested style of the line (e.g. "dashed")
95 * @since 1.6
96 */
97 public void drawLine(SynthContext context, Object paintKey,
98 Graphics g, int x1, int y1, int x2, int y2,
99 Object styleKey) {
100 if ("dashed".equals(styleKey)) {
101 // draw vertical line
102 if (x1 == x2) {
103 y1 += (y1 % 2);
104
105 for (int y = y1; y <= y2; y+=2) {
106 g.drawLine(x1, y, x2, y);
107 }
108 // draw horizontal line
109 } else if (y1 == y2) {
110 x1 += (x1 % 2);
111
112 for (int x = x1; x <= x2; x+=2) {
113 g.drawLine(x, y1, x, y2);
114 }
115 // oblique lines are not supported
116 }
117 } else {
118 drawLine(context, paintKey, g, x1, y1, x2, y2);
119 }
120 }
121
122 /**
123 * Lays out text and an icon returning, by reference, the location to
124 * place the icon and text.
125 *
126 * @param ss SynthContext
127 * @param fm FontMetrics for the Font to use, this may be ignored
128 * @param text Text to layout
129 * @param icon Icon to layout
130 * @param hAlign horizontal alignment
131 * @param vAlign vertical alignment
132 * @param hTextPosition horizontal text position
133 * @param vTextPosition vertical text position
134 * @param viewR Rectangle to layout text and icon in.
135 * @param iconR Rectangle to place icon bounds in
136 * @param textR Rectangle to place text in
137 * @param iconTextGap gap between icon and text
138 */
139 public String layoutText(SynthContext ss, FontMetrics fm,
140 String text, Icon icon, int hAlign,
141 int vAlign, int hTextPosition,
142 int vTextPosition, Rectangle viewR,
143 Rectangle iconR, Rectangle textR, int iconTextGap) {
144 if (icon instanceof SynthIcon) {
145 SynthIconWrapper wrapper = SynthIconWrapper.get((SynthIcon)icon,
146 ss);
147 String formattedText = SwingUtilities.layoutCompoundLabel(
148 ss.getComponent(), fm, text, wrapper, vAlign, hAlign,
149 vTextPosition, hTextPosition, viewR, iconR, textR,
150 iconTextGap);
151 SynthIconWrapper.release(wrapper);
152 return formattedText;
153 }
154 return SwingUtilities.layoutCompoundLabel(
155 ss.getComponent(), fm, text, icon, vAlign, hAlign,
156 vTextPosition, hTextPosition, viewR, iconR, textR,
157 iconTextGap);
158 }
159
160 /**
161 * Returns the size of the passed in string.
162 *
163 * @param ss SynthContext
164 * @param font Font to use
165 * @param metrics FontMetrics, may be ignored
166 * @param text Text to get size of.
167 */
168 public int computeStringWidth(SynthContext ss, Font font,
169 FontMetrics metrics, String text) {
170 return SwingUtilities2.stringWidth(ss.getComponent(), metrics,
171 text);
172 }
173
174 /**
175 * Returns the minimum size needed to properly render an icon and text.
176 *
177 * @param ss SynthContext
178 * @param font Font to use
179 * @param text Text to layout
180 * @param icon Icon to layout
181 * @param hAlign horizontal alignment
182 * @param vAlign vertical alignment
183 * @param hTextPosition horizontal text position
184 * @param vTextPosition vertical text position
185 * @param iconTextGap gap between icon and text
186 * @param mnemonicIndex Index into text to render the mnemonic at, -1
187 * indicates no mnemonic.
188 */
189 public Dimension getMinimumSize(SynthContext ss, Font font, String text,
190 Icon icon, int hAlign, int vAlign, int hTextPosition,
191 int vTextPosition, int iconTextGap, int mnemonicIndex) {
192 JComponent c = ss.getComponent();
193 Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
194 vAlign, hTextPosition, vTextPosition,
195 iconTextGap, mnemonicIndex);
196 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
197
198 if (v != null) {
199 size.width -= v.getPreferredSpan(View.X_AXIS) -
200 v.getMinimumSpan(View.X_AXIS);
201 }
202 return size;
203 }
204
205 /**
206 * Returns the maximum size needed to properly render an icon and text.
207 *
208 * @param ss SynthContext
209 * @param font Font to use
210 * @param text Text to layout
211 * @param icon Icon to layout
212 * @param hAlign horizontal alignment
213 * @param vAlign vertical alignment
214 * @param hTextPosition horizontal text position
215 * @param vTextPosition vertical text position
216 * @param iconTextGap gap between icon and text
217 * @param mnemonicIndex Index into text to render the mnemonic at, -1
218 * indicates no mnemonic.
219 */
220 public Dimension getMaximumSize(SynthContext ss, Font font, String text,
221 Icon icon, int hAlign, int vAlign, int hTextPosition,
222 int vTextPosition, int iconTextGap, int mnemonicIndex) {
223 JComponent c = ss.getComponent();
224 Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
225 vAlign, hTextPosition, vTextPosition,
226 iconTextGap, mnemonicIndex);
227 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
228
229 if (v != null) {
230 size.width += v.getMaximumSpan(View.X_AXIS) -
231 v.getPreferredSpan(View.X_AXIS);
232 }
233 return size;
234 }
235
236 /**
237 * Returns the maximum height of the the Font from the passed in
238 * SynthContext.
239 *
240 * @param context SynthContext used to determine font.
241 * @return maximum height of the characters for the font from the passed
242 * in context.
243 */
244 public int getMaximumCharHeight(SynthContext context) {
245 FontMetrics fm = context.getComponent().getFontMetrics(
246 context.getStyle().getFont(context));
247 return (fm.getAscent() + fm.getDescent());
248 }
249
250 /**
251 * Returns the preferred size needed to properly render an icon and text.
252 *
253 * @param ss SynthContext
254 * @param font Font to use
255 * @param text Text to layout
256 * @param icon Icon to layout
257 * @param hAlign horizontal alignment
258 * @param vAlign vertical alignment
259 * @param hTextPosition horizontal text position
260 * @param vTextPosition vertical text position
261 * @param iconTextGap gap between icon and text
262 * @param mnemonicIndex Index into text to render the mnemonic at, -1
263 * indicates no mnemonic.
264 */
265 public Dimension getPreferredSize(SynthContext ss, Font font, String text,
266 Icon icon, int hAlign, int vAlign, int hTextPosition,
267 int vTextPosition, int iconTextGap, int mnemonicIndex) {
268 JComponent c = ss.getComponent();
269 Insets insets = c.getInsets(viewSizingInsets);
270 int dx = insets.left + insets.right;
271 int dy = insets.top + insets.bottom;
272
273 if (icon == null && (text == null || font == null)) {
274 return new Dimension(dx, dy);
275 }
276 else if ((text == null) || ((icon != null) && (font == null))) {
277 return new Dimension(SynthIcon.getIconWidth(icon, ss) + dx,
278 SynthIcon.getIconHeight(icon, ss) + dy);
279 }
280 else {
281 FontMetrics fm = c.getFontMetrics(font);
282
283 iconR.x = iconR.y = iconR.width = iconR.height = 0;
284 textR.x = textR.y = textR.width = textR.height = 0;
285 viewR.x = dx;
286 viewR.y = dy;
287 viewR.width = viewR.height = Short.MAX_VALUE;
288
289 layoutText(ss, fm, text, icon, hAlign, vAlign,
290 hTextPosition, vTextPosition, viewR, iconR, textR,
291 iconTextGap);
292 int x1 = Math.min(iconR.x, textR.x);
293 int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
294 int y1 = Math.min(iconR.y, textR.y);
295 int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
296 Dimension rv = new Dimension(x2 - x1, y2 - y1);
297
298 rv.width += dx;
299 rv.height += dy;
300 return rv;
301 }
302 }
303
304 /**
305 * Paints text at the specified location. This will not attempt to
306 * render the text as html nor will it offset by the insets of the
307 * component.
308 *
309 * @param ss SynthContext
310 * @param g Graphics used to render string in.
311 * @param text Text to render
312 * @param bounds Bounds of the text to be drawn.
313 * @param mnemonicIndex Index to draw string at.
314 */
315 public void paintText(SynthContext ss, Graphics g, String text,
316 Rectangle bounds, int mnemonicIndex) {
317 paintText(ss, g, text, bounds.x, bounds.y, mnemonicIndex);
318 }
319
320 /**
321 * Paints text at the specified location. This will not attempt to
322 * render the text as html nor will it offset by the insets of the
323 * component.
324 *
325 * @param ss SynthContext
326 * @param g Graphics used to render string in.
327 * @param text Text to render
328 * @param x X location to draw text at.
329 * @param y Upper left corner to draw text at.
330 * @param mnemonicIndex Index to draw string at.
331 */
332 public void paintText(SynthContext ss, Graphics g, String text,
333 int x, int y, int mnemonicIndex) {
334 if (text != null) {
335 JComponent c = ss.getComponent();
336 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
337 y += fm.getAscent();
338 SwingUtilities2.drawStringUnderlineCharAt(c, g, text,
339 mnemonicIndex, x, y);
340 }
341 }
342
343 /**
344 * Paints an icon and text. This will render the text as html, if
345 * necessary, and offset the location by the insets of the component.
346 *
347 * @param ss SynthContext
348 * @param g Graphics to render string and icon into
349 * @param text Text to layout
350 * @param icon Icon to layout
351 * @param hAlign horizontal alignment
352 * @param vAlign vertical alignment
353 * @param hTextPosition horizontal text position
354 * @param vTextPosition vertical text position
355 * @param iconTextGap gap between icon and text
356 * @param mnemonicIndex Index into text to render the mnemonic at, -1
357 * indicates no mnemonic.
358 * @param textOffset Amount to offset the text when painting
359 */
360 public void paintText(SynthContext ss, Graphics g, String text,
361 Icon icon, int hAlign, int vAlign, int hTextPosition,
362 int vTextPosition, int iconTextGap, int mnemonicIndex,
363 int textOffset) {
364 if ((icon == null) && (text == null)) {
365 return;
366 }
367 JComponent c = ss.getComponent();
368 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
369 Insets insets = SynthLookAndFeel.getPaintingInsets(ss, paintInsets);
370
371 paintViewR.x = insets.left;
372 paintViewR.y = insets.top;
373 paintViewR.width = c.getWidth() - (insets.left + insets.right);
374 paintViewR.height = c.getHeight() - (insets.top + insets.bottom);
375
376 paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
377 paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
378
379 String clippedText =
380 layoutText(ss, fm, text, icon, hAlign, vAlign,
381 hTextPosition, vTextPosition, paintViewR, paintIconR,
382 paintTextR, iconTextGap);
383
384 if (icon != null) {
385 Color color = g.getColor();
386
387 if (ss.getStyle().getBoolean(ss, "TableHeader.alignSorterArrow", false) &&
388 "TableHeader.renderer".equals(c.getName())) {
389 paintIconR.x = paintViewR.width - paintIconR.width;
390 } else {
391 paintIconR.x += textOffset;
392 }
393 paintIconR.y += textOffset;
394 SynthIcon.paintIcon(icon, ss, g, paintIconR.x, paintIconR.y,
395 paintIconR.width, paintIconR.height);
396 g.setColor(color);
397 }
398
399 if (text != null) {
400 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
401
402 if (v != null) {
403 v.paint(g, paintTextR);
404 } else {
405 paintTextR.x += textOffset;
406 paintTextR.y += textOffset;
407
408 paintText(ss, g, clippedText, paintTextR, mnemonicIndex);
409 }
410 }
411 }
412
413
414 /**
415 * Wraps a SynthIcon around the Icon interface, forwarding calls to
416 * the SynthIcon with a given SynthContext.
417 */
418 private static class SynthIconWrapper implements Icon {
419 private static final java.util.List CACHE = new java.util.ArrayList(1);
420
421 private SynthIcon synthIcon;
422 private SynthContext context;
423
424 static SynthIconWrapper get(SynthIcon icon, SynthContext context) {
425 synchronized(CACHE) {
426 int size = CACHE.size();
427 if (size > 0) {
428 SynthIconWrapper wrapper = (SynthIconWrapper)CACHE.remove(
429 size - 1);
430 wrapper.reset(icon, context);
431 return wrapper;
432 }
433 }
434 return new SynthIconWrapper(icon, context);
435 }
436
437 static void release(SynthIconWrapper wrapper) {
438 wrapper.reset(null, null);
439 synchronized(CACHE) {
440 CACHE.add(wrapper);
441 }
442 }
443
444 SynthIconWrapper(SynthIcon icon, SynthContext context) {
445 reset(icon, context);
446 }
447
448 void reset(SynthIcon icon, SynthContext context) {
449 synthIcon = icon;
450 this.context = context;
451 }
452
453 public void paintIcon(Component c, Graphics g, int x, int y) {
454 // This is a noop as this should only be for sizing calls.
455 }
456
457 public int getIconWidth() {
458 return synthIcon.getIconWidth(context);
459 }
460
461 public int getIconHeight() {
462 return synthIcon.getIconHeight(context);
463 }
464 }
465}