File: ComboBoxPopupLocationFix.java

package info (click to toggle)
libglazedlists-java 1.9.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 3,024 kB
  • ctags: 4,252
  • sloc: java: 22,561; xml: 818; sh: 51; makefile: 5
file content (101 lines) | stat: -rw-r--r-- 3,650 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* Glazed Lists                                                 (c) 2003-2006 */
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
/*                                                     O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl.swing;

import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;

/**
 * Fix a problem where the JComboBox's popup obscures its editor in the Mac OS X
 * Aqua look and feel.
 *
 * <p>Installing this fix will resolve the problem for Aqua without having
 * side-effects for other look-and-feels. It also supports dynamically changed
 * look and feels.
 *
 * @see <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=332">bug 332</a>
 *
 * @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
 */
public final class ComboBoxPopupLocationFix {

    /** the components being fixed */
    private final JComboBox comboBox;
    private final JPopupMenu popupMenu;

    /** the listener provides callbacks as necessary */
    private final Listener listener = new Listener();

    /**
     * Private constructor so users use the more action-oriented
     * {@link #install} method.
     */
    private ComboBoxPopupLocationFix(JComboBox comboBox) {
        this.comboBox = comboBox;
        this.popupMenu = (JPopupMenu)comboBox.getUI().getAccessibleChild(comboBox, 0);

        popupMenu.addPopupMenuListener(listener);
    }

    /**
     * Install the fix for the specified combo box.
     */
    public static ComboBoxPopupLocationFix install(JComboBox comboBox) {
        if(comboBox == null) throw new IllegalArgumentException();
        return new ComboBoxPopupLocationFix(comboBox);
    }

    /**
     * Uninstall the fix. Usually this is unnecessary since letting the combo
     * box go out of scope is sufficient.
     */
    public void uninstall() {
        popupMenu.removePopupMenuListener(listener);
    }

    /**
     * Reposition the popup immediately before it is shown.
     */
    private class Listener implements PopupMenuListener {
        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
            final JComponent popupComponent = (JComponent) e.getSource();
            fixPopupLocation(popupComponent);
        }
        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
            // do nothing
        }
        public void popupMenuCanceled(PopupMenuEvent e) {
            // do nothing
        }
    }

    /**
     * Do the adjustment on the specified popupComponent immediately before
     * it is displayed.
     */
    private void fixPopupLocation(JComponent popupComponent) {
        // we only need to fix Apple's aqua look and feel
        if(popupComponent.getClass().getName().indexOf("apple.laf") != 0) {
            return;
        }

        // put the popup right under the combo box so it looks like a
        // normal Aqua combo box
        Point comboLocationOnScreen = comboBox.getLocationOnScreen();
        int comboHeight = comboBox.getHeight();
        int popupY = comboLocationOnScreen.y + comboHeight;

        // ...unless the popup overflows the screen, in which case we put it
        // above the combobox
        Rectangle screenBounds = new ScreenGeometry(comboBox).getScreenBounds();
        int popupHeight = popupComponent.getPreferredSize().height;
        if(comboLocationOnScreen.y + comboHeight + popupHeight > screenBounds.x + screenBounds.height) {
            popupY = comboLocationOnScreen.y - popupHeight;
        }

        popupComponent.setLocation(comboLocationOnScreen.x, popupY);
    }
}