blob: f0165be3ea9eabdc3fb66f3ac459bf080da38f5a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 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 */
25
26package sun.swing;
27
28import java.security.*;
29import java.lang.reflect.*;
30import java.lang.ref.SoftReference;
31import java.awt.*;
32import static java.awt.RenderingHints.*;
33import java.awt.event.*;
34import java.awt.font.*;
35import java.awt.geom.*;
36import java.awt.print.PrinterGraphics;
37import java.text.Bidi;
38import java.text.AttributedCharacterIterator;
39import java.text.AttributedString;
40
41import javax.swing.*;
42import javax.swing.plaf.*;
43import javax.swing.text.Highlighter;
44import javax.swing.text.JTextComponent;
45import javax.swing.text.DefaultHighlighter;
46import javax.swing.text.DefaultCaret;
47import javax.swing.table.TableCellRenderer;
48import sun.swing.PrintColorUIResource;
49import sun.swing.ImageIconUIResource;
50import sun.print.ProxyPrintGraphics;
51import sun.awt.*;
52import sun.security.action.GetPropertyAction;
53import sun.security.util.SecurityConstants;
54import java.io.*;
55import java.util.*;
56import sun.font.FontDesignMetrics;
57import sun.font.FontManager;
58
59import java.util.concurrent.Callable;
60import java.util.concurrent.Future;
61import java.util.concurrent.FutureTask;
62
63/**
64 * A collection of utility methods for Swing.
65 * <p>
66 * <b>WARNING:</b> While this class is public, it should not be treated as
67 * public API and its API may change in incompatable ways between dot dot
68 * releases and even patch releases. You should not rely on this class even
69 * existing.
70 *
71 */
72public class SwingUtilities2 {
73 /**
74 * The <code>AppContext</code> key for our one <code>LAFState</code>
75 * instance.
76 */
77 public static final Object LAF_STATE_KEY =
78 new StringBuffer("LookAndFeel State");
79
80 // Most of applications use 10 or less fonts simultaneously
81 private static final int STRONG_BEARING_CACHE_SIZE = 10;
82 // Strong cache for the left and right side bearings
83 // for STRONG_BEARING_CACHE_SIZE most recently used fonts.
84 private static BearingCacheEntry[] strongBearingCache =
85 new BearingCacheEntry[STRONG_BEARING_CACHE_SIZE];
86 // Next index to insert an entry into the strong bearing cache
87 private static int strongBearingCacheNextIndex = 0;
88 // Soft cache for the left and right side bearings
89 private static Set<SoftReference<BearingCacheEntry>> softBearingCache =
90 new HashSet<SoftReference<BearingCacheEntry>>();
91
92 public static final FontRenderContext DEFAULT_FRC =
93 new FontRenderContext(null, false, false);
94
95 /**
96 * A JComponent client property is used to determine text aa settings.
97 * To avoid having this property persist between look and feels changes
98 * the value of the property is set to null in JComponent.setUI
99 */
100 public static final Object AA_TEXT_PROPERTY_KEY =
101 new StringBuffer("AATextInfoPropertyKey");
102
103 /**
104 * Used to tell a text component, being used as an editor for table
105 * or tree, how many clicks it took to start editing.
106 */
107 private static final StringBuilder SKIP_CLICK_COUNT =
108 new StringBuilder("skipClickCount");
109
110 /* Presently this class assumes default fractional metrics.
111 * This may need to change to emulate future platform L&Fs.
112 */
113 public static class AATextInfo {
114
115 private static AATextInfo getAATextInfoFromMap(Map hints) {
116
117 Object aaHint = hints.get(KEY_TEXT_ANTIALIASING);
118 Object contHint = hints.get(KEY_TEXT_LCD_CONTRAST);
119
120 if (aaHint == null ||
121 aaHint == VALUE_TEXT_ANTIALIAS_OFF ||
122 aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) {
123 return null;
124 } else {
125 return new AATextInfo(aaHint, (Integer)contHint);
126 }
127 }
128
129 public static AATextInfo getAATextInfo(boolean lafCondition) {
130 SunToolkit.setAAFontSettingsCondition(lafCondition);
131 Toolkit tk = Toolkit.getDefaultToolkit();
132 Object map = tk.getDesktopProperty(SunToolkit.DESKTOPFONTHINTS);
133 if (map instanceof Map) {
134 return getAATextInfoFromMap((Map)map);
135 } else {
136 return null;
137 }
138 }
139
140 Object aaHint;
141 Integer lcdContrastHint;
142 FontRenderContext frc;
143
144 /* These are rarely constructed objects, and only when a complete
145 * UI is being updated, so the cost of the tests here is minimal
146 * and saves tests elsewhere.
147 * We test that the values are ones we support/expect.
148 */
149 public AATextInfo(Object aaHint, Integer lcdContrastHint) {
150 if (aaHint == null) {
151 throw new InternalError("null not allowed here");
152 }
153 if (aaHint == VALUE_TEXT_ANTIALIAS_OFF ||
154 aaHint == VALUE_TEXT_ANTIALIAS_DEFAULT) {
155 throw new InternalError("AA must be on");
156 }
157 this.aaHint = aaHint;
158 this.lcdContrastHint = lcdContrastHint;
159 this.frc = new FontRenderContext(null, aaHint,
160 VALUE_FRACTIONALMETRICS_DEFAULT);
161 }
162 }
163
164 /**
165 * Key used in client properties used to indicate that the
166 * <code>ComponentUI</code> of the JComponent instance should be returned.
167 */
168 public static final Object COMPONENT_UI_PROPERTY_KEY =
169 new StringBuffer("ComponentUIPropertyKey");
170
171 /** Client Property key for the text maximal offsets for BasicMenuItemUI */
172 public static final StringUIClientPropertyKey BASICMENUITEMUI_MAX_TEXT_OFFSET =
173 new StringUIClientPropertyKey ("maxTextOffset");
174
175 // security stuff
176 private static Field inputEvent_CanAccessSystemClipboard_Field = null;
177 private static final String UntrustedClipboardAccess =
178 "UNTRUSTED_CLIPBOARD_ACCESS_KEY";
179
180 //all access to charsBuffer is to be synchronized on charsBufferLock
181 private static final int CHAR_BUFFER_SIZE = 100;
182 private static final Object charsBufferLock = new Object();
183 private static char[] charsBuffer = new char[CHAR_BUFFER_SIZE];
184
185 /**
186 * checks whether TextLayout is required to handle characters.
187 *
188 * @param text characters to be tested
189 * @param start start
190 * @param limit limit
191 * @return <tt>true</tt> if TextLayout is required
192 * <tt>false</tt> if TextLayout is not required
193 */
194 public static final boolean isComplexLayout(char[] text, int start, int limit) {
195 return FontManager.isComplexText(text, start, limit);
196 }
197
198 //
199 // WARNING WARNING WARNING WARNING WARNING WARNING
200 // Many of the following methods are invoked from older API.
201 // As this older API was not passed a Component, a null Component may
202 // now be passsed in. For example, SwingUtilities.computeStringWidth
203 // is implemented to call SwingUtilities2.stringWidth, the
204 // SwingUtilities variant does not take a JComponent, as such
205 // SwingUtilities2.stringWidth can be passed a null Component.
206 // In other words, if you add new functionality to these methods you
207 // need to gracefully handle null.
208 //
209
210 /**
211 * Returns whether or not text should be drawn antialiased.
212 *
213 * @param c JComponent to test.
214 * @return Whether or not text should be drawn antialiased for the
215 * specified component.
216 */
217 public static AATextInfo drawTextAntialiased(JComponent c) {
218 if (c != null) {
219 /* a non-null property implies some form of AA requested */
220 return (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY);
221 }
222 // No component, assume aa is off
223 return null;
224 }
225
226 /**
227 * Returns the left side bearing of the first character of string. The
228 * left side bearing is calculated from the passed in FontMetrics.
229 *
230 * @param c JComponent that will display the string
231 * @param fm FontMetrics used to measure the String width
232 * @param string String to get the left side bearing for.
233 */
234 public static int getLeftSideBearing(JComponent c, FontMetrics fm,
235 String string) {
236 if ((string == null) || (string.length() == 0)) {
237 return 0;
238 }
239 return getLeftSideBearing(c, fm, string.charAt(0));
240 }
241
242 /**
243 * Returns the left side bearing of the specified character. The
244 * left side bearing is calculated from the passed in FontMetrics.
245 *
246 * @param c JComponent that will display the string
247 * @param fm FontMetrics used to measure the String width
248 * @param firstChar Character to get the left side bearing for.
249 */
250 public static int getLeftSideBearing(JComponent c, FontMetrics fm,
251 char firstChar) {
252 return getBearing(c, fm, firstChar, true);
253 }
254
255 /**
256 * Returns the right side bearing of the last character of string. The
257 * right side bearing is calculated from the passed in FontMetrics.
258 *
259 * @param c JComponent that will display the string
260 * @param fm FontMetrics used to measure the String width
261 * @param string String to get the right side bearing for.
262 */
263 public static int getRightSideBearing(JComponent c, FontMetrics fm,
264 String string) {
265 if ((string == null) || (string.length() == 0)) {
266 return 0;
267 }
268 return getRightSideBearing(c, fm, string.charAt(string.length() - 1));
269 }
270
271 /**
272 * Returns the right side bearing of the specified character. The
273 * right side bearing is calculated from the passed in FontMetrics.
274 *
275 * @param c JComponent that will display the string
276 * @param fm FontMetrics used to measure the String width
277 * @param lastChar Character to get the right side bearing for.
278 */
279 public static int getRightSideBearing(JComponent c, FontMetrics fm,
280 char lastChar) {
281 return getBearing(c, fm, lastChar, false);
282 }
283
284 /* Calculates the left and right side bearing for a character.
285 * Strongly caches bearings for STRONG_BEARING_CACHE_SIZE
286 * most recently used Fonts and softly caches as many as GC allows.
287 */
288 private static int getBearing(JComponent comp, FontMetrics fm, char c,
289 boolean isLeftBearing) {
290 if (fm == null) {
291 if (comp == null) {
292 return 0;
293 } else {
294 fm = comp.getFontMetrics(comp.getFont());
295 }
296 }
297 synchronized (SwingUtilities2.class) {
298 BearingCacheEntry entry = null;
299 BearingCacheEntry searchKey = new BearingCacheEntry(fm);
300 // See if we already have an entry in the strong cache
301 for (BearingCacheEntry cacheEntry : strongBearingCache) {
302 if (searchKey.equals(cacheEntry)) {
303 entry = cacheEntry;
304 break;
305 }
306 }
307 // See if we already have an entry in the soft cache
308 if (entry == null) {
309 Iterator<SoftReference<BearingCacheEntry>> iter =
310 softBearingCache.iterator();
311 while (iter.hasNext()) {
312 BearingCacheEntry cacheEntry = iter.next().get();
313 if (cacheEntry == null) {
314 // Remove discarded soft reference from the cache
315 iter.remove();
316 continue;
317 }
318 if (searchKey.equals(cacheEntry)) {
319 entry = cacheEntry;
320 putEntryInStrongCache(entry);
321 break;
322 }
323 }
324 }
325 if (entry == null) {
326 // No entry, add it
327 entry = searchKey;
328 cacheEntry(entry);
329 }
330 return (isLeftBearing)
331 ? entry.getLeftSideBearing(c)
332 : entry.getRightSideBearing(c);
333 }
334 }
335
336 private synchronized static void cacheEntry(BearingCacheEntry entry) {
337 // Move the oldest entry from the strong cache into the soft cache
338 BearingCacheEntry oldestEntry =
339 strongBearingCache[strongBearingCacheNextIndex];
340 if (oldestEntry != null) {
341 softBearingCache.add(new SoftReference<BearingCacheEntry>(oldestEntry));
342 }
343 // Put entry in the strong cache
344 putEntryInStrongCache(entry);
345 }
346
347 private synchronized static void putEntryInStrongCache(BearingCacheEntry entry) {
348 strongBearingCache[strongBearingCacheNextIndex] = entry;
349 strongBearingCacheNextIndex = (strongBearingCacheNextIndex + 1)
350 % STRONG_BEARING_CACHE_SIZE;
351 }
352
353 /**
354 * Returns the FontMetrics for the current Font of the passed
355 * in Graphics. This method is used when a Graphics
356 * is available, typically when painting. If a Graphics is not
357 * available the JComponent method of the same name should be used.
358 * <p>
359 * Callers should pass in a non-null JComponent, the exception
360 * to this is if a JComponent is not readily available at the time of
361 * painting.
362 * <p>
363 * This does not necessarily return the FontMetrics from the
364 * Graphics.
365 *
366 * @param c JComponent requesting FontMetrics, may be null
367 * @param g Graphics Graphics
368 */
369 public static FontMetrics getFontMetrics(JComponent c, Graphics g) {
370 return getFontMetrics(c, g, g.getFont());
371 }
372
373
374 /**
375 * Returns the FontMetrics for the specified Font.
376 * This method is used when a Graphics is available, typically when
377 * painting. If a Graphics is not available the JComponent method of
378 * the same name should be used.
379 * <p>
380 * Callers should pass in a non-null JComonent, the exception
381 * to this is if a JComponent is not readily available at the time of
382 * painting.
383 * <p>
384 * This does not necessarily return the FontMetrics from the
385 * Graphics.
386 *
387 * @param c JComponent requesting FontMetrics, may be null
388 * @param c Graphics Graphics
389 * @param font Font to get FontMetrics for
390 */
391 public static FontMetrics getFontMetrics(JComponent c, Graphics g,
392 Font font) {
393 if (c != null) {
394 // Note: We assume that we're using the FontMetrics
395 // from the widget to layout out text, otherwise we can get
396 // mismatches when printing.
397 return c.getFontMetrics(font);
398 }
399 return Toolkit.getDefaultToolkit().getFontMetrics(font);
400 }
401
402
403 /**
404 * Returns the width of the passed in String.
405 * If the passed String is <code>null</code>, returns zero.
406 *
407 * @param c JComponent that will display the string, may be null
408 * @param fm FontMetrics used to measure the String width
409 * @param string String to get the width of
410 */
411 public static int stringWidth(JComponent c, FontMetrics fm, String string){
412 if (string == null || string.equals("")) {
413 return 0;
414 }
415 return fm.stringWidth(string);
416 }
417
418
419 /**
420 * Clips the passed in String to the space provided.
421 *
422 * @param c JComponent that will display the string, may be null
423 * @param fm FontMetrics used to measure the String width
424 * @param string String to display
425 * @param availTextWidth Amount of space that the string can be drawn in
426 * @return Clipped string that can fit in the provided space.
427 */
428 public static String clipStringIfNecessary(JComponent c, FontMetrics fm,
429 String string,
430 int availTextWidth) {
431 if ((string == null) || (string.equals(""))) {
432 return "";
433 }
434 int textWidth = SwingUtilities2.stringWidth(c, fm, string);
435 if (textWidth > availTextWidth) {
436 return SwingUtilities2.clipString(c, fm, string, availTextWidth);
437 }
438 return string;
439 }
440
441
442 /**
443 * Clips the passed in String to the space provided. NOTE: this assumes
444 * the string does not fit in the available space.
445 *
446 * @param c JComponent that will display the string, may be null
447 * @param fm FontMetrics used to measure the String width
448 * @param string String to display
449 * @param availTextWidth Amount of space that the string can be drawn in
450 * @return Clipped string that can fit in the provided space.
451 */
452 public static String clipString(JComponent c, FontMetrics fm,
453 String string, int availTextWidth) {
454 // c may be null here.
455 String clipString = "...";
456 int stringLength = string.length();
457 availTextWidth -= SwingUtilities2.stringWidth(c, fm, clipString);
458 if (availTextWidth <= 0) {
459 //can not fit any characters
460 return clipString;
461 }
462
463 boolean needsTextLayout = false;
464
465 synchronized (charsBufferLock) {
466 if (charsBuffer == null || charsBuffer.length < stringLength) {
467 charsBuffer = string.toCharArray();
468 } else {
469 string.getChars(0, stringLength, charsBuffer, 0);
470 }
471 needsTextLayout =
472 isComplexLayout(charsBuffer, 0, stringLength);
473 if (!needsTextLayout) {
474 int width = 0;
475 for (int nChars = 0; nChars < stringLength; nChars++) {
476 width += fm.charWidth(charsBuffer[nChars]);
477 if (width > availTextWidth) {
478 string = string.substring(0, nChars);
479 break;
480 }
481 }
482 }
483 }
484 if (needsTextLayout) {
485 FontRenderContext frc = getFontRenderContext(c, fm);
486 AttributedString aString = new AttributedString(string);
487 LineBreakMeasurer measurer =
488 new LineBreakMeasurer(aString.getIterator(), frc);
489 int nChars = measurer.nextOffset(availTextWidth);
490 string = string.substring(0, nChars);
491
492 }
493 return string + clipString;
494 }
495
496
497 /**
498 * Draws the string at the specified location.
499 *
500 * @param c JComponent that will display the string, may be null
501 * @param g Graphics to draw the text to
502 * @param text String to display
503 * @param x X coordinate to draw the text at
504 * @param y Y coordinate to draw the text at
505 */
506 public static void drawString(JComponent c, Graphics g, String text,
507 int x, int y) {
508 // c may be null
509
510 // All non-editable widgets that draw strings call into this
511 // methods. By non-editable that means widgets like JLabel, JButton
512 // but NOT JTextComponents.
513 if ( text == null || text.length() <= 0 ) { //no need to paint empty strings
514 return;
515 }
516 if (isPrinting(g)) {
517 Graphics2D g2d = getGraphics2D(g);
518 if (g2d != null) {
519 /* The printed text must scale linearly with the UI.
520 * Calculate the width on screen, obtain a TextLayout with
521 * advances for the printer graphics FRC, and then justify
522 * it to fit in the screen width. This distributes the spacing
523 * more evenly than directly laying out to the screen advances.
524 */
525 float screenWidth = (float)
526 g2d.getFont().getStringBounds(text, DEFAULT_FRC).getWidth();
527 TextLayout layout = new TextLayout(text, g2d.getFont(),
528 g2d.getFontRenderContext());
529
530 layout = layout.getJustifiedLayout(screenWidth);
531 /* Use alternate print color if specified */
532 Color col = g2d.getColor();
533 if (col instanceof PrintColorUIResource) {
534 g2d.setColor(((PrintColorUIResource)col).getPrintColor());
535 }
536
537 layout.draw(g2d, x, y);
538
539 g2d.setColor(col);
540
541 return;
542 }
543 }
544
545 // If we get here we're not printing
546 AATextInfo info = drawTextAntialiased(c);
547 if (info != null && (g instanceof Graphics2D)) {
548 Graphics2D g2 = (Graphics2D)g;
549
550 Object oldContrast = null;
551 Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
552 if (info.aaHint != oldAAValue) {
553 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, info.aaHint);
554 } else {
555 oldAAValue = null;
556 }
557 if (info.lcdContrastHint != null) {
558 oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);
559 if (info.lcdContrastHint.equals(oldContrast)) {
560 oldContrast = null;
561 } else {
562 g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,
563 info.lcdContrastHint);
564 }
565 }
566
567 g.drawString(text, x, y);
568
569 if (oldAAValue != null) {
570 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
571 }
572 if (oldContrast != null) {
573 g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);
574 }
575 }
576 else {
577 g.drawString(text, x, y);
578 }
579 }
580
581
582 /**
583 * Draws the string at the specified location underlining the specified
584 * character.
585 *
586 * @param c JComponent that will display the string, may be null
587 * @param g Graphics to draw the text to
588 * @param text String to display
589 * @param underlinedIndex Index of a character in the string to underline
590 * @param x X coordinate to draw the text at
591 * @param y Y coordinate to draw the text at
592 */
593 public static void drawStringUnderlineCharAt(JComponent c,Graphics g,
594 String text, int underlinedIndex, int x,int y) {
595 if (text == null || text.length() <= 0) {
596 return;
597 }
598 SwingUtilities2.drawString(c, g, text, x, y);
599 int textLength = text.length();
600 if (underlinedIndex >= 0 && underlinedIndex < textLength ) {
601 int underlineRectY = y;
602 int underlineRectHeight = 1;
603 int underlineRectX = 0;
604 int underlineRectWidth = 0;
605 boolean isPrinting = isPrinting(g);
606 boolean needsTextLayout = isPrinting;
607 if (!needsTextLayout) {
608 synchronized (charsBufferLock) {
609 if (charsBuffer == null || charsBuffer.length < textLength) {
610 charsBuffer = text.toCharArray();
611 } else {
612 text.getChars(0, textLength, charsBuffer, 0);
613 }
614 needsTextLayout =
615 isComplexLayout(charsBuffer, 0, textLength);
616 }
617 }
618 if (!needsTextLayout) {
619 FontMetrics fm = g.getFontMetrics();
620 underlineRectX = x +
621 SwingUtilities2.stringWidth(c,fm,
622 text.substring(0,underlinedIndex));
623 underlineRectWidth = fm.charWidth(text.
624 charAt(underlinedIndex));
625 } else {
626 Graphics2D g2d = getGraphics2D(g);
627 if (g2d != null) {
628 TextLayout layout =
629 new TextLayout(text, g2d.getFont(),
630 g2d.getFontRenderContext());
631 if (isPrinting) {
632 float screenWidth = (float)g2d.getFont().
633 getStringBounds(text, DEFAULT_FRC).getWidth();
634 layout = layout.getJustifiedLayout(screenWidth);
635 }
636 TextHitInfo leading =
637 TextHitInfo.leading(underlinedIndex);
638 TextHitInfo trailing =
639 TextHitInfo.trailing(underlinedIndex);
640 Shape shape =
641 layout.getVisualHighlightShape(leading, trailing);
642 Rectangle rect = shape.getBounds();
643 underlineRectX = x + rect.x;
644 underlineRectWidth = rect.width;
645 }
646 }
647 g.fillRect(underlineRectX, underlineRectY + 1,
648 underlineRectWidth, underlineRectHeight);
649 }
650 }
651
652
653 /**
654 * A variation of locationToIndex() which only returns an index if the
655 * Point is within the actual bounds of a list item (not just in the cell)
656 * and if the JList has the "List.isFileList" client property set.
657 * Otherwise, this method returns -1.
658 * This is used to make WindowsL&F JFileChooser act like native dialogs.
659 */
660 public static int loc2IndexFileList(JList list, Point point) {
661 int index = list.locationToIndex(point);
662 if (index != -1) {
663 Object bySize = list.getClientProperty("List.isFileList");
664 if (bySize instanceof Boolean && ((Boolean)bySize).booleanValue() &&
665 !pointIsInActualBounds(list, index, point)) {
666 index = -1;
667 }
668 }
669 return index;
670 }
671
672
673 /**
674 * Returns true if the given point is within the actual bounds of the
675 * JList item at index (not just inside the cell).
676 */
677 private static boolean pointIsInActualBounds(JList list, int index,
678 Point point) {
679 ListCellRenderer renderer = list.getCellRenderer();
680 ListModel dataModel = list.getModel();
681 Object value = dataModel.getElementAt(index);
682 Component item = renderer.getListCellRendererComponent(list,
683 value, index, false, false);
684 Dimension itemSize = item.getPreferredSize();
685 Rectangle cellBounds = list.getCellBounds(index, index);
686 if (!item.getComponentOrientation().isLeftToRight()) {
687 cellBounds.x += (cellBounds.width - itemSize.width);
688 }
689 cellBounds.width = itemSize.width;
690
691 return cellBounds.contains(point);
692 }
693
694
695 /**
696 * Returns true if the given point is outside the preferredSize of the
697 * item at the given row of the table. (Column must be 0).
698 * Does not check the "Table.isFileList" property. That should be checked
699 * before calling this method.
700 * This is used to make WindowsL&F JFileChooser act like native dialogs.
701 */
702 public static boolean pointOutsidePrefSize(JTable table, int row, int column, Point p) {
703 if (table.convertColumnIndexToModel(column) != 0 || row == -1) {
704 return true;
705 }
706 TableCellRenderer tcr = table.getCellRenderer(row, column);
707 Object value = table.getValueAt(row, column);
708 Component cell = tcr.getTableCellRendererComponent(table, value, false,
709 false, row, column);
710 Dimension itemSize = cell.getPreferredSize();
711 Rectangle cellBounds = table.getCellRect(row, column, false);
712 cellBounds.width = itemSize.width;
713 cellBounds.height = itemSize.height;
714
715 // See if coords are inside
716 // ASSUME: mouse x,y will never be < cell's x,y
717 assert (p.x >= cellBounds.x && p.y >= cellBounds.y);
718 if (p.x > cellBounds.x + cellBounds.width ||
719 p.y > cellBounds.y + cellBounds.height) {
720 return true;
721 }
722 return false;
723 }
724
725 /**
726 * Set the lead and anchor without affecting selection.
727 */
728 public static void setLeadAnchorWithoutSelection(ListSelectionModel model,
729 int lead, int anchor) {
730 if (anchor == -1) {
731 anchor = lead;
732 }
733 if (lead == -1) {
734 model.setAnchorSelectionIndex(-1);
735 model.setLeadSelectionIndex(-1);
736 } else {
737 if (model.isSelectedIndex(lead)) {
738 model.addSelectionInterval(lead, lead);
739 } else {
740 model.removeSelectionInterval(lead, lead);
741 }
742 model.setAnchorSelectionIndex(anchor);
743 }
744 }
745
746 /**
747 * Ignore mouse events if the component is null, not enabled, the event
748 * is not associated with the left mouse button, or the event has been
749 * consumed.
750 */
751 public static boolean shouldIgnore(MouseEvent me, JComponent c) {
752 return c == null || !c.isEnabled()
753 || !SwingUtilities.isLeftMouseButton(me)
754 || me.isConsumed();
755 }
756
757 /**
758 * Request focus on the given component if it doesn't already have it
759 * and <code>isRequestFocusEnabled()</code> returns true.
760 */
761 public static void adjustFocus(JComponent c) {
762 if (!c.hasFocus() && c.isRequestFocusEnabled()) {
763 c.requestFocus();
764 }
765 }
766
767 /**
768 * The following draw functions have the same semantic as the
769 * Graphics methods with the same names.
770 *
771 * this is used for printing
772 */
773 public static int drawChars(JComponent c, Graphics g,
774 char[] data,
775 int offset,
776 int length,
777 int x,
778 int y) {
779 if ( length <= 0 ) { //no need to paint empty strings
780 return x;
781 }
782 int nextX = x + getFontMetrics(c, g).charsWidth(data, offset, length);
783 if (isPrinting(g)) {
784 Graphics2D g2d = getGraphics2D(g);
785 if (g2d != null) {
786 FontRenderContext deviceFontRenderContext = g2d.
787 getFontRenderContext();
788 FontRenderContext frc = getFontRenderContext(c);
789 if (frc != null &&
790 !isFontRenderContextPrintCompatible
791 (deviceFontRenderContext, frc)) {
792 TextLayout layout =
793 new TextLayout(new String(data,offset,length),
794 g2d.getFont(),
795 deviceFontRenderContext);
796 float screenWidth = (float)g2d.getFont().
797 getStringBounds(data, offset, offset + length, frc).
798 getWidth();
799 layout = layout.getJustifiedLayout(screenWidth);
800
801 /* Use alternate print color if specified */
802 Color col = g2d.getColor();
803 if (col instanceof PrintColorUIResource) {
804 g2d.setColor(((PrintColorUIResource)col).getPrintColor());
805 }
806
807 layout.draw(g2d,x,y);
808
809 g2d.setColor(col);
810
811 return nextX;
812 }
813 }
814 }
815 // Assume we're not printing if we get here, or that we are invoked
816 // via Swing text printing which is laid out for the printer.
817 AATextInfo info = drawTextAntialiased(c);
818 if (info != null && (g instanceof Graphics2D)) {
819 Graphics2D g2 = (Graphics2D)g;
820
821 Object oldContrast = null;
822 Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
823 if (info.aaHint != null && info.aaHint != oldAAValue) {
824 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, info.aaHint);
825 } else {
826 oldAAValue = null;
827 }
828 if (info.lcdContrastHint != null) {
829 oldContrast = g2.getRenderingHint(KEY_TEXT_LCD_CONTRAST);
830 if (info.lcdContrastHint.equals(oldContrast)) {
831 oldContrast = null;
832 } else {
833 g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST,
834 info.lcdContrastHint);
835 }
836 }
837
838 g.drawChars(data, offset, length, x, y);
839
840 if (oldAAValue != null) {
841 g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
842 }
843 if (oldContrast != null) {
844 g2.setRenderingHint(KEY_TEXT_LCD_CONTRAST, oldContrast);
845 }
846 }
847 else {
848 g.drawChars(data, offset, length, x, y);
849 }
850 return nextX;
851 }
852
853 /*
854 * see documentation for drawChars
855 * returns the advance
856 */
857 public static float drawString(JComponent c, Graphics g,
858 AttributedCharacterIterator iterator,
859 int x,
860 int y) {
861
862 float retVal;
863 boolean isPrinting = isPrinting(g);
864 Color col = g.getColor();
865
866 if (isPrinting) {
867 /* Use alternate print color if specified */
868 if (col instanceof PrintColorUIResource) {
869 g.setColor(((PrintColorUIResource)col).getPrintColor());
870 }
871 }
872
873 Graphics2D g2d = getGraphics2D(g);
874 if (g2d == null) {
875 g.drawString(iterator,x,y); //for the cases where advance
876 //matters it should not happen
877 retVal = x;
878
879 } else {
880 FontRenderContext frc;
881 if (isPrinting) {
882 frc = getFontRenderContext(c);
883 if (frc.isAntiAliased() || frc.usesFractionalMetrics()) {
884 frc = new FontRenderContext(frc.getTransform(), false, false);
885 }
886 } else if ((frc = getFRCProperty(c)) != null) {
887 /* frc = frc; ! */
888 } else {
889 frc = g2d.getFontRenderContext();
890 }
891 TextLayout layout = new TextLayout(iterator, frc);
892 if (isPrinting) {
893 FontRenderContext deviceFRC = g2d.getFontRenderContext();
894 if (!isFontRenderContextPrintCompatible(frc, deviceFRC)) {
895 float screenWidth = layout.getAdvance();
896 layout = new TextLayout(iterator, deviceFRC);
897 layout = layout.getJustifiedLayout(screenWidth);
898 }
899 }
900 layout.draw(g2d, x, y);
901 retVal = layout.getAdvance();
902 }
903
904 if (isPrinting) {
905 g.setColor(col);
906 }
907
908 return retVal;
909 }
910
911 /*
912 * Checks if two given FontRenderContexts are compatible for printing.
913 * We can't just use equals as we want to exclude from the comparison :
914 * + whether AA is set as irrelevant for printing and shouldn't affect
915 * printed metrics anyway
916 * + any translation component in the transform of either FRC, as it
917 * does not affect metrics.
918 * Compatible means no special handling needed for text painting
919 */
920 private static boolean
921 isFontRenderContextPrintCompatible(FontRenderContext frc1,
922 FontRenderContext frc2) {
923
924 if (frc1 == frc2) {
925 return true;
926 }
927
928 if (frc1 == null || frc2 == null) { // not supposed to happen
929 return false;
930 }
931
932 if (frc1.getFractionalMetricsHint() !=
933 frc2.getFractionalMetricsHint()) {
934 return false;
935 }
936
937 /* If both are identity, return true */
938 if (!frc1.isTransformed() && !frc2.isTransformed()) {
939 return true;
940 }
941
942 /* That's the end of the cheap tests, need to get and compare
943 * the transform matrices. We don't care about the translation
944 * components, so return true if they are otherwise identical.
945 */
946 double[] mat1 = new double[4];
947 double[] mat2 = new double[4];
948 frc1.getTransform().getMatrix(mat1);
949 frc2.getTransform().getMatrix(mat2);
950 return
951 mat1[0] == mat2[0] &&
952 mat1[1] == mat2[1] &&
953 mat1[2] == mat2[2] &&
954 mat1[3] == mat2[3];
955 }
956
957 /*
958 * Tries it best to get Graphics2D out of the given Graphics
959 * returns null if can not derive it.
960 */
961 public static Graphics2D getGraphics2D(Graphics g) {
962 if (g instanceof Graphics2D) {
963 return (Graphics2D) g;
964 } else if (g instanceof ProxyPrintGraphics) {
965 return (Graphics2D)(((ProxyPrintGraphics)g).getGraphics());
966 } else {
967 return null;
968 }
969 }
970
971 /*
972 * Returns FontRenderContext associated with Component.
973 * FontRenderContext from Component.getFontMetrics is associated
974 * with the component.
975 *
976 * Uses Component.getFontMetrics to get the FontRenderContext from.
977 * see JComponent.getFontMetrics and TextLayoutStrategy.java
978 */
979 public static FontRenderContext getFontRenderContext(Component c) {
980 assert c != null;
981 if (c == null) {
982 return DEFAULT_FRC;
983 } else {
984 return c.getFontMetrics(c.getFont()).getFontRenderContext();
985 }
986 }
987
988 /**
989 * A convenience method to get FontRenderContext.
990 * Returns the FontRenderContext for the passed in FontMetrics or
991 * for the passed in Component if FontMetrics is null
992 */
993 private static FontRenderContext getFontRenderContext(Component c, FontMetrics fm) {
994 assert fm != null || c!= null;
995 return (fm != null) ? fm.getFontRenderContext()
996 : getFontRenderContext(c);
997 }
998
999 /*
1000 * This method is to be used only for JComponent.getFontMetrics.
1001 * In all other places to get FontMetrics we need to use
1002 * JComponent.getFontMetrics.
1003 *
1004 */
1005 public static FontMetrics getFontMetrics(JComponent c, Font font) {
1006 FontRenderContext frc = getFRCProperty(c);
1007 if (frc == null) {
1008 frc = DEFAULT_FRC;
1009 }
1010 return FontDesignMetrics.getMetrics(font, frc);
1011 }
1012
1013
1014 /* Get any FontRenderContext associated with a JComponent
1015 * - may return null
1016 */
1017 private static FontRenderContext getFRCProperty(JComponent c) {
1018 if (c != null) {
1019 AATextInfo info =
1020 (AATextInfo)c.getClientProperty(AA_TEXT_PROPERTY_KEY);
1021 if (info != null) {
1022 return info.frc;
1023 }
1024 }
1025 return null;
1026 }
1027
1028 /*
1029 * returns true if the Graphics is print Graphics
1030 * false otherwise
1031 */
1032 static boolean isPrinting(Graphics g) {
1033 return (g instanceof PrinterGraphics || g instanceof PrintGraphics);
1034 }
1035
1036 /**
1037 * Determines whether the SelectedTextColor should be used for painting text
1038 * foreground for the specified highlight.
1039 *
1040 * Returns true only if the highlight painter for the specified highlight
1041 * is the swing painter (whether inner class of javax.swing.text.DefaultHighlighter
1042 * or com.sun.java.swing.plaf.windows.WindowsTextUI) and its background color
1043 * is null or equals to the selection color of the text component.
1044 *
1045 * This is a hack for fixing both bugs 4761990 and 5003294
1046 */
1047 public static boolean useSelectedTextColor(Highlighter.Highlight h, JTextComponent c) {
1048 Highlighter.HighlightPainter painter = h.getPainter();
1049 String painterClass = painter.getClass().getName();
1050 if (painterClass.indexOf("javax.swing.text.DefaultHighlighter") != 0 &&
1051 painterClass.indexOf("com.sun.java.swing.plaf.windows.WindowsTextUI") != 0) {
1052 return false;
1053 }
1054 try {
1055 DefaultHighlighter.DefaultHighlightPainter defPainter =
1056 (DefaultHighlighter.DefaultHighlightPainter) painter;
1057 if (defPainter.getColor() != null &&
1058 !defPainter.getColor().equals(c.getSelectionColor())) {
1059 return false;
1060 }
1061 } catch (ClassCastException e) {
1062 return false;
1063 }
1064 return true;
1065 }
1066
1067 /**
1068 * BearingCacheEntry is used to cache left and right character bearings
1069 * for a particular <code>Font</code> and <code>FontRenderContext</code>.
1070 */
1071 private static class BearingCacheEntry {
1072 private FontMetrics fontMetrics;
1073 private Font font;
1074 private FontRenderContext frc;
1075 private Map<Character, Short> cache;
1076 // Used for the creation of a GlyphVector
1077 private static final char[] oneChar = new char[1];
1078
1079 public BearingCacheEntry(FontMetrics fontMetrics) {
1080 this.fontMetrics = fontMetrics;
1081 this.font = fontMetrics.getFont();
1082 this.frc = fontMetrics.getFontRenderContext();
1083 this.cache = new HashMap<Character, Short>();
1084 assert (font != null && frc != null);
1085 }
1086
1087 public int getLeftSideBearing(char aChar) {
1088 Short bearing = cache.get(aChar);
1089 if (bearing == null) {
1090 bearing = calcBearing(aChar);
1091 cache.put(aChar, bearing);
1092 }
1093 return ((0xFF00 & bearing) >>> 8) - 127;
1094 }
1095
1096 public int getRightSideBearing(char aChar) {
1097 Short bearing = cache.get(aChar);
1098 if (bearing == null) {
1099 bearing = calcBearing(aChar);
1100 cache.put(aChar, bearing);
1101 }
1102 return (0xFF & bearing) - 127;
1103 }
1104
1105 /* Calculates left and right side bearings for a character.
1106 * Makes an assumption that bearing is a value between -127 and +127.
1107 * Stores LSB and RSB as single two-byte number (short):
1108 * LSB is the high byte, RSB is the low byte.
1109 */
1110 private short calcBearing(char aChar) {
1111 oneChar[0] = aChar;
1112 GlyphVector gv = font.createGlyphVector(frc, oneChar);
1113 Rectangle pixelBounds = gv.getGlyphPixelBounds(0, frc, 0f, 0f);
1114
1115 // Get bearings
1116 int lsb = pixelBounds.x;
1117 int rsb = pixelBounds.width - fontMetrics.charWidth(aChar);
1118
1119 /* HRGB/HBGR LCD glyph images will always have a pixel
1120 * on the left and a pixel on the right
1121 * used in colour fringe reduction.
1122 * Text rendering positions this correctly but here
1123 * we are using the glyph image to adjust that position
1124 * so must account for it.
1125 */
1126 if (lsb < 0) {
1127 Object aaHint = frc.getAntiAliasingHint();
1128 if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
1129 aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
1130 lsb++;
1131 }
1132 }
1133 if (rsb > 0) {
1134 Object aaHint = frc.getAntiAliasingHint();
1135 if (aaHint == VALUE_TEXT_ANTIALIAS_LCD_HRGB ||
1136 aaHint == VALUE_TEXT_ANTIALIAS_LCD_HBGR) {
1137 rsb--;
1138 }
1139 }
1140
1141 // Make sure that LSB and RSB are valid (see 6472972)
1142 if (lsb < -127 || lsb > 127) {
1143 lsb = 0;
1144 }
1145 if (rsb < -127 || rsb > 127) {
1146 rsb = 0;
1147 }
1148
1149 int bearing = ((lsb + 127) << 8) + (rsb + 127);
1150 return (short)bearing;
1151 }
1152
1153 public boolean equals(Object entry) {
1154 if (entry == this) {
1155 return true;
1156 }
1157 if (!(entry instanceof BearingCacheEntry)) {
1158 return false;
1159 }
1160 BearingCacheEntry oEntry = (BearingCacheEntry)entry;
1161 return (font.equals(oEntry.font) &&
1162 frc.equals(oEntry.frc));
1163 }
1164
1165 public int hashCode() {
1166 int result = 17;
1167 if (font != null) {
1168 result = 37 * result + font.hashCode();
1169 }
1170 if (frc != null) {
1171 result = 37 * result + frc.hashCode();
1172 }
1173 return result;
1174 }
1175 }
1176
1177
1178 /*
1179 * here goes the fix for 4856343 [Problem with applet interaction
1180 * with system selection clipboard]
1181 *
1182 * NOTE. In case isTrustedContext() no checking
1183 * are to be performed
1184 */
1185
1186
1187 /**
1188 * checks the security permissions for accessing system clipboard
1189 *
1190 * for untrusted context (see isTrustedContext) checks the
1191 * permissions for the current event being handled
1192 *
1193 */
1194 public static boolean canAccessSystemClipboard() {
1195 boolean canAccess = false;
1196 if (!GraphicsEnvironment.isHeadless()) {
1197 SecurityManager sm = System.getSecurityManager();
1198 if (sm == null) {
1199 canAccess = true;
1200 } else {
1201 try {
1202 sm.checkSystemClipboardAccess();
1203 canAccess = true;
1204 } catch (SecurityException e) {
1205 }
1206 if (canAccess && ! isTrustedContext()) {
1207 canAccess = canCurrentEventAccessSystemClipboard(true);
1208 }
1209 }
1210 }
1211 return canAccess;
1212 }
1213
1214 /**
1215 * Returns true if EventQueue.getCurrentEvent() has the permissions to
1216 * access the system clipboard
1217 */
1218 public static boolean canCurrentEventAccessSystemClipboard() {
1219 return isTrustedContext()
1220 || canCurrentEventAccessSystemClipboard(false);
1221 }
1222
1223 /**
1224 * Returns true if the given event has permissions to access the
1225 * system clipboard
1226 *
1227 * @param e AWTEvent to check
1228 */
1229 public static boolean canEventAccessSystemClipboard(AWTEvent e) {
1230 return isTrustedContext()
1231 || canEventAccessSystemClipboard(e, false);
1232 }
1233
1234 /**
1235 * returns canAccessSystemClipboard field from InputEvent
1236 *
1237 * @param ie InputEvent to get the field from
1238 */
1239 private static synchronized boolean inputEvent_canAccessSystemClipboard(InputEvent ie) {
1240 if (inputEvent_CanAccessSystemClipboard_Field == null) {
1241 inputEvent_CanAccessSystemClipboard_Field =
1242 (Field)AccessController.doPrivileged(
1243 new java.security.PrivilegedAction() {
1244 public Object run() {
1245 Field field = null;
1246 try {
1247 field = InputEvent.class.
1248 getDeclaredField("canAccessSystemClipboard");
1249 field.setAccessible(true);
1250 return field;
1251 } catch (SecurityException e) {
1252 } catch (NoSuchFieldException e) {
1253 }
1254 return null;
1255 }
1256 });
1257 }
1258 if (inputEvent_CanAccessSystemClipboard_Field == null) {
1259 return false;
1260 }
1261 boolean ret = false;
1262 try {
1263 ret = inputEvent_CanAccessSystemClipboard_Field.
1264 getBoolean(ie);
1265 } catch(IllegalAccessException e) {
1266 }
1267 return ret;
1268 }
1269
1270 /**
1271 * Returns true if the given event is corrent gesture for
1272 * accessing clipboard
1273 *
1274 * @param ie InputEvent to check
1275 */
1276
1277 private static boolean isAccessClipboardGesture(InputEvent ie) {
1278 boolean allowedGesture = false;
1279 if (ie instanceof KeyEvent) { //we can validate only keyboard gestures
1280 KeyEvent ke = (KeyEvent)ie;
1281 int keyCode = ke.getKeyCode();
1282 int keyModifiers = ke.getModifiers();
1283 switch(keyCode) {
1284 case KeyEvent.VK_C:
1285 case KeyEvent.VK_V:
1286 case KeyEvent.VK_X:
1287 allowedGesture = (keyModifiers == InputEvent.CTRL_MASK);
1288 break;
1289 case KeyEvent.VK_INSERT:
1290 allowedGesture = (keyModifiers == InputEvent.CTRL_MASK ||
1291 keyModifiers == InputEvent.SHIFT_MASK);
1292 break;
1293 case KeyEvent.VK_COPY:
1294 case KeyEvent.VK_PASTE:
1295 case KeyEvent.VK_CUT:
1296 allowedGesture = true;
1297 break;
1298 case KeyEvent.VK_DELETE:
1299 allowedGesture = ( keyModifiers == InputEvent.SHIFT_MASK);
1300 break;
1301 }
1302 }
1303 return allowedGesture;
1304 }
1305
1306 /**
1307 * Returns true if e has the permissions to
1308 * access the system clipboard and if it is allowed gesture (if
1309 * checkGesture is true)
1310 *
1311 * @param e AWTEvent to check
1312 * @param checkGesture boolean
1313 */
1314 private static boolean canEventAccessSystemClipboard(AWTEvent e,
1315 boolean checkGesture) {
1316 if (EventQueue.isDispatchThread()) {
1317 /*
1318 * Checking event permissions makes sense only for event
1319 * dispathing thread
1320 */
1321 if (e instanceof InputEvent
1322 && (! checkGesture || isAccessClipboardGesture((InputEvent)e))) {
1323 return inputEvent_canAccessSystemClipboard((InputEvent)e);
1324 } else {
1325 return false;
1326 }
1327 } else {
1328 return true;
1329 }
1330 }
1331
1332 /**
1333 * Returns true if EventQueue.getCurrentEvent() has the permissions to
1334 * access the system clipboard and if it is allowed gesture (if
1335 * checkGesture true)
1336 *
1337 * @param checkGesture boolean
1338 */
1339 private static boolean canCurrentEventAccessSystemClipboard(boolean
1340 checkGesture) {
1341 AWTEvent event = EventQueue.getCurrentEvent();
1342 return canEventAccessSystemClipboard(event, checkGesture);
1343 }
1344
1345 /**
1346 * see RFE 5012841 [Per AppContect security permissions] for the
1347 * details
1348 *
1349 */
1350 private static boolean isTrustedContext() {
1351 return (System.getSecurityManager() == null)
1352 || (AppContext.getAppContext().
1353 get(UntrustedClipboardAccess) == null);
1354 }
1355
1356 public static String displayPropertiesToCSS(Font font, Color fg) {
1357 StringBuffer rule = new StringBuffer("body {");
1358 if (font != null) {
1359 rule.append(" font-family: ");
1360 rule.append(font.getFamily());
1361 rule.append(" ; ");
1362 rule.append(" font-size: ");
1363 rule.append(font.getSize());
1364 rule.append("pt ;");
1365 if (font.isBold()) {
1366 rule.append(" font-weight: 700 ; ");
1367 }
1368 if (font.isItalic()) {
1369 rule.append(" font-style: italic ; ");
1370 }
1371 }
1372 if (fg != null) {
1373 rule.append(" color: #");
1374 if (fg.getRed() < 16) {
1375 rule.append('0');
1376 }
1377 rule.append(Integer.toHexString(fg.getRed()));
1378 if (fg.getGreen() < 16) {
1379 rule.append('0');
1380 }
1381 rule.append(Integer.toHexString(fg.getGreen()));
1382 if (fg.getBlue() < 16) {
1383 rule.append('0');
1384 }
1385 rule.append(Integer.toHexString(fg.getBlue()));
1386 rule.append(" ; ");
1387 }
1388 rule.append(" }");
1389 return rule.toString();
1390 }
1391
1392 /**
1393 * Utility method that creates a <code>UIDefaults.LazyValue</code> that
1394 * creates an <code>ImageIcon</code> <code>UIResource</code> for the
1395 * specified image file name. The image is loaded using
1396 * <code>getResourceAsStream</code>, starting with a call to that method
1397 * on the base class parameter. If it cannot be found, searching will
1398 * continue through the base class' inheritance hierarchy, up to and
1399 * including <code>rootClass</code>.
1400 *
1401 * @param baseClass the first class to use in searching for the resource
1402 * @param rootClass an ancestor of <code>baseClass</code> to finish the
1403 * search at
1404 * @param imageFile the name of the file to be found
1405 * @return a lazy value that creates the <code>ImageIcon</code>
1406 * <code>UIResource</code> for the image,
1407 * or null if it cannot be found
1408 */
1409 public static Object makeIcon(final Class<?> baseClass,
1410 final Class<?> rootClass,
1411 final String imageFile) {
1412
1413 return new UIDefaults.LazyValue() {
1414 public Object createValue(UIDefaults table) {
1415 /* Copy resource into a byte array. This is
1416 * necessary because several browsers consider
1417 * Class.getResource a security risk because it
1418 * can be used to load additional classes.
1419 * Class.getResourceAsStream just returns raw
1420 * bytes, which we can convert to an image.
1421 */
1422 byte[] buffer = (byte[])
1423 java.security.AccessController.doPrivileged(
1424 new java.security.PrivilegedAction() {
1425 public Object run() {
1426 try {
1427 InputStream resource = null;
1428 Class<?> srchClass = baseClass;
1429
1430 while (srchClass != null) {
1431 resource = srchClass.getResourceAsStream(imageFile);
1432
1433 if (resource != null || srchClass == rootClass) {
1434 break;
1435 }
1436
1437 srchClass = srchClass.getSuperclass();
1438 }
1439
1440 if (resource == null) {
1441 return null;
1442 }
1443
1444 BufferedInputStream in =
1445 new BufferedInputStream(resource);
1446 ByteArrayOutputStream out =
1447 new ByteArrayOutputStream(1024);
1448 byte[] buffer = new byte[1024];
1449 int n;
1450 while ((n = in.read(buffer)) > 0) {
1451 out.write(buffer, 0, n);
1452 }
1453 in.close();
1454 out.flush();
1455 return out.toByteArray();
1456 } catch (IOException ioe) {
1457 System.err.println(ioe.toString());
1458 }
1459 return null;
1460 }
1461 });
1462
1463 if (buffer == null) {
1464 return null;
1465 }
1466 if (buffer.length == 0) {
1467 System.err.println("warning: " + imageFile +
1468 " is zero-length");
1469 return null;
1470 }
1471
1472 return new ImageIconUIResource(buffer);
1473 }
1474 };
1475 }
1476
1477 /* Used to help decide if AA text rendering should be used, so
1478 * this local display test should be additionally qualified
1479 * against whether we have XRender support on both ends of the wire,
1480 * as with that support remote performance may be good enough to turn
1481 * on by default. An additional complication there is XRender does not
1482 * appear capable of performing gamma correction needed for LCD text.
1483 */
1484 public static boolean isLocalDisplay() {
1485 try {
1486 // On Windows just return true. Permission to read os.name
1487 // is granted to all code but wrapped in try to be safe.
1488 if (OSInfo.getOSType() == OSInfo.OSType.WINDOWS) {
1489 return true;
1490 }
1491 // Else probably Solaris or Linux in which case may be remote X11
1492 Class x11Class = Class.forName("sun.awt.X11GraphicsEnvironment");
1493 Method isDisplayLocalMethod = x11Class.getMethod(
1494 "isDisplayLocal", new Class[0]);
1495 return (Boolean)isDisplayLocalMethod.invoke(null, (Object[])null);
1496 } catch (Throwable t) {
1497 }
1498 // If we get here we're most likely being run on some other O/S
1499 // or we didn't properly detect Windows.
1500 return true;
1501 }
1502
1503 /**
1504 * Returns an integer from the defaults table. If <code>key</code> does
1505 * not map to a valid <code>Integer</code>, or can not be convered from
1506 * a <code>String</code> to an integer, the value 0 is returned.
1507 *
1508 * @param key an <code>Object</code> specifying the int.
1509 * @return the int
1510 */
1511 public static int getUIDefaultsInt(Object key) {
1512 return getUIDefaultsInt(key, 0);
1513 }
1514
1515 /**
1516 * Returns an integer from the defaults table that is appropriate
1517 * for the given locale. If <code>key</code> does not map to a valid
1518 * <code>Integer</code>, or can not be convered from a <code>String</code>
1519 * to an integer, the value 0 is returned.
1520 *
1521 * @param key an <code>Object</code> specifying the int. Returned value
1522 * is 0 if <code>key</code> is not available,
1523 * @param l the <code>Locale</code> for which the int is desired
1524 * @return the int
1525 */
1526 public static int getUIDefaultsInt(Object key, Locale l) {
1527 return getUIDefaultsInt(key, l, 0);
1528 }
1529
1530 /**
1531 * Returns an integer from the defaults table. If <code>key</code> does
1532 * not map to a valid <code>Integer</code>, or can not be convered from
1533 * a <code>String</code> to an integer, <code>default</code> is
1534 * returned.
1535 *
1536 * @param key an <code>Object</code> specifying the int. Returned value
1537 * is 0 if <code>key</code> is not available,
1538 * @param defaultValue Returned value if <code>key</code> is not available,
1539 * or is not an Integer
1540 * @return the int
1541 */
1542 public static int getUIDefaultsInt(Object key, int defaultValue) {
1543 return getUIDefaultsInt(key, null, defaultValue);
1544 }
1545
1546 /**
1547 * Returns an integer from the defaults table that is appropriate
1548 * for the given locale. If <code>key</code> does not map to a valid
1549 * <code>Integer</code>, or can not be convered from a <code>String</code>
1550 * to an integer, <code>default</code> is returned.
1551 *
1552 * @param key an <code>Object</code> specifying the int. Returned value
1553 * is 0 if <code>key</code> is not available,
1554 * @param l the <code>Locale</code> for which the int is desired
1555 * @param defaultValue Returned value if <code>key</code> is not available,
1556 * or is not an Integer
1557 * @return the int
1558 */
1559 public static int getUIDefaultsInt(Object key, Locale l, int defaultValue) {
1560 Object value = UIManager.get(key, l);
1561
1562 if (value instanceof Integer) {
1563 return ((Integer)value).intValue();
1564 }
1565 if (value instanceof String) {
1566 try {
1567 return Integer.parseInt((String)value);
1568 } catch (NumberFormatException nfe) {}
1569 }
1570 return defaultValue;
1571 }
1572
1573 // At this point we need this method here. But we assume that there
1574 // will be a common method for this purpose in the future releases.
1575 public static Component compositeRequestFocus(Component component) {
1576 if (component instanceof Container) {
1577 Container container = (Container)component;
1578 if (container.isFocusCycleRoot()) {
1579 FocusTraversalPolicy policy = container.getFocusTraversalPolicy();
1580 Component comp = policy.getDefaultComponent(container);
1581 if (comp!=null) {
1582 comp.requestFocus();
1583 return comp;
1584 }
1585 }
1586 Container rootAncestor = container.getFocusCycleRootAncestor();
1587 if (rootAncestor!=null) {
1588 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
1589 Component comp = policy.getComponentAfter(rootAncestor, container);
1590
1591 if (comp!=null && SwingUtilities.isDescendingFrom(comp, container)) {
1592 comp.requestFocus();
1593 return comp;
1594 }
1595 }
1596 }
1597 if (component.isFocusable()) {
1598 component.requestFocus();
1599 return component;
1600 }
1601 return null;
1602 }
1603
1604 /**
1605 * Change focus to the visible component in {@code JTabbedPane}.
1606 * This is not a general-purpose method and is here only to permit
1607 * sharing code.
1608 */
1609 public static boolean tabbedPaneChangeFocusTo(Component comp) {
1610 if (comp != null) {
1611 if (comp.isFocusTraversable()) {
1612 SwingUtilities2.compositeRequestFocus(comp);
1613 return true;
1614 } else if (comp instanceof JComponent
1615 && ((JComponent)comp).requestDefaultFocus()) {
1616
1617 return true;
1618 }
1619 }
1620
1621 return false;
1622 }
1623
1624 /**
1625 * Submits a value-returning task for execution on the EDT and
1626 * returns a Future representing the pending results of the task.
1627 *
1628 * @param task the task to submit
1629 * @return a Future representing pending completion of the task
1630 * @throws NullPointerException if the task is null
1631 */
1632 public static <V> Future<V> submit(Callable<V> task) {
1633 if (task == null) {
1634 throw new NullPointerException();
1635 }
1636 FutureTask<V> future = new FutureTask<V>(task);
1637 execute(future);
1638 return future;
1639 }
1640
1641 /**
1642 * Submits a Runnable task for execution on the EDT and returns a
1643 * Future representing that task.
1644 *
1645 * @param task the task to submit
1646 * @param result the result to return upon successful completion
1647 * @return a Future representing pending completion of the task,
1648 * and whose <tt>get()</tt> method will return the given
1649 * result value upon completion
1650 * @throws NullPointerException if the task is null
1651 */
1652 public static <V> Future<V> submit(Runnable task, V result) {
1653 if (task == null) {
1654 throw new NullPointerException();
1655 }
1656 FutureTask<V> future = new FutureTask<V>(task, result);
1657 execute(future);
1658 return future;
1659 }
1660
1661 /**
1662 * Sends a Runnable to the EDT for the execution.
1663 */
1664 private static void execute(Runnable command) {
1665 SwingUtilities.invokeLater(command);
1666 }
1667
1668 /**
1669 * Sets the {@code SKIP_CLICK_COUNT} client property on the component
1670 * if it is an instance of {@code JTextComponent} with a
1671 * {@code DefaultCaret}. This property, used for text components acting
1672 * as editors in a table or tree, tells {@code DefaultCaret} how many
1673 * clicks to skip before starting selection.
1674 */
1675 public static void setSkipClickCount(Component comp, int count) {
1676 if (comp instanceof JTextComponent
1677 && ((JTextComponent) comp).getCaret() instanceof DefaultCaret) {
1678
1679 ((JTextComponent) comp).putClientProperty(SKIP_CLICK_COUNT, count);
1680 }
1681 }
1682
1683 /**
1684 * Return the MouseEvent's click count, possibly reduced by the value of
1685 * the component's {@code SKIP_CLICK_COUNT} client property. Clears
1686 * the {@code SKIP_CLICK_COUNT} property if the mouse event's click count
1687 * is 1. In order for clearing of the property to work correctly, there
1688 * must be a mousePressed implementation on the caller with this
1689 * call as the first line.
1690 */
1691 public static int getAdjustedClickCount(JTextComponent comp, MouseEvent e) {
1692 int cc = e.getClickCount();
1693
1694 if (cc == 1) {
1695 comp.putClientProperty(SKIP_CLICK_COUNT, null);
1696 } else {
1697 Integer sub = (Integer) comp.getClientProperty(SKIP_CLICK_COUNT);
1698 if (sub != null) {
1699 return cc - sub;
1700 }
1701 }
1702
1703 return cc;
1704 }
1705
1706 /**
1707 * Used by the {@code liesIn} method to return which section
1708 * the point lies in.
1709 *
1710 * @see #liesIn
1711 */
1712 public enum Section {
1713
1714 /** The leading section */
1715 LEADING,
1716
1717 /** The middle section */
1718 MIDDLE,
1719
1720 /** The trailing section */
1721 TRAILING
1722 }
1723
1724 /**
1725 * This method divides a rectangle into two or three sections along
1726 * the specified axis and determines which section the given point
1727 * lies in on that axis; used by drag and drop when calculating drop
1728 * locations.
1729 * <p>
1730 * For two sections, the rectangle is divided equally and the method
1731 * returns whether the point lies in {@code Section.LEADING} or
1732 * {@code Section.TRAILING}. For horizontal divisions, the calculation
1733 * respects component orientation.
1734 * <p>
1735 * For three sections, if the rectangle is greater than or equal to
1736 * 30 pixels in length along the axis, the calculation gives 10 pixels
1737 * to each of the leading and trailing sections and the remainder to the
1738 * middle. For smaller sizes, the rectangle is divided equally into three
1739 * sections.
1740 * <p>
1741 * Note: This method assumes that the point is within the bounds of
1742 * the given rectangle on the specified axis. However, in cases where
1743 * it isn't, the results still have meaning: {@code Section.MIDDLE}
1744 * remains the same, {@code Section.LEADING} indicates that the point
1745 * is in or somewhere before the leading section, and
1746 * {@code Section.TRAILING} indicates that the point is in or somewhere
1747 * after the trailing section.
1748 *
1749 * @param rect the rectangle
1750 * @param p the point the check
1751 * @param horizontal {@code true} to use the horizontal axis,
1752 * or {@code false} for the vertical axis
1753 * @param ltr {@code true} for left to right orientation,
1754 * or {@code false} for right to left orientation;
1755 * only used for horizontal calculations
1756 * @param three {@code true} for three sections,
1757 * or {@code false} for two
1758 *
1759 * @return the {@code Section} where the point lies
1760 *
1761 * @throws NullPointerException if {@code rect} or {@code p} are
1762 * {@code null}
1763 */
1764 private static Section liesIn(Rectangle rect, Point p, boolean horizontal,
1765 boolean ltr, boolean three) {
1766
1767 /* beginning of the rectangle on the axis */
1768 int p0;
1769
1770 /* point on the axis we're interested in */
1771 int pComp;
1772
1773 /* length of the rectangle on the axis */
1774 int length;
1775
1776 /* value of ltr if horizontal, else true */
1777 boolean forward;
1778
1779 if (horizontal) {
1780 p0 = rect.x;
1781 pComp = p.x;
1782 length = rect.width;
1783 forward = ltr;
1784 } else {
1785 p0 = rect.y;
1786 pComp = p.y;
1787 length = rect.height;
1788 forward = true;
1789 }
1790
1791 if (three) {
1792 int boundary = (length >= 30) ? 10 : length / 3;
1793
1794 if (pComp < p0 + boundary) {
1795 return forward ? Section.LEADING : Section.TRAILING;
1796 } else if (pComp >= p0 + length - boundary) {
1797 return forward ? Section.TRAILING : Section.LEADING;
1798 }
1799
1800 return Section.MIDDLE;
1801 } else {
1802 int middle = p0 + length / 2;
1803 if (forward) {
1804 return pComp >= middle ? Section.TRAILING : Section.LEADING;
1805 } else {
1806 return pComp < middle ? Section.TRAILING : Section.LEADING;
1807 }
1808 }
1809 }
1810
1811 /**
1812 * This method divides a rectangle into two or three sections along
1813 * the horizontal axis and determines which section the given point
1814 * lies in; used by drag and drop when calculating drop locations.
1815 * <p>
1816 * See the documentation for {@link #liesIn} for more information
1817 * on how the section is calculated.
1818 *
1819 * @param rect the rectangle
1820 * @param p the point the check
1821 * @param ltr {@code true} for left to right orientation,
1822 * or {@code false} for right to left orientation
1823 * @param three {@code true} for three sections,
1824 * or {@code false} for two
1825 *
1826 * @return the {@code Section} where the point lies
1827 *
1828 * @throws NullPointerException if {@code rect} or {@code p} are
1829 * {@code null}
1830 */
1831 public static Section liesInHorizontal(Rectangle rect, Point p,
1832 boolean ltr, boolean three) {
1833 return liesIn(rect, p, true, ltr, three);
1834 }
1835
1836 /**
1837 * This method divides a rectangle into two or three sections along
1838 * the vertical axis and determines which section the given point
1839 * lies in; used by drag and drop when calculating drop locations.
1840 * <p>
1841 * See the documentation for {@link #liesIn} for more information
1842 * on how the section is calculated.
1843 *
1844 * @param rect the rectangle
1845 * @param p the point the check
1846 * @param three {@code true} for three sections,
1847 * or {@code false} for two
1848 *
1849 * @return the {@code Section} where the point lies
1850 *
1851 * @throws NullPointerException if {@code rect} or {@code p} are
1852 * {@code null}
1853 */
1854 public static Section liesInVertical(Rectangle rect, Point p,
1855 boolean three) {
1856 return liesIn(rect, p, false, false, three);
1857 }
1858}