History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/swing/SwingUtilities2.java (1 of 18352 files). Show all

Unified · Split

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