1 |
/* BasicGraphicsUtils.java |
/* BasicGraphicsUtils.java |
2 |
Copyright (C) 2002 Free Software Foundation, Inc. |
Copyright (C) 2003 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
35 |
obligated to do so. If you do not wish to do so, delete this |
obligated to do so. If you do not wish to do so, delete this |
36 |
exception statement from your version. */ |
exception statement from your version. */ |
37 |
|
|
|
|
|
38 |
package javax.swing.plaf.basic; |
package javax.swing.plaf.basic; |
39 |
|
|
40 |
|
import java.awt.Color; |
41 |
|
import java.awt.Dimension; |
42 |
|
import java.awt.Font; |
43 |
|
import java.awt.FontMetrics; |
44 |
|
import java.awt.Graphics; |
45 |
|
import java.awt.Graphics2D; |
46 |
|
import java.awt.Insets; |
47 |
|
import java.awt.Rectangle; |
48 |
|
|
49 |
|
import java.awt.font.FontRenderContext; |
50 |
|
import java.awt.font.LineMetrics; |
51 |
|
import java.awt.font.TextLayout; |
52 |
|
|
53 |
|
import java.awt.geom.Rectangle2D; |
54 |
|
|
55 |
|
import javax.swing.AbstractButton; |
56 |
|
import javax.swing.SwingUtilities; |
57 |
|
|
58 |
|
|
59 |
|
/** |
60 |
|
* A utility class providing commonly used drawing and measurement |
61 |
|
* routines. |
62 |
|
* |
63 |
|
* @author Sascha Brawer (brawer@dandelis.ch) |
64 |
|
*/ |
65 |
|
public class BasicGraphicsUtils |
66 |
|
{ |
67 |
|
/** |
68 |
|
* Constructor. It is utterly unclear why this class should |
69 |
|
* be constructable, but this is what the API specification |
70 |
|
* says. |
71 |
|
*/ |
72 |
|
public BasicGraphicsUtils() |
73 |
|
{ |
74 |
|
} |
75 |
|
|
76 |
|
|
77 |
|
/** |
78 |
|
* Draws a rectangle that appears etched into the surface, given |
79 |
|
* four colors that are used for drawing. |
80 |
|
* |
81 |
|
* <p><img src="BasicGraphicsUtils-1.png" width="360" |
82 |
|
* height="200" alt="[An illustration that shows which pixels |
83 |
|
* get painted in what color]" /> |
84 |
|
* |
85 |
|
* @param g the graphics into which the rectangle is drawn. |
86 |
|
* @param x the x coordinate of the rectangle. |
87 |
|
* @param y the y coordinate of the rectangle. |
88 |
|
* @param width the width of the rectangle in pixels. |
89 |
|
* @param height the height of the rectangle in pixels. |
90 |
|
* |
91 |
|
* @param shadow the color that will be used for painting |
92 |
|
* the outer side of the top and left edges. |
93 |
|
* |
94 |
|
* @param darkShadow the color that will be used for painting |
95 |
|
* the inner side of the top and left edges. |
96 |
|
* |
97 |
|
* @param highlight the color that will be used for painting |
98 |
|
* the inner side of the bottom and right edges. |
99 |
|
* |
100 |
|
* @param lightHighlight the color that will be used for painting |
101 |
|
* the outer side of the bottom and right edges. |
102 |
|
* |
103 |
|
* @see #getEtchedInsets() |
104 |
|
* @see javax.swing.border.EtchedBorder |
105 |
|
*/ |
106 |
|
public static void drawEtchedRect(Graphics g, |
107 |
|
int x, int y, int width, int height, |
108 |
|
Color shadow, Color darkShadow, |
109 |
|
Color highlight, Color lightHighlight) |
110 |
|
{ |
111 |
|
Color oldColor; |
112 |
|
int x2, y2; |
113 |
|
|
114 |
|
oldColor = g.getColor(); |
115 |
|
x2 = x + width - 1; |
116 |
|
y2 = y + height - 1; |
117 |
|
|
118 |
import java.awt.*; |
try |
119 |
import javax.swing.*; |
{ |
120 |
|
/* To understand this code, it might be helpful to look at the |
121 |
|
* image "BasicGraphicsUtils-1.png" that is included with the |
122 |
|
* JavaDoc. The file is located in the "doc-files" subdirectory. |
123 |
|
* |
124 |
|
* (x2, y2) is the coordinate of the most right and bottom pixel |
125 |
|
* to be painted. |
126 |
|
*/ |
127 |
|
g.setColor(shadow); |
128 |
|
g.drawLine(x, y, x2 - 1, y); // top, outer |
129 |
|
g.drawLine(x, y + 1, x, y2 - 1); // left, outer |
130 |
|
|
131 |
|
g.setColor(darkShadow); |
132 |
|
g.drawLine(x + 1, y + 1, x2 - 2, y + 1); // top, inner |
133 |
|
g.drawLine(x + 1, y + 2, x + 1, y2 - 2); // left, inner |
134 |
|
|
135 |
|
g.setColor(highlight); |
136 |
|
g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1); // bottom, inner |
137 |
|
g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2); // right, inner |
138 |
|
|
139 |
|
g.setColor(lightHighlight); |
140 |
|
g.drawLine(x, y2, x2, y2); // bottom, outer |
141 |
|
g.drawLine(x2, y, x2, y2 - 1); // right, outer |
142 |
|
} |
143 |
|
finally |
144 |
|
{ |
145 |
|
g.setColor(oldColor); |
146 |
|
} |
147 |
|
} |
148 |
|
|
149 |
|
|
150 |
|
/** |
151 |
|
* Determines the width of the border that gets painted by |
152 |
|
* {@link #drawEtchedRect}. |
153 |
|
* |
154 |
|
* @return an <code>Insets</code> object whose <code>top</code>, |
155 |
|
* <code>left</code>, <code>bottom</code> and |
156 |
|
* <code>right</code> field contain the border width at the |
157 |
|
* respective edge in pixels. |
158 |
|
*/ |
159 |
|
public static Insets getEtchedInsets() |
160 |
|
{ |
161 |
|
return new Insets(2, 2, 2, 2); |
162 |
|
} |
163 |
|
|
164 |
|
|
165 |
|
/** |
166 |
|
* Draws a rectangle that appears etched into the surface, given |
167 |
|
* two colors that are used for drawing. |
168 |
|
* |
169 |
|
* <p><img src="BasicGraphicsUtils-2.png" width="360" |
170 |
|
* height="200" alt="[An illustration that shows which pixels |
171 |
|
* get painted in what color]" /> |
172 |
|
* |
173 |
|
* @param g the graphics into which the rectangle is drawn. |
174 |
|
* @param x the x coordinate of the rectangle. |
175 |
|
* @param y the y coordinate of the rectangle. |
176 |
|
* @param width the width of the rectangle in pixels. |
177 |
|
* @param height the height of the rectangle in pixels. |
178 |
|
* |
179 |
|
* @param shadow the color that will be used for painting the outer |
180 |
|
* side of the top and left edges, and for the inner side of |
181 |
|
* the bottom and right ones. |
182 |
|
* |
183 |
|
* @param highlight the color that will be used for painting the |
184 |
|
* inner side of the top and left edges, and for the outer |
185 |
|
* side of the bottom and right ones. |
186 |
|
* |
187 |
|
* @see #getGrooveInsets() |
188 |
|
* @see javax.swing.border.EtchedBorder |
189 |
|
*/ |
190 |
|
public static void drawGroove(Graphics g, |
191 |
|
int x, int y, int width, int height, |
192 |
|
Color shadow, Color highlight) |
193 |
|
{ |
194 |
|
/* To understand this, it might be helpful to look at the image |
195 |
|
* "BasicGraphicsUtils-2.png" that is included with the JavaDoc, |
196 |
|
* and to compare it with "BasicGraphicsUtils-1.png" which shows |
197 |
|
* the pixels painted by drawEtchedRect. These image files are |
198 |
|
* located in the "doc-files" subdirectory. |
199 |
|
*/ |
200 |
|
drawEtchedRect(g, x, y, width, height, |
201 |
|
/* outer topLeft */ shadow, |
202 |
|
/* inner topLeft */ highlight, |
203 |
|
/* inner bottomRight */ shadow, |
204 |
|
/* outer bottomRight */ highlight); |
205 |
|
} |
206 |
|
|
207 |
|
|
208 |
|
/** |
209 |
|
* Determines the width of the border that gets painted by |
210 |
|
* {@link #drawGroove}. |
211 |
|
* |
212 |
|
* @return an <code>Insets</code> object whose <code>top</code>, |
213 |
|
* <code>left</code>, <code>bottom</code> and |
214 |
|
* <code>right</code> field contain the border width at the |
215 |
|
* respective edge in pixels. |
216 |
|
*/ |
217 |
|
public static Insets getGrooveInsets() |
218 |
|
{ |
219 |
|
return new Insets(2, 2, 2, 2); |
220 |
|
} |
221 |
|
|
222 |
|
|
223 |
|
/** |
224 |
|
* Draws a border that is suitable for buttons of the Basic look and |
225 |
|
* feel. |
226 |
|
* |
227 |
|
* <p><img src="BasicGraphicsUtils-3.png" width="500" |
228 |
|
* height="300" alt="[An illustration that shows which pixels |
229 |
|
* get painted in what color]" /> |
230 |
|
* |
231 |
|
* @param g the graphics into which the rectangle is drawn. |
232 |
|
* @param x the x coordinate of the rectangle. |
233 |
|
* @param y the y coordinate of the rectangle. |
234 |
|
* @param width the width of the rectangle in pixels. |
235 |
|
* @param height the height of the rectangle in pixels. |
236 |
|
* |
237 |
|
* @param isPressed <code>true</code> to draw the button border |
238 |
|
* with a pressed-in appearance; <code>false</code> for |
239 |
|
* normal (unpressed) appearance. |
240 |
|
* |
241 |
|
* @param isDefault <code>true</code> to draw the border with |
242 |
|
* the appearance it has when hitting the enter key in a |
243 |
|
* dialog will simulate a click to this button; |
244 |
|
* <code>false</code> for normal appearance. |
245 |
|
* |
246 |
|
* @param shadow the shadow color. |
247 |
|
* @param darkShadow a darker variant of the shadow color. |
248 |
|
* @param highlight the highlight color. |
249 |
|
* @param lightHighlight a brighter variant of the highlight color. |
250 |
|
*/ |
251 |
|
public static void drawBezel(Graphics g, |
252 |
|
int x, int y, int width, int height, |
253 |
|
boolean isPressed, boolean isDefault, |
254 |
|
Color shadow, Color darkShadow, |
255 |
|
Color highlight, Color lightHighlight) |
256 |
|
{ |
257 |
|
Color oldColor = g.getColor(); |
258 |
|
|
259 |
|
/* To understand this, it might be helpful to look at the image |
260 |
|
* "BasicGraphicsUtils-3.png" that is included with the JavaDoc, |
261 |
|
* and to compare it with "BasicGraphicsUtils-1.png" which shows |
262 |
|
* the pixels painted by drawEtchedRect. These image files are |
263 |
|
* located in the "doc-files" subdirectory. |
264 |
|
*/ |
265 |
|
try |
266 |
|
{ |
267 |
|
if ((isPressed == false) && (isDefault == false)) |
268 |
|
{ |
269 |
|
drawEtchedRect(g, x, y, width, height, |
270 |
|
lightHighlight, highlight, |
271 |
|
shadow, darkShadow); |
272 |
|
} |
273 |
|
|
274 |
|
if ((isPressed == true) && (isDefault == false)) |
275 |
|
{ |
276 |
|
g.setColor(shadow); |
277 |
|
g.drawRect(x + 1, y + 1, width - 2, height - 2); |
278 |
|
} |
279 |
|
|
280 |
|
if ((isPressed == false) && (isDefault == true)) |
281 |
|
{ |
282 |
|
g.setColor(darkShadow); |
283 |
|
g.drawRect(x, y, width - 1, height - 1); |
284 |
|
drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2, |
285 |
|
lightHighlight, highlight, |
286 |
|
shadow, darkShadow); |
287 |
|
} |
288 |
|
|
289 |
|
if ((isPressed == true) && (isDefault == true)) |
290 |
|
{ |
291 |
|
g.setColor(darkShadow); |
292 |
|
g.drawRect(x, y, width - 1, height - 1); |
293 |
|
g.setColor(shadow); |
294 |
|
g.drawRect(x + 1, y + 1, width - 3, height - 3); |
295 |
|
} |
296 |
|
} |
297 |
|
finally |
298 |
|
{ |
299 |
|
g.setColor(oldColor); |
300 |
|
} |
301 |
|
} |
302 |
|
|
303 |
|
|
304 |
|
/** |
305 |
|
* Draws a rectangle that appears lowered into the surface, given |
306 |
|
* four colors that are used for drawing. |
307 |
|
* |
308 |
|
* <p><img src="BasicGraphicsUtils-4.png" width="360" |
309 |
|
* height="200" alt="[An illustration that shows which pixels |
310 |
|
* get painted in what color]" /> |
311 |
|
* |
312 |
|
* <p><strong>Compatibility with the Sun reference |
313 |
|
* implementation:</strong> The Sun reference implementation seems |
314 |
|
* to ignore the <code>x</code> and <code>y</code> arguments, at |
315 |
|
* least in JDK 1.3.1 and 1.4.1_01. The method always draws the |
316 |
|
* rectangular area at location (0, 0). A bug report has been filed |
317 |
|
* with Sun; its “bug ID” is 4880003. The GNU Classpath |
318 |
|
* implementation behaves correctly, thus not replicating this bug. |
319 |
|
* |
320 |
|
* @param g the graphics into which the rectangle is drawn. |
321 |
|
* @param x the x coordinate of the rectangle. |
322 |
|
* @param y the y coordinate of the rectangle. |
323 |
|
* @param width the width of the rectangle in pixels. |
324 |
|
* @param height the height of the rectangle in pixels. |
325 |
|
* |
326 |
|
* @param shadow the color that will be used for painting |
327 |
|
* the inner side of the top and left edges. |
328 |
|
* |
329 |
|
* @param darkShadow the color that will be used for painting |
330 |
|
* the outer side of the top and left edges. |
331 |
|
* |
332 |
|
* @param highlight the color that will be used for painting |
333 |
|
* the inner side of the bottom and right edges. |
334 |
|
* |
335 |
|
* @param lightHighlight the color that will be used for painting |
336 |
|
* the outer side of the bottom and right edges. |
337 |
|
*/ |
338 |
|
public static void drawLoweredBezel(Graphics g, |
339 |
|
int x, int y, int width, int height, |
340 |
|
Color shadow, Color darkShadow, |
341 |
|
Color highlight, Color lightHighlight) |
342 |
|
{ |
343 |
|
/* Like drawEtchedRect, but swapping darkShadow and shadow. |
344 |
|
* |
345 |
|
* To understand this, it might be helpful to look at the image |
346 |
|
* "BasicGraphicsUtils-4.png" that is included with the JavaDoc, |
347 |
|
* and to compare it with "BasicGraphicsUtils-1.png" which shows |
348 |
|
* the pixels painted by drawEtchedRect. These image files are |
349 |
|
* located in the "doc-files" subdirectory. |
350 |
|
*/ |
351 |
|
drawEtchedRect(g, x, y, width, height, |
352 |
|
darkShadow, shadow, |
353 |
|
highlight, lightHighlight); |
354 |
|
} |
355 |
|
|
356 |
|
|
357 |
|
/** |
358 |
|
* Draws a String at the given location, underlining the first |
359 |
|
* occurence of a specified character. The algorithm for determining |
360 |
|
* the underlined position is not sensitive to case. If the |
361 |
|
* character is not part of <code>text</code>, the text will be |
362 |
|
* drawn without underlining. Drawing is performed in the current |
363 |
|
* color and font of <code>g</code>. |
364 |
|
* |
365 |
|
* <p><img src="BasicGraphicsUtils-5.png" width="500" |
366 |
|
* height="100" alt="[An illustration showing how to use the |
367 |
|
* method]" /> |
368 |
|
* |
369 |
|
* @param g the graphics into which the String is drawn. |
370 |
|
* |
371 |
|
* @param text the String to draw. |
372 |
|
* |
373 |
|
* @param underlinedChar the character whose first occurence in |
374 |
|
* <code>text</code> will be underlined. It is not clear |
375 |
|
* why the API specification declares this argument to be |
376 |
|
* of type <code>int</code> instead of <code>char</code>. |
377 |
|
* While this would allow to pass Unicode characters outside |
378 |
|
* Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least |
379 |
|
* the GNU Classpath implementation does not underline |
380 |
|
* anything if <code>underlinedChar</code> is outside |
381 |
|
* the range of <code>char</code>. |
382 |
|
* |
383 |
|
* @param x the x coordinate of the text, as it would be passed to |
384 |
|
* {@link java.awt.Graphics#drawString(java.lang.String, |
385 |
|
* int, int)}. |
386 |
|
* |
387 |
|
* @param y the y coordinate of the text, as it would be passed to |
388 |
|
* {@link java.awt.Graphics#drawString(java.lang.String, |
389 |
|
* int, int)}. |
390 |
|
*/ |
391 |
|
public static void drawString(Graphics g, String text, |
392 |
|
int underlinedChar, int x, int y) |
393 |
|
{ |
394 |
|
int index = -1; |
395 |
|
|
396 |
|
/* It is intentional that lower case is used. In some languages, |
397 |
|
* the set of lowercase characters is larger than the set of |
398 |
|
* uppercase ones. Therefore, it is good practice to use lowercase |
399 |
|
* for such comparisons (which really means that the author of this |
400 |
|
* code can vaguely remember having read some Unicode techreport |
401 |
|
* with this recommendation, but is too lazy to look for the URL). |
402 |
|
*/ |
403 |
|
if ((underlinedChar >= 0) || (underlinedChar <= 0xffff)) |
404 |
|
index = text.toLowerCase().indexOf( |
405 |
|
Character.toLowerCase((char) underlinedChar)); |
406 |
|
|
407 |
|
drawStringUnderlineCharAt(g, text, index, x, y); |
408 |
|
} |
409 |
|
|
410 |
|
|
411 |
|
/** |
412 |
|
* Draws a String at the given location, underlining the character |
413 |
|
* at the specified index. Drawing is performed in the current color |
414 |
|
* and font of <code>g</code>. |
415 |
|
* |
416 |
|
* <p><img src="BasicGraphicsUtils-5.png" width="500" |
417 |
|
* height="100" alt="[An illustration showing how to use the |
418 |
|
* method]" /> |
419 |
|
* |
420 |
|
* @param g the graphics into which the String is drawn. |
421 |
|
* |
422 |
|
* @param text the String to draw. |
423 |
|
* |
424 |
|
* @param underlinedIndex the index of the underlined character in |
425 |
|
* <code>text</code>. If <code>underlinedIndex</code> falls |
426 |
|
* outside the range <code>[0, text.length() - 1]</code>, the |
427 |
|
* text will be drawn without underlining anything. |
428 |
|
* |
429 |
|
* @param x the x coordinate of the text, as it would be passed to |
430 |
|
* {@link java.awt.Graphics#drawString(java.lang.String, |
431 |
|
* int, int)}. |
432 |
|
* |
433 |
|
* @param y the y coordinate of the text, as it would be passed to |
434 |
|
* {@link java.awt.Graphics#drawString(java.lang.String, |
435 |
|
* int, int)}. |
436 |
|
* |
437 |
|
* @since 1.4 |
438 |
|
*/ |
439 |
|
public static void drawStringUnderlineCharAt(Graphics g, String text, |
440 |
|
int underlinedIndex, |
441 |
|
int x, int y) |
442 |
|
{ |
443 |
|
Graphics2D g2; |
444 |
|
Rectangle2D.Double underline; |
445 |
|
FontRenderContext frc; |
446 |
|
FontMetrics fmet; |
447 |
|
LineMetrics lineMetrics; |
448 |
|
Font font; |
449 |
|
TextLayout layout; |
450 |
|
double underlineX1, underlineX2; |
451 |
|
boolean drawUnderline; |
452 |
|
int textLength; |
453 |
|
|
454 |
|
textLength = text.length(); |
455 |
|
if (textLength == 0) |
456 |
|
return; |
457 |
|
|
458 |
|
drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength); |
459 |
|
|
460 |
public class BasicGraphicsUtils |
if (!(g instanceof Graphics2D)) |
|
{ |
|
|
public static Dimension getPreferredSize(JComponent b, |
|
|
int gap, |
|
|
String text, |
|
|
Icon icon, |
|
|
int va, |
|
|
int ha, |
|
|
int htp, |
|
|
int vtp) |
|
|
{ |
|
|
JComponent c = b; |
|
|
// this is a staight copy from BasicButtonUI.paint() |
|
|
// |
|
|
Rectangle tr = new Rectangle(); |
|
|
Rectangle ir = new Rectangle(); |
|
|
Rectangle vr = new Rectangle(); |
|
|
|
|
|
Font f = c.getFont(); |
|
|
|
|
|
FontMetrics fm = SwingUtilities.getFontMetrics(f); |
|
|
|
|
|
Insets i = c.getInsets(); |
|
|
|
|
|
vr.x = i.left; |
|
|
vr.y = i.top; |
|
|
vr.width = b.getWidth() - (i.right + i.left); |
|
|
vr.height = b.getHeight() - (i.bottom + i.top); |
|
|
|
|
|
// System.out.println(" VIEW-RECT-BUTTON="+vr+", insets="+i); |
|
|
|
|
|
String tt = SwingUtilities.layoutCompoundLabel(b, |
|
|
fm, |
|
|
text, |
|
|
icon, |
|
|
va, |
|
|
ha, |
|
|
vtp, |
|
|
htp, |
|
|
vr, |
|
|
ir, |
|
|
tr, |
|
|
gap); |
|
|
|
|
|
Rectangle r = ir.union(tr); |
|
|
|
|
|
Insets insets = b.getInsets(); |
|
|
r.width += insets.left + insets.right; |
|
|
r.height += insets.top + insets.bottom; |
|
|
|
|
|
// System.out.println("COMPUTED SIZE FOR PREF_SIZE="+r); |
|
|
|
|
|
return r.getSize(); |
|
|
} |
|
|
|
|
|
public static void drawString(Graphics g, |
|
|
String text, |
|
|
int underlinedChar, |
|
|
int x, |
|
|
int y) |
|
461 |
{ |
{ |
462 |
g.drawString(text, x, y); |
/* Fall-back. This is likely to produce garbage for any text |
463 |
|
* containing right-to-left (Hebrew or Arabic) characters, even |
464 |
|
* if the underlined character is left-to-right. |
465 |
|
*/ |
466 |
|
g.drawString(text, x, y); |
467 |
|
if (drawUnderline) |
468 |
|
{ |
469 |
|
fmet = g.getFontMetrics(); |
470 |
|
g.fillRect( |
471 |
|
/* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)), |
472 |
|
/* y */ y + fmet.getDescent() - 1, |
473 |
|
/* width */ fmet.charWidth(text.charAt(underlinedIndex)), |
474 |
|
/* height */ 1); |
475 |
|
} |
476 |
|
|
477 |
|
return; |
478 |
} |
} |
|
} |
|
479 |
|
|
480 |
|
g2 = (Graphics2D) g; |
481 |
|
font = g2.getFont(); |
482 |
|
frc = g2.getFontRenderContext(); |
483 |
|
lineMetrics = font.getLineMetrics(text, frc); |
484 |
|
layout = new TextLayout(text, font, frc); |
485 |
|
|
486 |
|
/* Draw the text. */ |
487 |
|
layout.draw(g2, x, y); |
488 |
|
if (!drawUnderline) |
489 |
|
return; |
490 |
|
|
491 |
|
underlineX1 = x + layout.getLogicalHighlightShape( |
492 |
|
underlinedIndex, underlinedIndex).getBounds2D().getX(); |
493 |
|
underlineX2 = x + layout.getLogicalHighlightShape( |
494 |
|
underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX(); |
495 |
|
|
496 |
|
underline = new Rectangle2D.Double(); |
497 |
|
if (underlineX1 < underlineX2) |
498 |
|
{ |
499 |
|
underline.x = underlineX1; |
500 |
|
underline.width = underlineX2 - underlineX1; |
501 |
|
} |
502 |
|
else |
503 |
|
{ |
504 |
|
underline.x = underlineX2; |
505 |
|
underline.width = underlineX1 - underlineX2; |
506 |
|
} |
507 |
|
|
508 |
|
|
509 |
|
underline.height = lineMetrics.getUnderlineThickness(); |
510 |
|
underline.y = lineMetrics.getUnderlineOffset(); |
511 |
|
if (underline.y == 0) |
512 |
|
{ |
513 |
|
/* Some fonts do not specify an underline offset, although they |
514 |
|
* actually should do so. In that case, the result of calling |
515 |
|
* lineMetrics.getUnderlineOffset() will be zero. Since it would |
516 |
|
* look very ugly if the underline was be positioned immediately |
517 |
|
* below the baseline, we check for this and move the underline |
518 |
|
* below the descent, as shown in the following ASCII picture: |
519 |
|
* |
520 |
|
* ##### ##### # |
521 |
|
* # # # # |
522 |
|
* # # # # |
523 |
|
* # # # # |
524 |
|
* ##### ###### ---- baseline (0) |
525 |
|
* # |
526 |
|
* # |
527 |
|
* ------------------###----------- lineMetrics.getDescent() |
528 |
|
*/ |
529 |
|
underline.y = lineMetrics.getDescent(); |
530 |
|
} |
531 |
|
|
532 |
|
underline.y += y; |
533 |
|
g2.fill(underline); |
534 |
|
} |
535 |
|
|
536 |
|
|
537 |
|
/** |
538 |
|
* Draws a rectangle, simulating a dotted stroke by painting only |
539 |
|
* every second pixel along the one-pixel thick edge. The color of |
540 |
|
* those pixels is the current color of the Graphics <code>g</code>. |
541 |
|
* Any other pixels are left unchanged. |
542 |
|
* |
543 |
|
* <p><img src="BasicGraphicsUtils-7.png" width="360" |
544 |
|
* height="200" alt="[An illustration that shows which pixels |
545 |
|
* get painted]" /> |
546 |
|
* |
547 |
|
* @param g the graphics into which the rectangle is drawn. |
548 |
|
* @param x the x coordinate of the rectangle. |
549 |
|
* @param y the y coordinate of the rectangle. |
550 |
|
* @param width the width of the rectangle in pixels. |
551 |
|
* @param height the height of the rectangle in pixels. |
552 |
|
*/ |
553 |
|
public static void drawDashedRect(Graphics g, |
554 |
|
int x, int y, int width, int height) |
555 |
|
{ |
556 |
|
int right = x + width - 1; |
557 |
|
int bottom = y + height - 1; |
558 |
|
|
559 |
|
/* Draw the top and bottom edge of the dotted rectangle. */ |
560 |
|
for (int i = x; i <= right; i += 2) |
561 |
|
{ |
562 |
|
g.drawLine(i, y, i, y); |
563 |
|
g.drawLine(i, bottom, i, bottom); |
564 |
|
} |
565 |
|
|
566 |
|
/* Draw the left and right edge of the dotted rectangle. */ |
567 |
|
for (int i = y; i <= bottom; i += 2) |
568 |
|
{ |
569 |
|
g.drawLine(x, i, x, i); |
570 |
|
g.drawLine(right, i, right, i); |
571 |
|
} |
572 |
|
} |
573 |
|
|
574 |
|
|
575 |
|
/** |
576 |
|
* Determines the preferred width and height of an AbstractButton, |
577 |
|
* given the gap between the button’s text and icon. |
578 |
|
* |
579 |
|
* @param b the button whose preferred size is determined. |
580 |
|
* |
581 |
|
* @param textIconGap the gap between the button’s text and |
582 |
|
* icon. |
583 |
|
* |
584 |
|
* @return a <code>Dimension</code> object whose <code>width</code> |
585 |
|
* and <code>height</code> fields indicate the preferred |
586 |
|
* extent in pixels. |
587 |
|
* |
588 |
|
* @see javax.swing.SwingUtilities#layoutCompoundLabel |
589 |
|
*/ |
590 |
|
public static Dimension getPreferredButtonSize(AbstractButton b, |
591 |
|
int textIconGap) |
592 |
|
{ |
593 |
|
Rectangle contentRect; |
594 |
|
Rectangle viewRect; |
595 |
|
Rectangle iconRect = new Rectangle(); |
596 |
|
Rectangle textRect = new Rectangle(); |
597 |
|
Insets insets = b.getInsets(); |
598 |
|
|
599 |
|
/* For determining the ideal size, do not assume a size restriction. */ |
600 |
|
viewRect = new Rectangle(0, 0, |
601 |
|
/* width */ Integer.MAX_VALUE, |
602 |
|
/* height */ Integer.MAX_VALUE); |
603 |
|
|
604 |
|
/* java.awt.Toolkit.getFontMetrics is deprecated. However, it |
605 |
|
* seems not obvious how to get to the correct FontMetrics object |
606 |
|
* otherwise. The real problem probably is that the method |
607 |
|
* javax.swing.SwingUtilities.layoutCompundLabel should take a |
608 |
|
* LineMetrics, not a FontMetrics argument. But fixing this that |
609 |
|
* would change the public API. |
610 |
|
*/ |
611 |
|
SwingUtilities.layoutCompoundLabel( |
612 |
|
b, // for the component orientation |
613 |
|
b.getToolkit().getFontMetrics(b.getFont()), // see comment above |
614 |
|
b.getText(), |
615 |
|
b.getIcon(), |
616 |
|
b.getVerticalAlignment(), |
617 |
|
b.getHorizontalAlignment(), |
618 |
|
b.getVerticalTextPosition(), |
619 |
|
b.getHorizontalTextPosition(), |
620 |
|
viewRect, iconRect, textRect, |
621 |
|
textIconGap); |
622 |
|
|
623 |
|
|
624 |
|
/* +------------------------+ +------------------------+ |
625 |
|
* | | | | |
626 |
|
* | ICON | | CONTENTCONTENTCONTENT | |
627 |
|
* | TEXTTEXTTEXT | --> | CONTENTCONTENTCONTENT | |
628 |
|
* | TEXTTEXTTEXT | | CONTENTCONTENTCONTENT | |
629 |
|
* +------------------------+ +------------------------+ |
630 |
|
*/ |
631 |
|
contentRect = textRect.union(iconRect); |
632 |
|
|
633 |
|
return new Dimension(insets.left + contentRect.width + insets.right, |
634 |
|
insets.top + contentRect.height + insets.bottom); |
635 |
|
} |
636 |
|
} |