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