001: /*
002: * @(#)JidePopupMenu.java
003: *
004: * Copyright 2002 JIDE Software. All rights reserved.
005: */
006: package com.jidesoft.swing;
007:
008: import com.jidesoft.plaf.LookAndFeelFactory;
009: import com.jidesoft.plaf.UIDefaultsLookup;
010: import com.jidesoft.utils.PortingUtils;
011:
012: import javax.swing.*;
013: import javax.swing.event.PopupMenuEvent;
014: import javax.swing.event.PopupMenuListener;
015: import javax.swing.plaf.PopupMenuUI;
016: import java.awt.*;
017:
018: /**
019: * This component extends JPopupMenu and adds a method to
020: * display the menu inside the screen even if the mouse
021: * pointer is near the edge of the screen.
022: * <p/>
023: * It also puts the menu items into a scroll pane.
024: * When there are too many menu items that can't fit into one screen, the scroll pane
025: * will scroll up and down so that you can still get to all menu items.
026: */
027: public class JidePopupMenu extends JPopupMenu implements Scrollable {
028:
029: private static final String uiClassID = "JidePopupMenuUI";
030:
031: private boolean _useLightWeightPopup;
032:
033: /**
034: * Constructs a <code>JPopupMenu</code> without an "invoker".
035: */
036: public JidePopupMenu() {
037: setupPopupMenu();
038: }
039:
040: /**
041: * Constructs a <code>JPopupMenu</code> with the specified title.
042: *
043: * @param label the string that a UI may use to display as a title
044: * for the popup menu.
045: */
046: public JidePopupMenu(String label) {
047: super (label);
048: setupPopupMenu();
049: }
050:
051: @Override
052: public String getUIClassID() {
053: return uiClassID;
054: }
055:
056: private void setupPopupMenu() {
057: addPopupMenuListener(new ToolTipSwitchPopupMenuListener());
058: }
059:
060: @Override
061: public void updateUI() {
062: if (UIDefaultsLookup.get(uiClassID) == null) {
063: LookAndFeelFactory.installJideExtension();
064: }
065: setUI((PopupMenuUI) UIManager.getUI(this ));
066: }
067:
068: /**
069: * Displays the PopUpMenu at a specified position.
070: *
071: * @param invoker the component that triggers the event
072: * @param x mouse X position on screen
073: * @param y mouse Y position on screen
074: */
075: @Override
076: public void show(Component invoker, int x, int y) {
077: Point p = getPopupMenuOrigin(invoker, x, y);
078: super .show(invoker, p.x, p.y);
079: }
080:
081: /**
082: * Figures out the sizes needed to calculate the menu position.
083: *
084: * @param invoker the component that triggers the event
085: * @param x mouse X position on screen
086: * @param y mouse Y position on screen
087: * @return new position
088: */
089: protected Point getPopupMenuOrigin(Component invoker, int x, int y) {
090:
091: Rectangle bounds = PortingUtils.getScreenBounds(invoker);
092:
093: Dimension size = this .getSize();
094:
095: if (size.width == 0) {
096: size = this .getPreferredSize();
097: }
098:
099: Point p = new Point(x, y);
100: SwingUtilities.convertPointToScreen(p, invoker);
101: int left = p.x + size.width;
102: int bottom = p.y + size.height;
103:
104: if (x < bounds.x) {
105: x = bounds.x;
106: }
107: if (left > bounds.width) {
108: x -= size.width;
109: }
110:
111: if (bottom > bounds.height) {
112: y -= size.height;
113: }
114:
115: if (y < bounds.y) {
116: y = bounds.y;
117: }
118:
119: return new Point(x, y);
120: }
121:
122: @Override
123: public void setLocation(int x, int y) {
124: // TODO: this is really a hack. Two classes will call this method. One is when the JPopupMenu is show. The other
125: if (isVisible() && y <= 0) {
126: move(x, y); // cannot call setLocation because it will be recursive call. So call deprecated move in order to bypass this.
127: } else {
128: super .setLocation(x, y);
129: }
130: }
131:
132: public Dimension getPreferredScrollableViewportSize() {
133: Dimension size = getPreferredSize();
134: Dimension screenSize = PortingUtils.getLocalScreenSize(this );
135: Container container = SwingUtilities.getAncestorOfClass(
136: SimpleScrollPane.class, this );
137: if (container instanceof SimpleScrollPane) {
138: SimpleScrollPane scrollPane = (SimpleScrollPane) container;
139: size.height = Math.min(size.height,
140: screenSize.height
141: - scrollPane.getScrollUpButton()
142: .getPreferredSize().height
143: - scrollPane.getScrollDownButton()
144: .getPreferredSize().height);
145: }
146: return size;
147: }
148:
149: public int getScrollableUnitIncrement(Rectangle visibleRect,
150: int orientation, int direction) {
151: return new JMenuItem("ABC").getPreferredSize().height;
152: }
153:
154: public int getScrollableBlockIncrement(Rectangle visibleRect,
155: int orientation, int direction) {
156: return new JMenuItem("ABC").getPreferredSize().height * 5;
157: }
158:
159: public boolean getScrollableTracksViewportWidth() {
160: return true;
161: }
162:
163: public boolean getScrollableTracksViewportHeight() {
164: return false;
165: }
166:
167: private class ToolTipSwitchPopupMenuListener implements
168: PopupMenuListener {
169: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
170: _useLightWeightPopup = ToolTipManager.sharedInstance()
171: .isLightWeightPopupEnabled();
172: ToolTipManager.sharedInstance().setLightWeightPopupEnabled(
173: false);
174: }
175:
176: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
177: ToolTipManager.sharedInstance().setLightWeightPopupEnabled(
178: _useLightWeightPopup);
179: }
180:
181: public void popupMenuCanceled(PopupMenuEvent e) {
182: ToolTipManager.sharedInstance().setLightWeightPopupEnabled(
183: _useLightWeightPopup);
184: }
185: }
186: }
|