001 /*
002 * Copyright 2002-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.plaf.synth;
026
027 import sun.swing.SwingUtilities2;
028 import java.awt.*;
029 import javax.swing.*;
030 import javax.swing.plaf.basic.BasicHTML;
031 import javax.swing.text.*;
032 import sun.swing.plaf.synth.*;
033
034 /**
035 * Wrapper for primitive graphics calls.
036 *
037 * @version 1.27, 05/05/07
038 * @since 1.5
039 * @author Scott Violet
040 */
041 public class SynthGraphicsUtils {
042 // These are used in the text painting code to avoid allocating a bunch of
043 // garbage.
044 private Rectangle paintIconR = new Rectangle();
045 private Rectangle paintTextR = new Rectangle();
046 private Rectangle paintViewR = new Rectangle();
047 private Insets paintInsets = new Insets(0, 0, 0, 0);
048
049 // These Rectangles/Insets are used in the text size calculation to avoid a
050 // a bunch of garbage.
051 private Rectangle iconR = new Rectangle();
052 private Rectangle textR = new Rectangle();
053 private Rectangle viewR = new Rectangle();
054 private Insets viewSizingInsets = new Insets(0, 0, 0, 0);
055
056 /**
057 * Creates a <code>SynthGraphicsUtils</code>.
058 */
059 public SynthGraphicsUtils() {
060 }
061
062 /**
063 * Draws a line between the two end points.
064 *
065 * @param context Identifies hosting region.
066 * @param paintKey Identifies the portion of the component being asked
067 * to paint, may be null.
068 * @param g Graphics object to paint to
069 * @param x1 x origin
070 * @param y1 y origin
071 * @param x2 x destination
072 * @param y2 y destination
073 */
074 public void drawLine(SynthContext context, Object paintKey,
075 Graphics g, int x1, int y1, int x2, int y2) {
076 g.drawLine(x1, y1, x2, y2);
077 }
078
079 /**
080 * Draws a line between the two end points.
081 * <p>This implementation supports only one line style key,
082 * <code>"dashed"</code>. The <code>"dashed"</code> line style is applied
083 * only to vertical and horizontal lines.
084 * <p>Specifying <code>null</code> or any key different from
085 * <code>"dashed"</code> will draw solid lines.
086 *
087 * @param context identifies hosting region
088 * @param paintKey identifies the portion of the component being asked
089 * to paint, may be null
090 * @param g Graphics object to paint to
091 * @param x1 x origin
092 * @param y1 y origin
093 * @param x2 x destination
094 * @param y2 y destination
095 * @param styleKey identifies the requested style of the line (e.g. "dashed")
096 * @since 1.6
097 */
098 public void drawLine(SynthContext context, Object paintKey,
099 Graphics g, int x1, int y1, int x2, int y2, 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, int vAlign,
141 int hTextPosition, int vTextPosition, Rectangle viewR,
142 Rectangle iconR, Rectangle textR, int iconTextGap) {
143 if (icon instanceof SynthIcon) {
144 SynthIconWrapper wrapper = SynthIconWrapper.get(
145 (SynthIcon) icon, ss);
146 String formattedText = SwingUtilities.layoutCompoundLabel(
147 ss.getComponent(), fm, text, wrapper, vAlign,
148 hAlign, vTextPosition, hTextPosition, viewR, iconR,
149 textR, iconTextGap);
150 SynthIconWrapper.release(wrapper);
151 return formattedText;
152 }
153 return SwingUtilities.layoutCompoundLabel(ss.getComponent(),
154 fm, text, icon, vAlign, hAlign, vTextPosition,
155 hTextPosition, viewR, iconR, textR, iconTextGap);
156 }
157
158 /**
159 * Returns the size of the passed in string.
160 *
161 * @param ss SynthContext
162 * @param font Font to use
163 * @param metrics FontMetrics, may be ignored
164 * @param text Text to get size of.
165 */
166 public int computeStringWidth(SynthContext ss, Font font,
167 FontMetrics metrics, String text) {
168 return SwingUtilities2.stringWidth(ss.getComponent(), metrics,
169 text);
170 }
171
172 /**
173 * Returns the minimum size needed to properly render an icon and text.
174 *
175 * @param ss SynthContext
176 * @param font Font to use
177 * @param text Text to layout
178 * @param icon Icon to layout
179 * @param hAlign horizontal alignment
180 * @param vAlign vertical alignment
181 * @param hTextPosition horizontal text position
182 * @param vTextPosition vertical text position
183 * @param iconTextGap gap between icon and text
184 * @param mnemonicIndex Index into text to render the mnemonic at, -1
185 * indicates no mnemonic.
186 */
187 public Dimension getMinimumSize(SynthContext ss, Font font,
188 String text, Icon icon, int hAlign, int vAlign,
189 int hTextPosition, int vTextPosition, int iconTextGap,
190 int mnemonicIndex) {
191 JComponent c = ss.getComponent();
192 Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
193 vAlign, hTextPosition, vTextPosition, iconTextGap,
194 mnemonicIndex);
195 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
196
197 if (v != null) {
198 size.width -= v.getPreferredSpan(View.X_AXIS)
199 - v.getMinimumSpan(View.X_AXIS);
200 }
201 return size;
202 }
203
204 /**
205 * Returns the maximum size needed to properly render an icon and text.
206 *
207 * @param ss SynthContext
208 * @param font Font to use
209 * @param text Text to layout
210 * @param icon Icon to layout
211 * @param hAlign horizontal alignment
212 * @param vAlign vertical alignment
213 * @param hTextPosition horizontal text position
214 * @param vTextPosition vertical text position
215 * @param iconTextGap gap between icon and text
216 * @param mnemonicIndex Index into text to render the mnemonic at, -1
217 * indicates no mnemonic.
218 */
219 public Dimension getMaximumSize(SynthContext ss, Font font,
220 String text, Icon icon, int hAlign, int vAlign,
221 int hTextPosition, int vTextPosition, int iconTextGap,
222 int mnemonicIndex) {
223 JComponent c = ss.getComponent();
224 Dimension size = getPreferredSize(ss, font, text, icon, hAlign,
225 vAlign, hTextPosition, vTextPosition, iconTextGap,
226 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,
266 String text, Icon icon, int hAlign, int vAlign,
267 int hTextPosition, int vTextPosition, int iconTextGap,
268 int mnemonicIndex) {
269 JComponent c = ss.getComponent();
270 Insets insets = c.getInsets(viewSizingInsets);
271 int dx = insets.left + insets.right;
272 int dy = insets.top + insets.bottom;
273
274 if (icon == null && (text == null || font == null)) {
275 return new Dimension(dx, dy);
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 } else {
280 FontMetrics fm = c.getFontMetrics(font);
281
282 iconR.x = iconR.y = iconR.width = iconR.height = 0;
283 textR.x = textR.y = textR.width = textR.height = 0;
284 viewR.x = dx;
285 viewR.y = dy;
286 viewR.width = viewR.height = Short.MAX_VALUE;
287
288 layoutText(ss, fm, text, icon, hAlign, vAlign,
289 hTextPosition, vTextPosition, viewR, iconR, textR,
290 iconTextGap);
291 int x1 = Math.min(iconR.x, textR.x);
292 int x2 = Math.max(iconR.x + iconR.width, textR.x
293 + textR.width);
294 int y1 = Math.min(iconR.y, textR.y);
295 int y2 = Math.max(iconR.y + iconR.height, textR.y
296 + textR.height);
297 Dimension rv = new Dimension(x2 - x1, y2 - y1);
298
299 rv.width += dx;
300 rv.height += dy;
301 return rv;
302 }
303 }
304
305 /**
306 * Paints text at the specified location. This will not attempt to
307 * render the text as html nor will it offset by the insets of the
308 * component.
309 *
310 * @param ss SynthContext
311 * @param g Graphics used to render string in.
312 * @param text Text to render
313 * @param bounds Bounds of the text to be drawn.
314 * @param mnemonicIndex Index to draw string at.
315 */
316 public void paintText(SynthContext ss, Graphics g, String text,
317 Rectangle bounds, int mnemonicIndex) {
318 paintText(ss, g, text, bounds.x, bounds.y, mnemonicIndex);
319 }
320
321 /**
322 * Paints text at the specified location. This will not attempt to
323 * render the text as html nor will it offset by the insets of the
324 * component.
325 *
326 * @param ss SynthContext
327 * @param g Graphics used to render string in.
328 * @param text Text to render
329 * @param x X location to draw text at.
330 * @param y Upper left corner to draw text at.
331 * @param mnemonicIndex Index to draw string at.
332 */
333 public void paintText(SynthContext ss, Graphics g, String text,
334 int x, int y, int mnemonicIndex) {
335 if (text != null) {
336 JComponent c = ss.getComponent();
337 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
338 y += fm.getAscent();
339 SwingUtilities2.drawStringUnderlineCharAt(c, g, text,
340 mnemonicIndex, x, y);
341 }
342 }
343
344 /**
345 * Paints an icon and text. This will render the text as html, if
346 * necessary, and offset the location by the insets of the component.
347 *
348 * @param ss SynthContext
349 * @param g Graphics to render string and icon into
350 * @param text Text to layout
351 * @param icon Icon to layout
352 * @param hAlign horizontal alignment
353 * @param vAlign vertical alignment
354 * @param hTextPosition horizontal text position
355 * @param vTextPosition vertical text position
356 * @param iconTextGap gap between icon and text
357 * @param mnemonicIndex Index into text to render the mnemonic at, -1
358 * indicates no mnemonic.
359 * @param textOffset Amount to offset the text when painting
360 */
361 public void paintText(SynthContext ss, Graphics g, String text,
362 Icon icon, int hAlign, int vAlign, int hTextPosition,
363 int vTextPosition, int iconTextGap, int mnemonicIndex,
364 int textOffset) {
365 if ((icon == null) && (text == null)) {
366 return;
367 }
368 JComponent c = ss.getComponent();
369 FontMetrics fm = SwingUtilities2.getFontMetrics(c, g);
370 Insets insets = SynthLookAndFeel.getPaintingInsets(ss,
371 paintInsets);
372
373 paintViewR.x = insets.left;
374 paintViewR.y = insets.top;
375 paintViewR.width = c.getWidth() - (insets.left + insets.right);
376 paintViewR.height = c.getHeight()
377 - (insets.top + insets.bottom);
378
379 paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
380 paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
381
382 String clippedText = layoutText(ss, fm, text, icon, hAlign,
383 vAlign, hTextPosition, vTextPosition, paintViewR,
384 paintIconR, paintTextR, iconTextGap);
385
386 if (icon != null) {
387 Color color = g.getColor();
388
389 if (ss.getStyle().getBoolean(ss,
390 "TableHeader.alignSorterArrow", false)
391 && "TableHeader.renderer".equals(c.getName())) {
392 paintIconR.x = paintViewR.width - paintIconR.width;
393 } else {
394 paintIconR.x += textOffset;
395 }
396 paintIconR.y += textOffset;
397 SynthIcon.paintIcon(icon, ss, g, paintIconR.x,
398 paintIconR.y, paintIconR.width, paintIconR.height);
399 g.setColor(color);
400 }
401
402 if (text != null) {
403 View v = (View) c.getClientProperty(BasicHTML.propertyKey);
404
405 if (v != null) {
406 v.paint(g, paintTextR);
407 } else {
408 paintTextR.x += textOffset;
409 paintTextR.y += textOffset;
410
411 paintText(ss, g, clippedText, paintTextR, mnemonicIndex);
412 }
413 }
414 }
415
416 /**
417 * Wraps a SynthIcon around the Icon interface, forwarding calls to
418 * the SynthIcon with a given SynthContext.
419 */
420 private static class SynthIconWrapper implements Icon {
421 private static final java.util.List CACHE = new java.util.ArrayList(
422 1);
423
424 private SynthIcon synthIcon;
425 private SynthContext context;
426
427 static SynthIconWrapper get(SynthIcon icon, SynthContext context) {
428 synchronized (CACHE) {
429 int size = CACHE.size();
430 if (size > 0) {
431 SynthIconWrapper wrapper = (SynthIconWrapper) CACHE
432 .remove(size - 1);
433 wrapper.reset(icon, context);
434 return wrapper;
435 }
436 }
437 return new SynthIconWrapper(icon, context);
438 }
439
440 static void release(SynthIconWrapper wrapper) {
441 wrapper.reset(null, null);
442 synchronized (CACHE) {
443 CACHE.add(wrapper);
444 }
445 }
446
447 SynthIconWrapper(SynthIcon icon, SynthContext context) {
448 reset(icon, context);
449 }
450
451 void reset(SynthIcon icon, SynthContext context) {
452 synthIcon = icon;
453 this .context = context;
454 }
455
456 public void paintIcon(Component c, Graphics g, int x, int y) {
457 // This is a noop as this should only be for sizing calls.
458 }
459
460 public int getIconWidth() {
461 return synthIcon.getIconWidth(context);
462 }
463
464 public int getIconHeight() {
465 return synthIcon.getIconHeight(context);
466 }
467 }
468 }
|