0001: /*
0002: * @(#)JidePopup.java 2/24/2005
0003: *
0004: * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
0005: */
0006:
0007: package com.jidesoft.popup;
0008:
0009: import com.jidesoft.plaf.LookAndFeelFactory;
0010: import com.jidesoft.plaf.PopupUI;
0011: import com.jidesoft.plaf.UIDefaultsLookup;
0012: import com.jidesoft.swing.JideScrollPane;
0013: import com.jidesoft.swing.JideSwingUtilities;
0014: import com.jidesoft.swing.Resizable;
0015: import com.jidesoft.swing.ResizableWindow;
0016: import com.jidesoft.utils.PortingUtils;
0017: import com.jidesoft.utils.SecurityUtils;
0018: import sun.awt.EmbeddedFrame;
0019:
0020: import javax.accessibility.Accessible;
0021: import javax.accessibility.AccessibleContext;
0022: import javax.accessibility.AccessibleRole;
0023: import javax.accessibility.AccessibleValue;
0024: import javax.swing.*;
0025: import javax.swing.border.Border;
0026: import javax.swing.event.PopupMenuEvent;
0027: import javax.swing.event.PopupMenuListener;
0028: import java.applet.Applet;
0029: import java.awt.*;
0030: import java.awt.event.*;
0031: import java.util.ArrayList;
0032: import java.util.List;
0033:
0034: /**
0035: * <code>JidePopup</code> is a popup window which can be resized, dragged and autohide if time out.
0036: * <p/>
0037: * JidePopup uses JWindow as the container in order to show itself. By default, JidePopup is not focusable which means
0038: * no component in the JidePopup will get focus. For example, if you put a JTextField in JidePopup and the JTextField becomes not editable,
0039: * this is a result of non-focusable JWindow. So if you want components in JidePopup to be able to receive focus, you can either
0040: * call setFocusable(true) or you can call {@link #setDefaultFocusComponent(java.awt.Component)} to set a child component
0041: * as the default focus component.
0042: */
0043: public class JidePopup extends JComponent implements Accessible,
0044: WindowConstants {
0045:
0046: /**
0047: * @see #getUIClassID
0048: * @see #readObject
0049: */
0050: private static final String uiClassID = "JidePopupUI";
0051:
0052: /**
0053: * The <code>JRootPane</code> instance that manages the
0054: * content pane
0055: * and optional menu bar for this Popup, as well as the
0056: * glass pane.
0057: *
0058: * @see javax.swing.JRootPane
0059: * @see javax.swing.RootPaneContainer
0060: */
0061: private JRootPane rootPane;
0062:
0063: /**
0064: * If <code>true</code> then calls to <code>add</code> and <code>setLayout</code>
0065: * cause an exception to be thrown.
0066: */
0067: private boolean rootPaneCheckingEnabled = false;
0068:
0069: /**
0070: * Bound property name.
0071: */
0072: public final static String CONTENT_PANE_PROPERTY = "contentPane";
0073:
0074: /**
0075: * Bound property name.
0076: */
0077: public final static String MENU_BAR_PROPERTY = "JMenuBar";
0078:
0079: /**
0080: * Bound property name.
0081: */
0082: public final static String LAYERED_PANE_PROPERTY = "layeredPane";
0083:
0084: /**
0085: * Bound property name.
0086: */
0087: public final static String ROOT_PANE_PROPERTY = "rootPane";
0088:
0089: /**
0090: * Bound property name.
0091: */
0092: public final static String GLASS_PANE_PROPERTY = "glassPane";
0093:
0094: /**
0095: * Bound property name.
0096: */
0097: public final static String VISIBLE_PROPERTY = "visible";
0098:
0099: public final static String TRANSIENT_PROPERTY = "transient";
0100:
0101: /**
0102: * Constrained property name indicated that this frame has
0103: * selected status.
0104: */
0105:
0106: /**
0107: * Constrained property name indicating that the popup is attachable.
0108: */
0109: public final static String ATTACHABLE_PROPERTY = "attachable";
0110:
0111: private boolean _attachable = true;
0112:
0113: /**
0114: * Bound property name for gripper.
0115: */
0116: public final static String MOVABLE_PROPERTY = "movable";
0117:
0118: /**
0119: * If the gripper should be shown. Gripper is something on divider to indicate it can be dragged.
0120: */
0121: private boolean _movable = false;
0122:
0123: /**
0124: * Bound property name for if the popup is detached.
0125: */
0126: public final static String DETACHED_PROPERTY = "detached";
0127:
0128: protected boolean _detached;
0129:
0130: protected ResizableWindow _window;
0131:
0132: private ComponentAdapter _componentListener;
0133: private WindowAdapter _windowListener;
0134: private ComponentAdapter _ownerComponentListener;
0135: private HierarchyListener _hierarchyListener;
0136:
0137: /**
0138: * Bound property name for resizable.
0139: */
0140: public final static String RESIZABLE_PROPERTY = "resizable";
0141:
0142: private boolean _resizable = true;
0143:
0144: // /**
0145: // * Bound property name for movable.
0146: // */
0147: // public final static String MOVABLE_PROPERTY = "movable";
0148: //
0149: // private boolean _movable;
0150:
0151: /**
0152: * Bound property name for owner.
0153: */
0154: public final static String OWNER_PROPERTY = "owner";
0155:
0156: private Component _owner;
0157:
0158: private Border _popupBorder;
0159:
0160: private boolean _transient = true;
0161:
0162: private int _timeout = 0;
0163: private Timer _timer;
0164:
0165: private Component _defaultFocusComponent;
0166:
0167: /**
0168: * Hides the popup when the owner is moved.
0169: */
0170: public static final int DO_NOTHING_ON_MOVED = -1;
0171:
0172: /**
0173: * Hides the popup when the owner is moved.
0174: */
0175: public static final int HIDE_ON_MOVED = 0;
0176:
0177: /**
0178: * Moves the popup along with owner when the owner is moved.
0179: */
0180: public static final int MOVE_ON_MOVED = 1;
0181:
0182: private int _defaultMoveOperation = HIDE_ON_MOVED;
0183: /**
0184: * The distance between alert and screen border.
0185: */
0186: public int DISTANCE_TO_SCREEN_BORDER = 10;
0187:
0188: private List<Component> _excludedComponents;
0189:
0190: private int _gripperLocation = SwingConstants.NORTH;
0191:
0192: public static final String PROPERTY_GRIPPER_LOCATION = "gripperLocation";
0193: private ComponentAdapter _popupResizeListener;
0194:
0195: protected Dimension _previousSize;
0196: private Component _actualOwner;
0197: private Point _actualOwnerLocation;
0198:
0199: /**
0200: * Creates a Popup.
0201: */
0202: public JidePopup() {
0203: _excludedComponents = new ArrayList<Component>();
0204: setRootPane(createRootPane());
0205: setLayout(new BorderLayout());
0206: setRootPaneCheckingEnabled(true);
0207: setFocusable(false);
0208: updateUI();
0209: }
0210:
0211: /**
0212: * Called by the constructor to set up the <code>JRootPane</code>.
0213: *
0214: * @return a new <code>JRootPane</code>
0215: * @see javax.swing.JRootPane
0216: */
0217: protected JRootPane createRootPane() {
0218: return new JRootPane();
0219: }
0220:
0221: /**
0222: * Returns the look-and-feel object that renders this component.
0223: *
0224: * @return the <code>PopupUI</code> object that renders
0225: * this component
0226: */
0227: public PopupUI getUI() {
0228: return (PopupUI) ui;
0229: }
0230:
0231: /**
0232: * Sets the UI delegate for this <code>Popup</code>.
0233: *
0234: * @param ui the UI delegate
0235: */
0236: public void setUI(PopupUI ui) {
0237: boolean checkingEnabled = isRootPaneCheckingEnabled();
0238: try {
0239: setRootPaneCheckingEnabled(false);
0240: super .setUI(ui);
0241: } finally {
0242: setRootPaneCheckingEnabled(checkingEnabled);
0243: }
0244: }
0245:
0246: /**
0247: * Notification from the <code>UIManager</code> that the look and feel
0248: * has changed.
0249: * Replaces the current UI object with the latest version from the
0250: * <code>UIManager</code>.
0251: *
0252: * @see javax.swing.JComponent#updateUI
0253: */
0254: @Override
0255: public void updateUI() {
0256: if (UIDefaultsLookup.get(uiClassID) == null) {
0257: LookAndFeelFactory.installJideExtension();
0258: }
0259: setUI((PopupUI) UIManager.getUI(this ));
0260: invalidate();
0261: }
0262:
0263: /**
0264: * Returns the name of the look-and-feel
0265: * class that renders this component.
0266: *
0267: * @return the string "PopupUI"
0268: * @see javax.swing.JComponent#getUIClassID
0269: * @see javax.swing.UIDefaults#getUI
0270: */
0271: @Override
0272: public String getUIClassID() {
0273: return uiClassID;
0274: }
0275:
0276: /**
0277: * Returns whether calls to <code>add</code> and
0278: * <code>setLayout</code> cause an exception to be thrown.
0279: *
0280: * @return <code>true</code> if <code>add</code> and <code>setLayout</code>
0281: * are checked
0282: * @see #addImpl
0283: * @see #setLayout
0284: * @see #setRootPaneCheckingEnabled
0285: */
0286: protected boolean isRootPaneCheckingEnabled() {
0287: return rootPaneCheckingEnabled;
0288: }
0289:
0290: /**
0291: * Determines whether calls to <code>add</code> and
0292: * <code>setLayout</code> cause an exception to be thrown.
0293: *
0294: * @param enabled a boolean value, <code>true</code> if checking is to be
0295: * enabled, which cause the exceptions to be thrown
0296: * @see #addImpl
0297: * @see #setLayout
0298: * @see #isRootPaneCheckingEnabled
0299: */
0300: protected void setRootPaneCheckingEnabled(boolean enabled) {
0301: rootPaneCheckingEnabled = enabled;
0302: }
0303:
0304: /**
0305: * Ensures that, by default, children cannot be added
0306: * directly to this component.
0307: * Instead, children must be added to its content pane.
0308: * For example:
0309: * <pre>
0310: * thisComponent.getContentPane().add(child)
0311: * </pre>
0312: * An attempt to add to directly to this component will cause a
0313: * runtime exception to be thrown. Subclasses can disable this
0314: * behavior.
0315: *
0316: * @param comp the <code>Component</code> to be added
0317: * @param constraints the object containing the constraints, if any
0318: * @param index the index
0319: * @throws Error if called with <code>isRootPaneChecking</code> <code>true</code>
0320: * @see #setRootPaneCheckingEnabled
0321: */
0322: @Override
0323: protected void addImpl(Component comp, Object constraints, int index) {
0324: if (isRootPaneCheckingEnabled()) {
0325: getContentPane().add(comp, constraints, index);
0326: } else {
0327: super .addImpl(comp, constraints, index);
0328: }
0329: }
0330:
0331: /**
0332: * Removes the specified component from this container.
0333: *
0334: * @param comp the component to be removed
0335: * @see #add
0336: */
0337: @Override
0338: public void remove(Component comp) {
0339: int oldCount = getComponentCount();
0340: super .remove(comp);
0341: if (oldCount == getComponentCount()) {
0342: // Client mistake, but we need to handle it to avoid a
0343: // common object leak in client applications.
0344: getContentPane().remove(comp);
0345: }
0346: }
0347:
0348: /**
0349: * Ensures that, by default, the layout of this component cannot be set.
0350: * Instead, the layout of its content pane should be set.
0351: * For example:
0352: * <pre>
0353: * thisComponent.getContentPane().setLayout(new GridLayout(1,2))
0354: * </pre>
0355: * An attempt to set the layout of this component will cause an
0356: * runtime exception to be thrown. Subclasses can disable this
0357: * behavior.
0358: *
0359: * @param manager the <code>LayoutManager</code>
0360: * @throws Error if called with <code>isRootPaneChecking</code> <code>true</code>
0361: * @see #setRootPaneCheckingEnabled
0362: */
0363: @Override
0364: public void setLayout(LayoutManager manager) {
0365: if (isRootPaneCheckingEnabled()) {
0366: getContentPane().setLayout(manager);
0367: } else {
0368: super .setLayout(manager);
0369: }
0370: }
0371:
0372: //////////////////////////////////////////////////////////////////////////
0373: /// Property Methods
0374: //////////////////////////////////////////////////////////////////////////
0375:
0376: /**
0377: * Returns the current <code>JMenuBar</code> for this
0378: * <code>Popup</code>, or <code>null</code>
0379: * if no menu bar has been set.
0380: *
0381: * @return the <code>JMenuBar</code> used by this Popup.
0382: * @see #setJMenuBar
0383: */
0384: public JMenuBar getJMenuBar() {
0385: return getRootPane().getJMenuBar();
0386: }
0387:
0388: /**
0389: * Sets the <code>menuBar</code> property for this <code>Popup</code>.
0390: *
0391: * @param m the <code>JMenuBar</code> to use in this Popup.
0392: * @see #getJMenuBar
0393: */
0394: public void setJMenuBar(JMenuBar m) {
0395: JMenuBar oldValue = getJMenuBar();
0396: getRootPane().setJMenuBar(m);
0397: firePropertyChange(MENU_BAR_PROPERTY, oldValue, m);
0398: }
0399:
0400: // implements javax.swing.RootPaneContainer
0401:
0402: /**
0403: * Returns the content pane for this Popup.
0404: *
0405: * @return the content pane
0406: */
0407: public Container getContentPane() {
0408: return getRootPane().getContentPane();
0409: }
0410:
0411: /**
0412: * Sets this <code>Popup</code>'s <code>contentPane</code>
0413: * property.
0414: *
0415: * @param c the content pane for this popup.
0416: * @throws java.awt.IllegalComponentStateException
0417: * (a runtime
0418: * exception) if the content pane parameter is <code>null</code>
0419: * @see javax.swing.RootPaneContainer#getContentPane
0420: */
0421: public void setContentPane(Container c) {
0422: Container oldValue = getContentPane();
0423: getRootPane().setContentPane(c);
0424: firePropertyChange(CONTENT_PANE_PROPERTY, oldValue, c);
0425: }
0426:
0427: /**
0428: * Returns the layered pane for this popup.
0429: *
0430: * @return a <code>JLayeredPane</code> object
0431: * @see javax.swing.RootPaneContainer#setLayeredPane
0432: * @see javax.swing.RootPaneContainer#getLayeredPane
0433: */
0434: public JLayeredPane getLayeredPane() {
0435: return getRootPane().getLayeredPane();
0436: }
0437:
0438: /**
0439: * Sets this <code>Popup</code>'s
0440: * <code>layeredPane</code> property.
0441: *
0442: * @param layered the <code>JLayeredPane</code> for this popup
0443: * @throws java.awt.IllegalComponentStateException
0444: * (a runtime
0445: * exception) if the layered pane parameter is <code>null</code>
0446: * @see javax.swing.RootPaneContainer#setLayeredPane
0447: */
0448: public void setLayeredPane(JLayeredPane layered) {
0449: JLayeredPane oldValue = getLayeredPane();
0450: getRootPane().setLayeredPane(layered);
0451: firePropertyChange(LAYERED_PANE_PROPERTY, oldValue, layered);
0452: }
0453:
0454: /**
0455: * Returns the glass pane for this popup.
0456: *
0457: * @return the glass pane
0458: * @see javax.swing.RootPaneContainer#setGlassPane
0459: */
0460: public Component getGlassPane() {
0461: return getRootPane().getGlassPane();
0462: }
0463:
0464: /**
0465: * Sets this <code>Popup</code>'s
0466: * <code>glassPane</code> property.
0467: *
0468: * @param glass the glass pane for this popup
0469: * @see javax.swing.RootPaneContainer#getGlassPane
0470: */
0471: public void setGlassPane(Component glass) {
0472: Component oldValue = getGlassPane();
0473: getRootPane().setGlassPane(glass);
0474: firePropertyChange(GLASS_PANE_PROPERTY, oldValue, glass);
0475: }
0476:
0477: /**
0478: * Returns the <code>rootPane</code> object for this popup.
0479: *
0480: * @return the <code>rootPane</code> property
0481: * @see javax.swing.RootPaneContainer#getRootPane
0482: */
0483: @Override
0484: public JRootPane getRootPane() {
0485: return rootPane;
0486: }
0487:
0488: /**
0489: * Sets the <code>rootPane</code> property
0490: * for this <code>Popup</code>.
0491: * This method is called by the constructor.
0492: *
0493: * @param root the new <code>JRootPane</code> object
0494: */
0495: protected void setRootPane(JRootPane root) {
0496: if (rootPane != null) {
0497: remove(rootPane);
0498: }
0499: JRootPane oldValue = getRootPane();
0500: rootPane = root;
0501: if (rootPane != null) {
0502: boolean checkingEnabled = isRootPaneCheckingEnabled();
0503: try {
0504: setRootPaneCheckingEnabled(false);
0505: add(rootPane, BorderLayout.CENTER);
0506: } finally {
0507: setRootPaneCheckingEnabled(checkingEnabled);
0508: }
0509: }
0510: firePropertyChange(ROOT_PANE_PROPERTY, oldValue, root);
0511: }
0512:
0513: /**
0514: * Makes the component visible or invisible.
0515: * Overrides <code>Component.setVisible</code>.
0516: *
0517: * @param visible true to make the component visible; false to
0518: * make it invisible
0519: */
0520: @Override
0521: public void setVisible(boolean visible) {
0522: boolean old = isVisible();
0523: if (visible != old) {
0524: super .setVisible(visible);
0525: firePropertyChange(VISIBLE_PROPERTY, old, visible);
0526: }
0527: }
0528:
0529: /**
0530: * Gets the <code>AccessibleContext</code> associated with this
0531: * <code>Popup</code>.
0532: * For popups, the <code>AccessibleContext</code>
0533: * takes the form of an
0534: * <code>AccessiblePopup</code> object.
0535: * A new <code>AccessiblePopup</code> instance is created if necessary.
0536: *
0537: * @return an <code>AccessiblePopup</code> that serves as the
0538: * <code>AccessibleContext</code> of this
0539: * <code>Popup</code>
0540: * @see com.jidesoft.popup.JidePopup.AccessiblePopup
0541: */
0542: @Override
0543: public AccessibleContext getAccessibleContext() {
0544: if (accessibleContext == null) {
0545: accessibleContext = new AccessiblePopup();
0546: }
0547: return accessibleContext;
0548: }
0549:
0550: /**
0551: * This class implements accessibility support for the
0552: * <code>Popup</code> class. It provides an implementation of the
0553: * Java Accessibility API appropriate to popup user-interface
0554: * elements.
0555: */
0556: protected class AccessiblePopup extends AccessibleJComponent
0557: implements AccessibleValue {
0558:
0559: /**
0560: * Get the accessible name of this object.
0561: *
0562: * @return the localized name of the object -- can be <code>null</code> if this
0563: * object does not have a name
0564: * @see #setAccessibleName
0565: */
0566: @Override
0567: public String getAccessibleName() {
0568: if (accessibleName != null) {
0569: return accessibleName;
0570: } else {
0571: return getName();
0572: }
0573: }
0574:
0575: /**
0576: * Get the role of this object.
0577: *
0578: * @return an instance of AccessibleRole describing the role of the
0579: * object
0580: * @see javax.accessibility.AccessibleRole
0581: */
0582: @Override
0583: public AccessibleRole getAccessibleRole() {
0584: return AccessibleRole.SWING_COMPONENT; // use a generic one since there is no specific one to choose
0585: }
0586:
0587: /**
0588: * Gets the AccessibleValue associated with this object. In the
0589: * implementation of the Java Accessibility API for this class,
0590: * returns this object, which is responsible for implementing the
0591: * <code>AccessibleValue</code> interface on behalf of itself.
0592: *
0593: * @return this object
0594: */
0595: @Override
0596: public AccessibleValue getAccessibleValue() {
0597: return this ;
0598: }
0599:
0600: //
0601: // AccessibleValue methods
0602: //
0603:
0604: /**
0605: * Get the value of this object as a Number.
0606: *
0607: * @return value of the object -- can be <code>null</code> if this object does not
0608: * have a value
0609: */
0610: public Number getCurrentAccessibleValue() {
0611: if (isVisible()) {
0612: return 1;
0613: } else {
0614: return 0;
0615: }
0616: }
0617:
0618: /**
0619: * Set the value of this object as a Number.
0620: *
0621: * @return <code>true</code> if the value was set
0622: */
0623: public boolean setCurrentAccessibleValue(Number n) {
0624: if (n instanceof Integer) {
0625: if (n.intValue() == 0)
0626: setVisible(true);
0627: else
0628: setVisible(false);
0629: return true;
0630: } else {
0631: return false;
0632: }
0633: }
0634:
0635: /**
0636: * Get the minimum value of this object as a Number.
0637: *
0638: * @return Minimum value of the object; <code>null</code> if this object does not
0639: * have a minimum value
0640: */
0641: public Number getMinimumAccessibleValue() {
0642: return Integer.MIN_VALUE;
0643: }
0644:
0645: /**
0646: * Get the maximum value of this object as a Number.
0647: *
0648: * @return Maximum value of the object; <code>null</code> if this object does not
0649: * have a maximum value
0650: */
0651: public Number getMaximumAccessibleValue() {
0652: return Integer.MAX_VALUE;
0653: }
0654: }
0655:
0656: /**
0657: * Shows the popup. By default, it will show right below the owner.
0658: */
0659: public void showPopup() {
0660: //David: To account for a popup within a popup, let the caller specify an owner
0661: // different from the RootPaneContainer(Applet) or ContentContainer.
0662: // showPopup(new Insets(0, 0, 0, 0));
0663: showPopup(new Insets(0, 0, 0, 0), null);
0664: }
0665:
0666: /**
0667: * Shows the popup. By default, it will show right below the owner after considering the insets.
0668: *
0669: * @param owner the popup window's owner; if unspecified, it will default to the
0670: * RootPaneContainer(Applet) or ContentContainer
0671: */
0672: public void showPopup(Component owner) {
0673: showPopup(new Insets(0, 0, 0, 0), owner);
0674: }
0675:
0676: /**
0677: * Shows the popup. By default, it will show right below the owner after considering the insets.
0678: *
0679: * @param insets the popup's insets
0680: * RootPaneContainer(Applet) or ContentContainer
0681: */
0682: public void showPopup(Insets insets) {
0683: showPopup(insets, null);
0684: }
0685:
0686: protected Insets _insets = null;
0687:
0688: /**
0689: * Shows the popup. By default, it will show right below the owner after considering the insets. Please note, if the owner
0690: * is not displayed (isShowing returns false), the popup will not be displayed either.
0691: *
0692: * @param insets the popup's insets
0693: * @param owner the popup window's owner; if unspecified, it will default to the
0694: * RootPaneContainer(Applet) or ContentContainer
0695: */
0696: public void showPopup(Insets insets, Component owner) {
0697: _insets = insets;
0698: Component actualOwner = (owner != null) ? owner : getOwner();
0699: if (actualOwner != null && actualOwner.isShowing()) {
0700: Point point = actualOwner.getLocationOnScreen();
0701: internalShowPopup(point.x, point.y, actualOwner);
0702: } else {
0703: showPopup(SwingConstants.CENTER);
0704: }
0705: }
0706:
0707: /**
0708: * Calculates the popup location.
0709: *
0710: * @param point owner is top-left coordinate relative to screen.
0711: * @param size the size of the popup window.
0712: * @param owner the owner
0713: * @return new popup location. By default, it will return the coordinate of the bottom-left corner of owner.
0714: */
0715: protected Point getPopupLocation(Point point, Dimension size,
0716: Component owner) {
0717: Component actualOwner = (owner != null) ? owner : getOwner();
0718: Dimension ownerSize = actualOwner != null ? actualOwner
0719: .getSize() : new Dimension(0, 0);
0720: Dimension screenSize = PortingUtils.getScreenSize(owner);
0721:
0722: if (size.width == 0) {
0723: size = this .getPreferredSize();
0724: }
0725:
0726: Point p = new Point(point.x + _insets.left, point.y
0727: + ownerSize.height - _insets.bottom);
0728: int left = p.x + size.width;
0729: int bottom = p.y + size.height;
0730:
0731: if (left > screenSize.width) {
0732: p.x -= left - screenSize.width; // move left so that the whole popup can fit in
0733: }
0734:
0735: if (bottom > screenSize.height) {
0736: p.y = point.y + _insets.top - size.height; // flip to upward
0737: if (isResizable()) {
0738: setupResizeCorner(Resizable.UPPER_RIGHT);
0739: }
0740: } else {
0741: if (isResizable()) {
0742: setupResizeCorner(Resizable.LOWER_RIGHT);
0743: }
0744: }
0745: return p;
0746: }
0747:
0748: /**
0749: * Setup Resizable's ResizeCorner.
0750: *
0751: * @param corner the corner.
0752: */
0753: public void setupResizeCorner(int corner) {
0754: switch (corner) {
0755: case Resizable.UPPER_RIGHT:
0756: _window.getResizable().setResizableCorners(
0757: Resizable.UPPER_RIGHT);
0758: JideSwingUtilities.setRecursively(this ,
0759: new JideSwingUtilities.Handler() {
0760: public boolean condition(Component c) {
0761: return c instanceof JideScrollPane;
0762: }
0763:
0764: public void action(Component c) {
0765: Resizable.ResizeCorner corner = new Resizable.ResizeCorner(
0766: Resizable.UPPER_RIGHT);
0767: corner.addMouseListener(_window
0768: .getResizable()
0769: .getMouseInputAdapter());
0770: corner.addMouseMotionListener(_window
0771: .getResizable()
0772: .getMouseInputAdapter());
0773: ((JideScrollPane) c)
0774: .setScrollBarCorner(
0775: JideScrollPane.VERTICAL_TOP,
0776: corner);
0777: ((JideScrollPane) c).setScrollBarCorner(
0778: JideScrollPane.VERTICAL_BOTTOM,
0779: null);
0780: }
0781:
0782: public void postAction(Component c) {
0783:
0784: }
0785: });
0786: break;
0787: case Resizable.LOWER_RIGHT:
0788: _window.getResizable().setResizableCorners(
0789: Resizable.LOWER_RIGHT);
0790: JideSwingUtilities.setRecursively(this ,
0791: new JideSwingUtilities.Handler() {
0792: public boolean condition(Component c) {
0793: return c instanceof JideScrollPane;
0794: }
0795:
0796: public void action(Component c) {
0797: Resizable.ResizeCorner corner = new Resizable.ResizeCorner(
0798: Resizable.LOWER_RIGHT);
0799: corner.addMouseListener(_window
0800: .getResizable()
0801: .getMouseInputAdapter());
0802: corner.addMouseMotionListener(_window
0803: .getResizable()
0804: .getMouseInputAdapter());
0805: ((JideScrollPane) c).setScrollBarCorner(
0806: JideScrollPane.VERTICAL_BOTTOM,
0807: corner);
0808: ((JideScrollPane) c).setScrollBarCorner(
0809: JideScrollPane.VERTICAL_TOP, null);
0810: }
0811:
0812: public void postAction(Component c) {
0813: }
0814: });
0815: break;
0816: default:
0817: _window.getResizable().setResizableCorners(corner);
0818: break;
0819: }
0820: }
0821:
0822: public static Component getTopLevelAncestor(Component component) {
0823: if (component == null) {
0824: return null;
0825: }
0826:
0827: for (Component p = component; p != null; p = p.getParent()) {
0828: if (p instanceof Window || p instanceof Applet) {
0829: return p;
0830: }
0831: }
0832: return null;
0833: }
0834:
0835: /**
0836: * Shows the popup at the specified location relative to the screen. The valid locations are:
0837: * <ul>
0838: * <li>{@link SwingConstants#CENTER}
0839: * <li>{@link SwingConstants#SOUTH}
0840: * <li>{@link SwingConstants#NORTH}
0841: * <li>{@link SwingConstants#WEST}
0842: * <li>{@link SwingConstants#EAST}
0843: * <li>{@link SwingConstants#NORTH_EAST}
0844: * <li>{@link SwingConstants#NORTH_WEST}
0845: * <li>{@link SwingConstants#SOUTH_EAST}
0846: * <li>{@link SwingConstants#SOUTH_WEST}
0847: * </ul>
0848: * The actual location will be based on the main screen bounds. Say if the location is SwingConstants.SOUTH_EAST,
0849: * the popup will appear at the south west corner of main screen with 10 pixels to the border. The 10 pixel is the
0850: * default value. You can change it by setting {@link #DISTANCE_TO_SCREEN_BORDER}.
0851: *
0852: * @param location the new location.
0853: */
0854: public void showPopup(int location) {
0855: showPopup(location, null);
0856: }
0857:
0858: /**
0859: * Shows the popup at the specified location relative to the owner. The valid locations are:
0860: * <ul>
0861: * <li>{@link SwingConstants#CENTER}
0862: * <li>{@link SwingConstants#SOUTH}
0863: * <li>{@link SwingConstants#NORTH}
0864: * <li>{@link SwingConstants#WEST}
0865: * <li>{@link SwingConstants#EAST}
0866: * <li>{@link SwingConstants#NORTH_EAST}
0867: * <li>{@link SwingConstants#NORTH_WEST}
0868: * <li>{@link SwingConstants#SOUTH_EAST}
0869: * <li>{@link SwingConstants#SOUTH_WEST}
0870: * </ul>
0871: * The actual location will be based on the owner's bounds. Say if the location is SwingConstants.SOUTH_EAST,
0872: * the popup will appear at the south west corner of owner with 10 pixels to the border. The 10 pixel is the
0873: * default value. You can change it by setting {@link #DISTANCE_TO_SCREEN_BORDER}.
0874: *
0875: * @param location the new location
0876: * @param owner the popup window's owner; if unspecified, it will default to the
0877: * RootPaneContainer(Applet) or ContentContainer
0878: */
0879: public void showPopup(int location, Component owner) {
0880: setDetached(true);
0881: Rectangle screenDim = getDisplayScreenBounds(owner);
0882: // Get the bounds of the splash window
0883: Dimension size = getPreferredSize();
0884: Point displayLocation = getDisplayStartLocation(screenDim,
0885: size, location);
0886: internalShowPopup(displayLocation.x, displayLocation.y, owner);
0887: }
0888:
0889: protected Point getDisplayStartLocation(Rectangle screenDim,
0890: Dimension size, int location) {
0891: switch (location) {
0892: case SwingConstants.CENTER:
0893: return new Point(screenDim.x
0894: + (screenDim.width - size.width) / 2, screenDim.y
0895: + (screenDim.height - size.height) / 2);
0896: case SwingConstants.SOUTH:
0897: return new Point(screenDim.x
0898: + (screenDim.width - size.width) / 2, screenDim.y
0899: + screenDim.height - size.height
0900: - DISTANCE_TO_SCREEN_BORDER);
0901: case SwingConstants.NORTH:
0902: return new Point(screenDim.x
0903: + (screenDim.width - size.width) / 2, screenDim.y
0904: + DISTANCE_TO_SCREEN_BORDER);
0905: case SwingConstants.EAST:
0906: return new Point(screenDim.x + screenDim.width - size.width
0907: - DISTANCE_TO_SCREEN_BORDER, screenDim.y
0908: + (screenDim.height - size.height) / 2);
0909: case SwingConstants.WEST:
0910: return new Point(screenDim.x + DISTANCE_TO_SCREEN_BORDER,
0911: screenDim.y + (screenDim.height - size.height) / 2);
0912: case SwingConstants.SOUTH_WEST:
0913: return new Point(screenDim.x + DISTANCE_TO_SCREEN_BORDER,
0914: screenDim.y + screenDim.height - size.height
0915: - DISTANCE_TO_SCREEN_BORDER);
0916: case SwingConstants.NORTH_EAST:
0917: return new Point(screenDim.x + screenDim.width - size.width
0918: - DISTANCE_TO_SCREEN_BORDER, screenDim.y
0919: + DISTANCE_TO_SCREEN_BORDER);
0920: case SwingConstants.NORTH_WEST:
0921: return new Point(screenDim.x + DISTANCE_TO_SCREEN_BORDER,
0922: screenDim.y + DISTANCE_TO_SCREEN_BORDER);
0923: case SwingConstants.SOUTH_EAST:
0924: default:
0925: return new Point(screenDim.x + screenDim.width - size.width
0926: - DISTANCE_TO_SCREEN_BORDER, screenDim.y
0927: + screenDim.height - size.height
0928: - DISTANCE_TO_SCREEN_BORDER);
0929: }
0930: }
0931:
0932: protected Rectangle getDisplayScreenBounds(Component owner) {
0933: Rectangle screenDim;
0934: if (owner != null && owner.isShowing()) {
0935: screenDim = owner.getBounds();
0936: Point p = owner.getLocationOnScreen();
0937: screenDim.x = p.x;
0938: screenDim.y = p.y;
0939: } else {
0940: screenDim = PortingUtils.getLocalScreenBounds();
0941: }
0942: return screenDim;
0943: }
0944:
0945: public void packPopup() {
0946: if (_window == null || !_window.isVisible()) {
0947: return;
0948: }
0949: _window.pack();
0950: }
0951:
0952: //David: To account for a popup within a popup, let the caller specify an owner
0953: // different from the RootPaneContainer(Applet) or ContentContainer.
0954:
0955: protected void internalShowPopup(int x, int y) {
0956: internalShowPopup(x, y, null);
0957: }
0958:
0959: protected void internalShowPopup(int x, int y, Component owner) {
0960: _actualOwner = owner;
0961: if (_actualOwner != null) {
0962: _actualOwnerLocation = _actualOwner.getLocationOnScreen();
0963: }
0964: createWindow(owner, x, y);
0965: showPopupImmediately();
0966: }
0967:
0968: protected void createWindow(Component owner, int x, int y) {
0969: if (_window == null) {
0970: _window = createPopupContainer(owner);
0971: installListeners();
0972: installBorder();
0973: }
0974:
0975: if (_previousSize != null) {
0976: _window.setSize(_previousSize);
0977: _previousSize = null;
0978: } else {
0979: _window.pack();
0980: }
0981:
0982: if (_insets != null) {
0983: Point p = getPopupLocation(new Point(x, y), _window
0984: .getSize(), owner);
0985: x = p.x;
0986: y = p.y;
0987: }
0988:
0989: _window.setLocation(x, y);
0990: }
0991:
0992: /**
0993: * Shows the popup at the specified x and y coordinates.
0994: *
0995: * @param x the x position.
0996: * @param y the y position.
0997: */
0998: public void showPopup(int x, int y) {
0999: showPopup(x, y, null);
1000: }
1001:
1002: /**
1003: * Shows the popup at the specified x and y coordinates.
1004: *
1005: * @param x the x position.
1006: * @param y the y position.
1007: * @param owner the popup window's owner; if unspecified, it will default to the
1008: * RootPaneContainer(Applet) or ContentContainer
1009: */
1010: public void showPopup(int x, int y, Component owner) {
1011: internalShowPopup(x, y, owner);
1012: }
1013:
1014: protected ResizableWindow createPopupContainer() {
1015: return createPopupContainer(null);
1016: }
1017:
1018: protected static Frame getFrame(Component c) {
1019: Component w = c;
1020:
1021: while (!(w instanceof Frame) && (w != null)) {
1022: w = w.getParent();
1023: }
1024: return (Frame) w;
1025: }
1026:
1027: protected ResizableWindow createPopupContainer(Component owner) {
1028: ResizableWindow container;
1029: Component actualOwner = (owner != null) ? owner : getOwner();
1030: Component topLevelAncestor = getTopLevelAncestor(actualOwner);
1031: if (topLevelAncestor instanceof Frame) {
1032: container = new ResizableWindow((Frame) topLevelAncestor);
1033: } else if (topLevelAncestor instanceof Window) {
1034: container = new ResizableWindow((Window) topLevelAncestor);
1035: } else {
1036: Frame frame = getFrame(actualOwner);
1037: container = new ResizableWindow(frame);
1038: }
1039: container.getContentPane().add(this );
1040: return container;
1041: }
1042:
1043: protected void installListeners() {
1044: //David: Adding the MouseEventHandler synchronously causes a strange
1045: // initialization sequence if the popup owner is a tree/table editor:
1046: //
1047: // - The editor gets moved to its initial location based on the cell location,
1048: // generating a COMPONENT_MOVED ComponentEvent.
1049: // - You add the MouseEventHandler, which includes a ComponentEvent listener on
1050: // the owner's hierarcy, including the editor.
1051: // - ComponentEvent is asynchronous. The ComponentEvent generated from moving
1052: // the editor to its initial position was placed on the event queue. So the
1053: // ComponentEvent gets handled by the MouseEventHandler's ComponentEvent
1054: // listener, even though it happened before the listener was added.
1055: //
1056: // I fixed it by calling addMouseEventHandler asynchronously, guaranteeing that
1057: // any previously-generated event it may listen for has been removed from the
1058: // event queue before the listener is added.
1059: //
1060: // This causes a couple of minor problems:
1061: //
1062: // - It is possible that hidePopupImmediately can be called synchronously before
1063: // the asynchronous call to addMouseEventHandler is executed. This can cause
1064: // NPEs because the listener handler methods dereference _window, which is set
1065: // to null in hidePopupImmediately. So I added null checks on _window in the
1066: // listener handler methods.
1067: //
1068: // - The removeMouseEventHandler method is called from hidePopupImmediately.
1069: // That means it could be called before addMouseEventHandler is executed
1070: // asynchronously; that would result in the listener not getting removed. So
1071: // I changed the removeMouseEventHandler to an asynchronous call, as well.
1072: //
1073: // This issue appeared in the 1.8.3 release because you changed the
1074: // COMPONENT_MOVED handler to hide the popup instead of moving it. Since you
1075: // made this an option in the 1.8.4 release, all of this asynchronous code is
1076: // needed.
1077: //
1078: // addMouseEventHandler()
1079: SwingUtilities.invokeLater(new Runnable() {
1080: public void run() {
1081: addMouseEventHandler();
1082: }
1083: });
1084: _componentListener = new ComponentAdapter() {
1085: @Override
1086: public void componentHidden(ComponentEvent e) {
1087: hidePopup();
1088: }
1089: };
1090: registerKeyboardAction(new ActionListener() {
1091: public void actionPerformed(ActionEvent e) {
1092: hidePopupImmediately(true);
1093: if (getOwner() != null) {
1094: getOwner().requestFocus();
1095: }
1096: }
1097: }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
1098: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1099: _window.addComponentListener(_componentListener);
1100: _windowListener = new WindowAdapter() {
1101: @Override
1102: public void windowClosing(WindowEvent e) {
1103: hidePopup();
1104: }
1105: };
1106: _window.addWindowListener(_windowListener);
1107:
1108: if (getOwner() != null) {
1109: _ownerComponentListener = new ComponentAdapter() {
1110: @Override
1111: public void componentHidden(ComponentEvent e) {
1112: ancestorHidden();
1113: }
1114:
1115: @Override
1116: public void componentMoved(ComponentEvent e) {
1117: if (_actualOwnerLocation == null
1118: || _actualOwner == null
1119: || !_actualOwner.getLocationOnScreen()
1120: .equals(_actualOwnerLocation)) {
1121: ancestorMoved();
1122: }
1123: }
1124: };
1125: getOwner().addComponentListener(_ownerComponentListener);
1126: _hierarchyListener = new HierarchyListener() {
1127: public void hierarchyChanged(HierarchyEvent e) {
1128: ancestorHidden();
1129: }
1130: };
1131: getOwner().addHierarchyListener(_hierarchyListener);
1132: }
1133:
1134: _popupResizeListener = new ComponentAdapter() {
1135: @Override
1136: public void componentResized(ComponentEvent e) {
1137: removeComponentListener(_popupResizeListener);
1138: contentResized();
1139: addComponentListener(_popupResizeListener);
1140: }
1141: };
1142: addComponentListener(_popupResizeListener);
1143: }
1144:
1145: protected void contentResized() {
1146: // pack is good enough to replace all code above
1147: _window.pack();
1148: }
1149:
1150: protected void installBorder() {
1151: if (getPopupBorder() != null) {
1152: if (isResizable()) {
1153: _window.getResizable().setResizableCorners(
1154: Resizable.ALL);
1155: } else {
1156: _window.getResizable().setResizableCorners(
1157: Resizable.NONE);
1158: }
1159: _window.setBorder(getPopupBorder());
1160: } else {
1161: if (isDetached()) {
1162: if (isResizable()) {
1163: _window.getResizable().setResizableCorners(
1164: Resizable.ALL);
1165: } else {
1166: _window.getResizable().setResizableCorners(
1167: Resizable.NONE);
1168: }
1169: _window.setBorder(UIDefaultsLookup
1170: .getBorder("Resizable.resizeBorder"));
1171: } else {
1172: if (isResizable()) {
1173: _window.getResizable().setResizableCorners(
1174: Resizable.RIGHT | Resizable.LOWER
1175: | Resizable.LOWER_RIGHT);
1176: } else {
1177: _window.getResizable().setResizableCorners(
1178: Resizable.NONE);
1179: }
1180: _window.setBorder(UIDefaultsLookup
1181: .getBorder("PopupMenu.border"));
1182: }
1183: }
1184: }
1185:
1186: protected void showPopupImmediately() {
1187: if (_window == null) {
1188: return;
1189: }
1190:
1191: firePopupMenuWillBecomeVisible();
1192:
1193: // only when the focus cycle root is true, the component in JidePopup won't request focus automatically.
1194: if (!isFocusable() && getDefaultFocusComponent() == null) {
1195: _window.setFocusableWindowState(false);
1196: }
1197:
1198: if (!_window.isVisible()) {
1199: _window.pack();
1200: _window.setVisible(true);
1201: _window.toFront();
1202: }
1203:
1204: firePropertyChange("visible", Boolean.FALSE, Boolean.TRUE);
1205:
1206: if (isFocusable() || getDefaultFocusComponent() != null) {
1207: // only allow window to have focus when there is a default focus component.
1208: _window.setFocusable(true);
1209: if (getDefaultFocusComponent() != null) {
1210: Runnable runnable = new Runnable() {
1211: public void run() {
1212: getDefaultFocusComponent().requestFocus();
1213: }
1214: };
1215: SwingUtilities.invokeLater(runnable);
1216: }
1217: }
1218:
1219: if (getTimeout() != 0) {
1220: startTimeoutTimer();
1221: }
1222: }
1223:
1224: protected void movePopup() {
1225: if (isPopupVisible()) {
1226: if (!isDetached() && _actualOwner != null) {
1227: if (_insets != null) {
1228: showPopup(_insets, _actualOwner);
1229: } else if (_actualOwnerLocation != null) {
1230: Point newLocation = _actualOwner
1231: .getLocationOnScreen();
1232: Point p = _window.getLocationOnScreen();
1233: p.x += newLocation.x - _actualOwnerLocation.x;
1234: p.y += newLocation.y - _actualOwnerLocation.y;
1235: showPopup(p.x, p.y, _actualOwner);
1236: }
1237: }
1238: }
1239: }
1240:
1241: private boolean _isDragging = false;
1242:
1243: /**
1244: * Mouse location related the frame it drags.
1245: */
1246: private double _relativeX, _relativeY;
1247:
1248: private Point _startPoint;
1249: private Window _currentWindow;
1250:
1251: protected void endDragging() {
1252: _isDragging = false;
1253: if (_currentWindow instanceof JWindow
1254: && ((JWindow) _currentWindow).getGlassPane() != null) {
1255: ((JWindow) _currentWindow).getGlassPane().setVisible(false);
1256: ((JWindow) _currentWindow).getGlassPane().setCursor(
1257: Cursor.getDefaultCursor());
1258: } else if (_currentWindow instanceof JDialog
1259: && ((JDialog) _currentWindow).getGlassPane() != null) {
1260: ((JDialog) _currentWindow).getGlassPane().setVisible(false);
1261: ((JDialog) _currentWindow).getGlassPane().setCursor(
1262: Cursor.getDefaultCursor());
1263: }
1264: _currentWindow = null;
1265: _relativeX = 0;
1266: _relativeY = 0;
1267: }
1268:
1269: protected void beginDragging(JComponent f, int mouseX, int mouseY,
1270: double relativeX, double relativeY) {
1271: _relativeX = relativeX;
1272: _relativeY = relativeY;
1273:
1274: if (f.getTopLevelAncestor() instanceof JWindow)
1275: _currentWindow = (JWindow) f.getTopLevelAncestor();
1276: if (f.getTopLevelAncestor() instanceof JDialog)
1277: _currentWindow = (JDialog) f.getTopLevelAncestor();
1278:
1279: if (_currentWindow instanceof JWindow
1280: && ((JWindow) _currentWindow).getGlassPane() != null) {
1281: ((JWindow) _currentWindow).getGlassPane().setVisible(true);
1282: ((JWindow) _currentWindow).getGlassPane().setCursor(
1283: Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
1284: } else if (_currentWindow instanceof JDialog
1285: && ((JDialog) _currentWindow).getGlassPane() != null) {
1286: ((JDialog) _currentWindow).getGlassPane().setVisible(true);
1287: ((JDialog) _currentWindow).getGlassPane().setCursor(
1288: Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
1289: }
1290:
1291: _isDragging = true;
1292: if (isDetached() && getOwner() != null) {
1293: _startPoint = getOwner().getLocationOnScreen();
1294: _startPoint.y += getOwner().getHeight();
1295: } else {
1296: _startPoint = _currentWindow.getLocationOnScreen();
1297: }
1298: }
1299:
1300: protected boolean isDragging() {
1301: return _isDragging;
1302: }
1303:
1304: static void convertPointToScreen(Point p, Component c,
1305: boolean startInFloat) {
1306: int x, y;
1307:
1308: do {
1309: if (c instanceof JComponent) {
1310: x = c.getX();
1311: y = c.getY();
1312: } else if (c instanceof java.applet.Applet
1313: || (startInFloat ? c instanceof Window
1314: : c instanceof JFrame)) {
1315: try {
1316: Point pp = c.getLocationOnScreen();
1317: x = pp.x;
1318: y = pp.y;
1319: } catch (IllegalComponentStateException icse) {
1320: x = c.getX();
1321: y = c.getY();
1322: }
1323: } else {
1324: x = c.getX();
1325: y = c.getY();
1326: }
1327:
1328: p.x += x;
1329: p.y += y;
1330:
1331: if ((startInFloat ? c instanceof Window
1332: : c instanceof JFrame)
1333: || c instanceof java.applet.Applet)
1334: break;
1335: c = c.getParent();
1336: } while (c != null);
1337: }
1338:
1339: protected void drag(JComponent f, int newX, int newY,
1340: int mouseModifiers) {
1341: int x = newX - (int) (_currentWindow.getWidth() * _relativeX);
1342: int y = newY - (int) (_currentWindow.getHeight() * _relativeY);
1343: Rectangle bounds = new Rectangle(x, y, _currentWindow
1344: .getWidth(), _currentWindow.getHeight());
1345:
1346: Rectangle screenBounds = PortingUtils
1347: .getScreenBounds(_currentWindow);
1348: if (bounds.y + bounds.height > screenBounds.y
1349: + screenBounds.height) {
1350: bounds.y = screenBounds.y + screenBounds.height
1351: - bounds.height;
1352: }
1353: if (bounds.y < screenBounds.y) {
1354: bounds.y = screenBounds.y;
1355: }
1356:
1357: if (isAttachable()
1358: && isWithinAroundArea(new Point(x, y), _startPoint)) {
1359: _currentWindow.setLocation(_startPoint);
1360: setDetached(false);
1361: } else {
1362: _currentWindow.setLocation(x, y);
1363: setDetached(true);
1364: }
1365: }
1366:
1367: final int AROUND_SIZE = 10;
1368:
1369: boolean isWithinAroundArea(Point p, Point newPoint) {
1370: Rectangle rect = new Rectangle(p.x - AROUND_SIZE, p.y
1371: - AROUND_SIZE, p.x + AROUND_SIZE, p.y + AROUND_SIZE);
1372: return rect.contains(newPoint);
1373: }
1374:
1375: static boolean isAncestorOf(Component component, Object ancester) {
1376: if (component == null) {
1377: return false;
1378: }
1379:
1380: for (Component p = component; p != null; p = p.getParent()) {
1381: if (p == ancester) {
1382: return true;
1383: }
1384: }
1385: return false;
1386: }
1387:
1388: // private AWTEventListener _awtEventListener = new AWTEventListener() {
1389: // public void eventDispatched(AWTEvent event) {
1390: // if (event instanceof MouseEvent) {
1391: // if (event.getID() == MouseEvent.MOUSE_PRESSED) {
1392: // MouseEvent e = (MouseEvent) event;
1393: // Object source = SwingUtilities.getDeepestComponentAt(e.getComponent(), e.getX(), e.getY());
1394: // if (!isAncestorOf((Container) source, JidePopup.this.getTopLevelAncestor())) { // todo: add a flag to not hidepopup in some cases
1395: // hidePopup();
1396: // }
1397: // else {
1398: // Point point = SwingUtilities.convertPoint((Component) e.getSource(), e.getPoint(), JidePopup.this);
1399: //
1400: // Rectangle startingBounds = JidePopup.this.getTopLevelAncestor().getBounds();
1401: // _relativeX = (double) point.x / startingBounds.width;
1402: // _relativeY = (double) point.y / startingBounds.height;
1403: //
1404: // Point screenPoint = new Point(e.getX(), e.getY());
1405: // JidePopup.convertPointToScreen(screenPoint, (Component) e.getSource(), true);
1406: //
1407: // // drag on gripper
1408: // if (source == JidePopup.this.getUI().getGripper()) {
1409: // beginDragging(JidePopup.this, screenPoint.x, screenPoint.y, _relativeX, _relativeY);
1410: // e.consume();
1411: // }
1412: // }
1413: // }
1414: // else if (event.getID() == MouseEvent.MOUSE_DRAGGED) {
1415: // if (isDragging()) {
1416: // MouseEvent e = (MouseEvent) event;
1417: // Point screenPoint = e.getPoint();
1418: // convertPointToScreen(screenPoint, ((Component) e.getSource()), true);
1419: // drag(null, screenPoint.x, screenPoint.y, e.getModifiersEx());
1420: // e.consume();
1421: // }
1422: // }
1423: // else if (event.getID() == MouseEvent.MOUSE_RELEASED) {
1424: // if (isDragging()) {
1425: // MouseEvent e = (MouseEvent) event;
1426: // endDragging();
1427: // e.consume();
1428: // }
1429: // }
1430: // else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1431: // if (_window.isAncestorOf(((Component) event.getSource())) && getTimeout() != 0) {
1432: // stopTimeoutTimer();
1433: // }
1434: // }
1435: // else if (event.getID() == MouseEvent.MOUSE_EXITED) {
1436: // if (_window.isAncestorOf(((Component) event.getSource())) && getTimeout() != 0) {
1437: // startTimeoutTimer();
1438: // }
1439: // }
1440: // }
1441: // else if (event instanceof WindowEvent) {
1442: // WindowEvent e = (WindowEvent) event;
1443: // if (e.getSource() != JidePopup.this.getTopLevelAncestor() && isAncestorOf(getOwner(), e.getWindow())) {
1444: // if (e.getID() == WindowEvent.WINDOW_CLOSING || e.getID() == WindowEvent.WINDOW_ICONIFIED) {
1445: // hidePopup();
1446: // }
1447: // }
1448: // }
1449: // else if (event instanceof ComponentEvent) {
1450: // ComponentEvent e = (ComponentEvent) event;
1451: // if (e.getID() == ComponentEvent.COMPONENT_HIDDEN && isAncestorOf(getOwner(), e.getSource())) {
1452: // hidePopup();
1453: // }
1454: // else if (e.getID() == ComponentEvent.COMPONENT_MOVED && isAncestorOf(getOwner(), e.getSource())) {
1455: // movePopup();
1456: // }
1457: // }
1458: // }
1459: // };
1460:
1461: private AWTEventListener _awtEventListener = new AWTEventListener() {
1462: public void eventDispatched(AWTEvent event) {
1463: if ("sun.awt.UngrabEvent"
1464: .equals(event.getClass().getName())) {
1465: // this is really a hack so that it can detect event when closing the windows in Eclise RCP env.
1466: // Popup should be canceled in case of ungrab event
1467: hidePopupImmediately(true);
1468: return;
1469: }
1470:
1471: if (event instanceof MouseEvent) {
1472: if (event.getID() == MouseEvent.MOUSE_PRESSED) {
1473: handleMousePressed((MouseEvent) event);
1474: } else if (event.getID() == MouseEvent.MOUSE_DRAGGED) {
1475: handleMouseDragged((MouseEvent) event);
1476: } else if (event.getID() == MouseEvent.MOUSE_RELEASED) {
1477: handleMouseReleased((MouseEvent) event);
1478: } else if (event.getID() == MouseEvent.MOUSE_ENTERED) {
1479: handleMouseEntered((MouseEvent) event);
1480: } else if (event.getID() == MouseEvent.MOUSE_EXITED) {
1481: handleMouseExited((MouseEvent) event);
1482: }
1483: } else if (event instanceof WindowEvent) {
1484: handleWindowEvent((WindowEvent) event);
1485: } else if (event instanceof ComponentEvent) {
1486: handleComponentEvent((ComponentEvent) event);
1487: }
1488: }
1489: };
1490:
1491: protected void handleMousePressed(MouseEvent e) {
1492: Object source = SwingUtilities.getDeepestComponentAt(e
1493: .getComponent(), e.getX(), e.getY());
1494: Component component = (Component) source;
1495: if (!isAncestorOf(component, getTopLevelAncestor())) {
1496: if (isExcludedComponent(component)) {
1497: return;
1498: }
1499: ancestorHidden();
1500: } else {
1501: Point point = SwingUtilities.convertPoint(component, e
1502: .getPoint(), this );
1503:
1504: Rectangle startingBounds = getTopLevelAncestor()
1505: .getBounds();
1506: _relativeX = (double) point.x / startingBounds.width;
1507: _relativeY = (double) point.y / startingBounds.height;
1508:
1509: Point screenPoint = new Point(e.getX(), e.getY());
1510: convertPointToScreen(screenPoint, component, true);
1511:
1512: // drag on gripper
1513: if (source == getUI().getGripper()) {
1514: beginDragging(this , screenPoint.x, screenPoint.y,
1515: _relativeX, _relativeY);
1516: e.consume();
1517: }
1518: }
1519: }
1520:
1521: protected void handleMouseReleased(MouseEvent e) {
1522: if (isDragging()) {
1523: endDragging();
1524: e.consume();
1525: }
1526: }
1527:
1528: protected void handleMouseDragged(MouseEvent e) {
1529: if (isDragging()) {
1530: Point screenPoint = e.getPoint();
1531: convertPointToScreen(screenPoint, ((Component) e
1532: .getSource()), true);
1533: drag(null, screenPoint.x, screenPoint.y, e.getModifiersEx());
1534: e.consume();
1535: }
1536: }
1537:
1538: protected void handleMouseEntered(MouseEvent e) {
1539: if ((_window != null)
1540: && _window.isAncestorOf(((Component) e.getSource()))
1541: && getTimeout() != 0) {
1542: stopTimeoutTimer();
1543: }
1544: }
1545:
1546: protected void handleMouseExited(MouseEvent e) {
1547: // if (_window.isAncestorOf(((Component) e.getSource())) && getTimeout() != 0) {
1548: if ((_window != null)
1549: && _window.isAncestorOf(((Component) e.getSource()))
1550: && getTimeout() != 0) {
1551: startTimeoutTimer();
1552: }
1553: }
1554:
1555: private static boolean checkedUnpostPopup;
1556: private static boolean unpostPopup;
1557:
1558: private static boolean doUnpostPopupOnDeactivation() {
1559: if (!checkedUnpostPopup) {
1560: unpostPopup = (Boolean) java.security.AccessController
1561: .doPrivileged(new java.security.PrivilegedAction() {
1562: public Object run() {
1563: String pKey = "sun.swing.unpostPopupsOnWindowDeactivation";
1564: String value = System.getProperty(pKey,
1565: "true");
1566: return Boolean.valueOf(value);
1567: }
1568: });
1569: checkedUnpostPopup = true;
1570: }
1571: return unpostPopup;
1572: }
1573:
1574: protected void handleWindowEvent(WindowEvent e) {
1575: if (e.getSource() != getTopLevelAncestor()
1576: && isAncestorOf(getOwner(), e.getWindow())) { // check if it's embeded in browser
1577: if (e.getID() == WindowEvent.WINDOW_CLOSING
1578: || e.getID() == WindowEvent.WINDOW_ICONIFIED) {
1579: hidePopup(true);
1580: }
1581:
1582: // The cases for window deactivated are too complex. Let's not consider it for now.
1583: // One case the code below didn't consider is an dialog is shown while in another thread, alert is showing and the owner is the frame.
1584: // At the end, frame received deactivated event and cause alert to hide immediately.
1585: //
1586: // 1/2/07: we have to put this code back because combobox's popup not hiding when the window is deactivated.
1587: // But I also copied the code from MenuSelectionManager to check doUnpostPopupOnDeactivation. Hopefully that addresses the issue above.
1588: else if (isTransient()
1589: && e.getID() == WindowEvent.WINDOW_DEACTIVATED
1590: && !(e.getWindow() instanceof EmbeddedFrame)) {
1591: // TODO: don't why DEACTIVATED event is fired when popup is showing only if the applet is in browser mode.
1592: // so the best solution is to find out why. For now just skip the case if the frame is a EmbeddedFrame.
1593: if (doUnpostPopupOnDeactivation()) {
1594: if (e.getOppositeWindow() != getTopLevelAncestor()) {
1595: hidePopup(true);
1596: }
1597: }
1598: }
1599: }
1600: }
1601:
1602: /**
1603: * This method will process component event. By default, if popup's ancestor is hidden, we will hide the popup as well
1604: * if the popup is transient (isTransient returns true). If popup's ancestor is moved, we will either move or hide
1605: * the popup depending on {@link #getDefaultMoveOperation()} value.
1606: *
1607: * @param e the ComponentEvent.
1608: */
1609: protected void handleComponentEvent(ComponentEvent e) {
1610: if (e.getID() == ComponentEvent.COMPONENT_HIDDEN
1611: && isAncestorOf(getOwner(), e.getSource())) {
1612: ancestorHidden();
1613: } else if (e.getID() == ComponentEvent.COMPONENT_MOVED
1614: && isAncestorOf(getOwner(), e.getSource())) {
1615: // this line is for Linux because the jframe moves when combobox is shown inside JidePopup
1616: // System.out.println("_actualOwnerLocation " + _actualOwnerLocation + " _actualOwner " + _actualOwner + " _actualOwner.getLocationOnScreen() " + (_actualOwner != null ? _actualOwner.getLocationOnScreen() : null));
1617: if (_actualOwnerLocation == null
1618: || _actualOwner == null
1619: || !_actualOwner.getLocationOnScreen().equals(
1620: _actualOwnerLocation)) {
1621: ancestorMoved();
1622: }
1623: }
1624: }
1625:
1626: /**
1627: * This method will process component hidden event for the popup's ancestor.
1628: * By default we will hide the popup immediately. You can override this to customize
1629: * the behavior.
1630: */
1631: protected void ancestorHidden() {
1632: if (isTransient()) {
1633: hidePopupImmediately(true);
1634: }
1635: }
1636:
1637: /**
1638: * This method will process component moved event for the popup's ancestor.
1639: * By default we will move the popup if getDefaultMoveOperation() is MOVE_ON_MOVED,
1640: * or hide the popup if getDefaultMoveOperation() is HIDE_ON_MOVED. You can override this to customize
1641: * the behavior.
1642: */
1643: protected void ancestorMoved() {
1644: if (getDefaultMoveOperation() == MOVE_ON_MOVED) {
1645: movePopup();
1646: } else if (getDefaultMoveOperation() == HIDE_ON_MOVED) {
1647: if (isTransient()) {
1648: hidePopupImmediately(true);
1649: }
1650: }
1651: }
1652:
1653: public void hidePopup() {
1654: hidePopup(false);
1655: }
1656:
1657: public void hidePopup(boolean cancelled) {
1658: if (_window == null || !_window.isShowing()) { // not showing. It must be closed already
1659: return;
1660: }
1661: hidePopupImmediately(cancelled);
1662: }
1663:
1664: public boolean isPopupVisible() {
1665: return _window != null;
1666: }
1667:
1668: public Rectangle getPopupBounds() {
1669: return isPopupVisible() ? _window.getBounds() : null;
1670: }
1671:
1672: public void hidePopupImmediately(boolean cancelled) {
1673: if (getOwner() != null) {
1674: getOwner().removeHierarchyListener(_hierarchyListener);
1675: getOwner().removeComponentListener(_ownerComponentListener);
1676: }
1677: if (_window != null) {
1678: _window.removeWindowListener(_windowListener);
1679: _window.removeComponentListener(_componentListener);
1680: _window.getContentPane().remove(this );
1681: if (cancelled) {
1682: firePopupMenuCanceled(); // will cause hidePopupImmediately called again.
1683: }
1684: firePopupMenuWillBecomeInvisible();
1685: }
1686: if (_popupResizeListener != null) {
1687: removeComponentListener(_popupResizeListener);
1688: _popupResizeListener = null;
1689: }
1690:
1691: if (_window != null) {
1692: _previousSize = _window.getSize();
1693: _window.setVisible(false);
1694: firePropertyChange("visible", Boolean.TRUE, Boolean.FALSE);
1695: _window.dispose();
1696: _window = null;
1697: }
1698: //<syd_0034>
1699: //David: There are synchronous events which can result in a call to
1700: // hidePopupImmediately. Because I made the call to addMouseEventHandler
1701: // asynchronous, this can result in a situation where hidePopupImmediately
1702: // gets called before addMouseEventHandler is executed. In that situation, the
1703: // mouseEventHandler would be added after it was removed, resulting in the
1704: // handler hanging around. So I call the removeMouseEventHandler method
1705: // asynchronously, as well, to insure that it happens after
1706: // addMouseEventHandler.
1707: // removeMouseEventHandler();
1708: SwingUtilities.invokeLater(new Runnable() {
1709: public void run() {
1710: removeMouseEventHandler();
1711: }
1712: });
1713: //</syd_0034>
1714:
1715: // comment out because bug report on http://www.jidesoft.com/forum/viewtopic.php?p=10333#10333.
1716: if (getOwner() != null && getOwner().isShowing()) {
1717: Component owner = getOwner();
1718: for (Container p = owner.getParent(); p != null; p = p
1719: .getParent()) {
1720: if (p instanceof JPopupMenu)
1721: break;
1722: if (p instanceof Window) {
1723: if (!((Window) p).isFocused()) {
1724: boolean success = owner.requestFocusInWindow();
1725: if (!success) {
1726: owner.requestFocus();
1727: }
1728: break;
1729: }
1730: }
1731: }
1732: }
1733:
1734: _actualOwner = null;
1735: _actualOwnerLocation = null;
1736: }
1737:
1738: /**
1739: * Hides the popup immediately (compare to {@link #hidePopup()} could use animation to hide the popup).
1740: */
1741: public void hidePopupImmediately() {
1742: hidePopupImmediately(false);
1743: }
1744:
1745: /**
1746: * Add an entry to global event queue.
1747: */
1748: private void addMouseEventHandler() {
1749: if (SecurityUtils.isAWTEventListenerDisabled()
1750: || "true".equals(SecurityUtils.getProperty(
1751: "jide.disableAWTEventListener", "false"))) {
1752: return;
1753: }
1754:
1755: try {
1756: java.security.AccessController
1757: .doPrivileged(new java.security.PrivilegedAction() {
1758: public Object run() {
1759: Toolkit
1760: .getDefaultToolkit()
1761: .addAWTEventListener(
1762: _awtEventListener,
1763: AWTEvent.MOUSE_EVENT_MASK
1764: | AWTEvent.MOUSE_MOTION_EVENT_MASK
1765: | AWTEvent.WINDOW_EVENT_MASK
1766: | AWTEvent.COMPONENT_EVENT_MASK);
1767: return null;
1768: }
1769: });
1770: } catch (SecurityException e) {
1771: throw new RuntimeException(e);
1772: }
1773: }
1774:
1775: /**
1776: * Add an entry to global event queue.
1777: */
1778: private void removeMouseEventHandler() {
1779: if (SecurityUtils.isAWTEventListenerDisabled()
1780: || "true".equals(SecurityUtils.getProperty(
1781: "jide.disableAWTEventListener", "false"))) {
1782: return;
1783: }
1784:
1785: try {
1786: java.security.AccessController
1787: .doPrivileged(new java.security.PrivilegedAction() {
1788: public Object run() {
1789: Toolkit.getDefaultToolkit()
1790: .removeAWTEventListener(
1791: _awtEventListener);
1792: return null;
1793: }
1794: });
1795: } catch (SecurityException e) {
1796: throw new RuntimeException(e);
1797: }
1798: }
1799:
1800: public Component getOwner() {
1801: return _owner;
1802: }
1803:
1804: public void setOwner(Component owner) {
1805: if (_owner != owner) {
1806: Component old = _owner;
1807: _owner = owner;
1808: firePropertyChange(OWNER_PROPERTY, old, _owner);
1809: removeExcludedComponent(old);
1810: addExcludedComponent(_owner);
1811: }
1812: }
1813:
1814: /**
1815: * Checks if the popup is movable. If yes, it will show the gripper
1816: * so that user can grab it and move the popup. If the popup is attached to its owner,
1817: * moving it will detach from the owner.
1818: *
1819: * @return true if gripper is visible
1820: */
1821: public boolean isMovable() {
1822: return _movable;
1823: }
1824:
1825: /**
1826: * Sets the movable attribute.
1827: *
1828: * @param movable true or false.
1829: */
1830: public void setMovable(boolean movable) {
1831: boolean old = _movable;
1832: if (old != movable) {
1833: _movable = movable;
1834: firePropertyChange(MOVABLE_PROPERTY, old, _movable);
1835: }
1836: }
1837:
1838: /**
1839: * Checks if the popup is resizable. By default, resizable option is true.
1840: * <p/>
1841: * Depending on the detached/attached mode, the resizing behavior
1842: * may be different. If a popup is detached to a component, it only
1843: * allows you to resize from bottom, bottom right and right
1844: * It obviously doesn't make sense to resize from top and top side
1845: * is aligned with the attached component.
1846: * <p/>
1847: * (Notes: in the future we will allow resize from different corner if the
1848: * popup is shown above owner due to not enough space on the screen).
1849: *
1850: * @return if the popup is resizable.
1851: */
1852: public boolean isResizable() {
1853: return _resizable;
1854: }
1855:
1856: /**
1857: * Sets the resizable option.
1858: *
1859: * @param resizable true or false.
1860: */
1861: public void setResizable(boolean resizable) {
1862: if (_resizable != resizable) {
1863: boolean old = _resizable;
1864: _resizable = resizable;
1865: firePropertyChange(RESIZABLE_PROPERTY, old, _resizable);
1866: }
1867: }
1868:
1869: /**
1870: * Checks if the popup is attachable. By default, attachable option is true.
1871: *
1872: * @return if the popup is attachable.
1873: */
1874: public boolean isAttachable() {
1875: return _attachable;
1876: }
1877:
1878: /**
1879: * Sets the attachable option.
1880: *
1881: * @param attachable true or false.
1882: */
1883: public void setAttachable(boolean attachable) {
1884: if (_attachable != attachable) {
1885: boolean old = _attachable;
1886: _attachable = attachable;
1887: firePropertyChange(ATTACHABLE_PROPERTY, old, _attachable);
1888: }
1889: }
1890:
1891: /**
1892: * Checks if the popup is detached.
1893: * <p/>
1894: * A popup has detached and attached mode. When a popup is in attached,
1895: * it will act like it's part of the owner (which can be set using {@link #setOwner(java.awt.Component)}.
1896: * When owner is moved, the popup will be moved.
1897: * If the owner is hidden, the popup will hidden.
1898: * In the other word, it is attached with the owner.
1899: * In detached mode, popup becomes an indenpendent floating window.
1900: * It will stay at the same location regardless if owner is moved.
1901: * It could still be visible when owner is hidden.
1902: * <p/>
1903: *
1904: * @return true if it's detacted. Otherwise false.
1905: */
1906: public boolean isDetached() {
1907: return _detached;
1908: }
1909:
1910: /**
1911: * Changes the popup's detached mode.
1912: *
1913: * @param detached true or false.
1914: */
1915: public void setDetached(boolean detached) {
1916: if (_detached != detached) {
1917: boolean old = _detached;
1918: _detached = detached;
1919: firePropertyChange("detacted", old, _detached);
1920: if (_window != null) { // todo: check property change
1921: if (_detached) {
1922: if (getPopupBorder() == null) {
1923: _window.setBorder(UIDefaultsLookup
1924: .getBorder("Resizable.resizeBorder"));
1925: } else {
1926: _window.setBorder(getPopupBorder());
1927: }
1928: if (isResizable()) {
1929: _window.getResizable().setResizableCorners(
1930: Resizable.ALL);
1931: } else {
1932: _window.getResizable().setResizableCorners(
1933: Resizable.NONE);
1934: }
1935: } else {
1936: if (getPopupBorder() == null) {
1937: _window.setBorder(UIDefaultsLookup
1938: .getBorder("PopupMenu.border"));
1939: } else {
1940: _window.setBorder(getPopupBorder());
1941: }
1942: if (isResizable()) {
1943: _window.getResizable().setResizableCorners(
1944: Resizable.RIGHT | Resizable.LOWER
1945: | Resizable.LOWER_RIGHT);
1946: } else {
1947: _window.getResizable().setResizableCorners(
1948: Resizable.NONE);
1949: }
1950: }
1951: }
1952: }
1953: }
1954:
1955: /**
1956: * Gets the popup border set by {@link #setPopupBorder(javax.swing.border.Border)}.
1957: *
1958: * @return the border for this popup.
1959: */
1960: public Border getPopupBorder() {
1961: return _popupBorder;
1962: }
1963:
1964: /**
1965: * Sets the border for this popup. Please note a non-empty border is needed if you want the popup to be
1966: * resizable.
1967: *
1968: * @param popupBorder the border for the popup.
1969: */
1970: public void setPopupBorder(Border popupBorder) {
1971: _popupBorder = popupBorder;
1972: }
1973:
1974: /**
1975: * Checks if the popup is transient.
1976: *
1977: * @return true if transient.
1978: * @see #setTransient(boolean)
1979: */
1980: public boolean isTransient() {
1981: return _transient;
1982: }
1983:
1984: /**
1985: * Sets the transient attribute. If a popup is transient, it will hide automatically when mouse is
1986: * clicked outside the popup. Otherwise, it will stay visible until timeout or hidePopup() is called.
1987: *
1988: * @param isTransient true or false.
1989: */
1990: public void setTransient(boolean isTransient) {
1991: boolean old = _transient;
1992: if (old != isTransient) {
1993: _transient = isTransient;
1994: firePropertyChange(TRANSIENT_PROPERTY, old, isTransient);
1995: }
1996: }
1997:
1998: /**
1999: * Gets the time out value, in milliseconds.
2000: *
2001: * @return the time out value, in milliseconds.
2002: */
2003: public int getTimeout() {
2004: return _timeout;
2005: }
2006:
2007: /**
2008: * Sets the time out value, in milliseconds. If you don't want the popup hide
2009: * after the time out, set the value to 0. By default it's 0 meaning it
2010: * will never time out.
2011: * <p/>
2012: * Typically, you call setTimeOut before the popup is visible. But if you do call setTimeOut when popup is
2013: * already visible (which means the timer is running), we will restart the timer using the new time out value you
2014: * just set, even the new time out value is the same as the old one. In the other word, this setTimeOut call
2015: * will always restart the timer if the timer is running.
2016: *
2017: * @param timeout new time out value, in milliseconds. 0 if you don't want popup automatically hides.
2018: */
2019: public void setTimeout(int timeout) {
2020: _timeout = timeout;
2021: if (_timer != null && _timer.isRunning()) {
2022: startTimeoutTimer(); // this call will restart the timer.
2023: }
2024: }
2025:
2026: private void stopTimeoutTimer() {
2027: if (_timer != null) {
2028: _timer.stop();
2029: _timer = null;
2030: // System.out.println("stop");
2031: }
2032: }
2033:
2034: private void startTimeoutTimer() {
2035: stopTimeoutTimer();
2036: _timer = new Timer(getTimeout(), new ActionListener() {
2037: public void actionPerformed(ActionEvent e) {
2038: hidePopup();
2039: }
2040: });
2041: _timer.setRepeats(false);
2042: _timer.start();
2043: // System.out.println("start");
2044: }
2045:
2046: /**
2047: * Gets the default focus component.
2048: *
2049: * @return the default focus component.
2050: */
2051: public Component getDefaultFocusComponent() {
2052: return _defaultFocusComponent;
2053: }
2054:
2055: /**
2056: * Sets the default focus component. Default focus component should be a child component on this popup. It will
2057: * get focus when popup is shown. By setting a non-null component as default focus component, the JWindow that contains the JidePopup will be
2058: * set focusable. Otherwise the JWindow will be non-focusable.
2059: *
2060: * @param defaultFocusComponent the default focus component.
2061: */
2062: public void setDefaultFocusComponent(Component defaultFocusComponent) {
2063: _defaultFocusComponent = defaultFocusComponent;
2064: }
2065:
2066: /**
2067: * Adds a <code>PopupMenu</code> listener which will listen to notification
2068: * messages from the popup portion of the combo box.
2069: * <p/>
2070: * For all standard look and feels shipped with Java 2, the popup list
2071: * portion of combo box is implemented as a <code>JPopupMenu</code>.
2072: * A custom look and feel may not implement it this way and will
2073: * therefore not receive the notification.
2074: *
2075: * @param l the <code>PopupMenuListener</code> to add
2076: */
2077: public void addPopupMenuListener(PopupMenuListener l) {
2078: listenerList.add(PopupMenuListener.class, l);
2079: }
2080:
2081: /**
2082: * Removes a <code>PopupMenuListener</code>.
2083: *
2084: * @param l the <code>PopupMenuListener</code> to remove
2085: * @see #addPopupMenuListener
2086: * @since 1.4
2087: */
2088: public void removePopupMenuListener(PopupMenuListener l) {
2089: listenerList.remove(PopupMenuListener.class, l);
2090: }
2091:
2092: /**
2093: * Returns an array of all the <code>PopupMenuListener</code>s added
2094: * to this JComboBox with addPopupMenuListener().
2095: *
2096: * @return all of the <code>PopupMenuListener</code>s added or an empty
2097: * array if no listeners have been added
2098: */
2099: public PopupMenuListener[] getPopupMenuListeners() {
2100: return listenerList.getListeners(PopupMenuListener.class);
2101: }
2102:
2103: /**
2104: * Notifies <code>PopupMenuListener</code>s that the popup portion of the
2105: * combo box will become visible.
2106: * <p/>
2107: * This method is public but should not be called by anything other than
2108: * the UI delegate.
2109: *
2110: * @see #addPopupMenuListener
2111: */
2112: public void firePopupMenuWillBecomeVisible() {
2113: Object[] listeners = listenerList.getListenerList();
2114: PopupMenuEvent e = null;
2115: for (int i = listeners.length - 2; i >= 0; i -= 2) {
2116: if (listeners[i] == PopupMenuListener.class) {
2117: if (e == null)
2118: e = new PopupMenuEvent(this );
2119: ((PopupMenuListener) listeners[i + 1])
2120: .popupMenuWillBecomeVisible(e);
2121: }
2122: }
2123: }
2124:
2125: /**
2126: * Notifies <code>PopupMenuListener</code>s that the popup portion of the
2127: * combo box has become invisible.
2128: * <p/>
2129: * This method is public but should not be called by anything other than
2130: * the UI delegate.
2131: *
2132: * @see #addPopupMenuListener
2133: */
2134: public void firePopupMenuWillBecomeInvisible() {
2135: Object[] listeners = listenerList.getListenerList();
2136: PopupMenuEvent e = null;
2137: for (int i = listeners.length - 2; i >= 0; i -= 2) {
2138: if (listeners[i] == PopupMenuListener.class) {
2139: if (e == null)
2140: e = new PopupMenuEvent(this );
2141: ((PopupMenuListener) listeners[i + 1])
2142: .popupMenuWillBecomeInvisible(e);
2143: }
2144: }
2145: }
2146:
2147: /**
2148: * Notifies <code>PopupMenuListener</code>s that the popup portion of the
2149: * combo box has been canceled.
2150: * <p/>
2151: * This method is public but should not be called by anything other than
2152: * the UI delegate.
2153: *
2154: * @see #addPopupMenuListener
2155: */
2156: public void firePopupMenuCanceled() {
2157: Object[] listeners = listenerList.getListenerList();
2158: PopupMenuEvent e = null;
2159: for (int i = listeners.length - 2; i >= 0; i -= 2) {
2160: if (listeners[i] == PopupMenuListener.class) {
2161: if (e == null)
2162: e = new PopupMenuEvent(this );
2163: ((PopupMenuListener) listeners[i + 1])
2164: .popupMenuCanceled(e);
2165: }
2166: }
2167: }
2168:
2169: /**
2170: * Gets the default operation when the owner is moved. The valid values are either {@link #HIDE_ON_MOVED},
2171: * {@link #MOVE_ON_MOVED} or {@link #DO_NOTHING_ON_MOVED}.
2172: *
2173: * @return the default operation when the owner is moved.
2174: */
2175: public int getDefaultMoveOperation() {
2176: return _defaultMoveOperation;
2177: }
2178:
2179: /**
2180: * Sets the default operation when the owner is moved. The valid could be either {@link #HIDE_ON_MOVED},
2181: * {@link #MOVE_ON_MOVED} or {@link #DO_NOTHING_ON_MOVED}.
2182: *
2183: * @param defaultMoveOperation the default operation when the owner is moved.
2184: */
2185: public void setDefaultMoveOperation(int defaultMoveOperation) {
2186: _defaultMoveOperation = defaultMoveOperation;
2187: }
2188:
2189: /**
2190: * Adds a component as excluded component. If a component is an excluded component or descendant of an excluded component, clicking on it will not
2191: * hide the popup.
2192: * <p/>
2193: * For example, AbstractComboBox uses JidePopup to display the popup. If you want to show a JDialog from the popup, you will
2194: * have to add the dialog as excluded component. See below for an example.
2195: * <pre><code>
2196: * JDialog dialog =new JDialog((Frame) JideSwingUtilities.getWindowForComponent(this), true);
2197: * dialog.add(new JTable(10, 4));
2198: * dialog.pack();
2199: * Container ancestorOfClass = SwingUtilities.getAncestorOfClass(JidePopup.class, this); // try to find the JidePopup
2200: * if(ancestorOfClass instanceof JidePopup) {
2201: * ((JidePopup) ancestorOfClass).addExcludedComponent(dialog);
2202: * }
2203: * dialog.setVisible(true);
2204: * if(ancestorOfClass instanceof JidePopup) {
2205: * ((JidePopup) ancestorOfClass).removeExcludedComponent(dialog);
2206: * }
2207: * </code></pre>
2208: *
2209: * @param component the component should be excluded.
2210: */
2211: public void addExcludedComponent(Component component) {
2212: if (component != null
2213: && !_excludedComponents.contains(component)) {
2214: _excludedComponents.add(component);
2215: }
2216: }
2217:
2218: /**
2219: * Removes a component from the excluded component list. If a component is an excluded component, clicking on it will not
2220: * hide the popup.
2221: *
2222: * @param component the component was excluded before.
2223: */
2224: public void removeExcludedComponent(Component component) {
2225: _excludedComponents.remove(component);
2226: }
2227:
2228: /**
2229: * Checks if a component is an excluded component. If a component is an excluded component, clicking on it will not
2230: * hide the popup. By default, owner is always the excluded component.
2231: *
2232: * @param component a component.
2233: * @return true if the component is an excluded component.
2234: */
2235: public boolean isExcludedComponent(Component component) {
2236: boolean contain = _excludedComponents.contains(component);
2237: if (!contain) {
2238: for (Component c : _excludedComponents) {
2239: if (c instanceof Container) {
2240: if (((Container) c).isAncestorOf(component)) {
2241: return true;
2242: }
2243: }
2244: }
2245: }
2246: return contain;
2247: }
2248:
2249: public int getGripperLocation() {
2250: return _gripperLocation;
2251: }
2252:
2253: /**
2254: * Sets the gripper location. The valid values are {@link SwingConstants#NORTH},
2255: * {@link SwingConstants#SOUTH}, {@link SwingConstants#EAST}, and {@link SwingConstants#WEST}.
2256: *
2257: * @param gripperLocation the new gripper location.
2258: */
2259: public void setGripperLocation(int gripperLocation) {
2260: int old = _gripperLocation;
2261: if (old != gripperLocation) {
2262: _gripperLocation = gripperLocation;
2263: firePropertyChange(PROPERTY_GRIPPER_LOCATION, old,
2264: gripperLocation);
2265: }
2266: }
2267: }
|