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