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