001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Alexander T. Simbirtsev
019: * @version $Revision$
020: */package javax.swing;
021:
022: import java.awt.Component;
023: import java.awt.Dimension;
024: import java.awt.Graphics;
025: import java.awt.Insets;
026: import java.awt.LayoutManager;
027: import java.awt.Point;
028: import java.awt.Rectangle;
029: import java.awt.Window;
030: import java.awt.event.KeyEvent;
031: import java.awt.event.MouseEvent;
032: import java.beans.PropertyChangeEvent;
033: import java.beans.PropertyChangeListener;
034: import java.util.HashSet;
035: import java.util.Iterator;
036:
037: import javax.accessibility.Accessible;
038: import javax.accessibility.AccessibleContext;
039: import javax.accessibility.AccessibleRole;
040: import javax.swing.event.PopupMenuEvent;
041: import javax.swing.event.PopupMenuListener;
042: import javax.swing.plaf.PopupMenuUI;
043:
044: import org.apache.harmony.awt.ComponentInternals;
045: import org.apache.harmony.awt.MouseEventPreprocessor;
046: import org.apache.harmony.x.swing.StringConstants;
047: import org.apache.harmony.x.swing.Utilities;
048:
049: import org.apache.harmony.x.swing.internal.nls.Messages;
050:
051: public class JPopupMenu extends JComponent implements Accessible,
052: MenuElement {
053:
054: public static class Separator extends JSeparator {
055: private final static String UI_CLASS_ID = "PopupMenuSeparatorUI";
056:
057: public String getUIClassID() {
058: return UI_CLASS_ID;
059: }
060: }
061:
062: // TODO implement accessibility
063: protected class AccessibleJPopupMenu extends AccessibleJComponent
064: implements PropertyChangeListener {
065: public AccessibleRole getAccessibleRole() {
066: return AccessibleRole.POPUP_MENU;
067: }
068:
069: public void propertyChange(final PropertyChangeEvent event) {
070: throw new UnsupportedOperationException(Messages
071: .getString("swing.27")); //$NON-NLS-1$
072: }
073: }
074:
075: private static class PopupMouseEventPreprocessor implements
076: MouseEventPreprocessor {
077: private final HashSet openedPopups = new HashSet();
078:
079: public boolean preprocess(final MouseEvent event) {
080: if (event.getID() != MouseEvent.MOUSE_PRESSED
081: || openedPopups.isEmpty()) {
082: return true;
083: }
084:
085: boolean inside = false;
086: final Iterator i = openedPopups.iterator();
087: while (i.hasNext() && !inside) {
088: final JPopupMenu popup = (JPopupMenu) i.next();
089: Point localPoint = SwingUtilities.convertPoint(event
090: .getComponent(), event.getPoint(), popup);
091: inside = popup.contains(localPoint);
092:
093: final Component invoker = popup.getInvoker();
094: if (invoker != null
095: && Boolean.TRUE
096: .equals(popup
097: .getClientProperty(StringConstants.HIDE_ON_INVOKER_PRESSED_PROPERTY))) {
098: localPoint = SwingUtilities.convertPoint(event
099: .getComponent(), event.getPoint(), invoker);
100: inside |= invoker.contains(localPoint);
101: }
102: }
103: if (inside) {
104: return true;
105: }
106:
107: final MenuElement[] selectedPath = MenuSelectionManager
108: .defaultManager().getSelectedPath();
109: for (int j = 1; j < selectedPath.length; j++) {
110: Component c = selectedPath[j].getComponent();
111: if (c != null) {
112: Point localPoint = SwingUtilities.convertPoint(
113: event.getComponent(), event.getPoint(), c);
114: inside = c.contains(localPoint);
115: if (inside) {
116: return true;
117: }
118: }
119: }
120: MenuSelectionManager.defaultManager().clearSelectedPath();
121: return true;
122: }
123:
124: public void registerPopup(final JPopupMenu popup) {
125: openedPopups.add(popup);
126: }
127:
128: public void unregisterPopup(final JPopupMenu popup) {
129: openedPopups.remove(popup);
130: }
131: }
132:
133: private static int numHWPopups;
134: private static Runnable cancelGrabRunnable;
135:
136: private final static String UI_CLASS_ID = "PopupMenuUI";
137:
138: private static boolean defaultLightWeightPopupEnabled = true;
139: private static final PopupMouseEventPreprocessor MOUSE_EVENTS_HANDLER = new PopupMouseEventPreprocessor();
140:
141: private SingleSelectionModel selectionModel = new DefaultSingleSelectionModel();
142: private String label;
143: private Component invoker;
144: private boolean borderPainted = true;
145: private boolean lightWeightPopupEnabled = getDefaultLightWeightPopupEnabled();
146: private Popup popup;
147: private final Point location = new Point();
148:
149: static {
150: ComponentInternals.getComponentInternals()
151: .setMouseEventPreprocessor(MOUSE_EVENTS_HANDLER);
152: }
153:
154: public JPopupMenu() {
155: super .setVisible(false);
156: updateUI();
157: }
158:
159: public JPopupMenu(final String text) {
160: label = text;
161: super .setVisible(false);
162: updateUI();
163: }
164:
165: public AccessibleContext getAccessibleContext() {
166: return (accessibleContext == null) ? (accessibleContext = new AccessibleJPopupMenu())
167: : accessibleContext;
168: }
169:
170: protected PropertyChangeListener createActionChangeListener(
171: final JMenuItem item) {
172: return (item != null) ? item
173: .createActionPropertyChangeListener(item.getAction())
174: : null;
175: }
176:
177: public void addPopupMenuListener(final PopupMenuListener listener) {
178: listenerList.add(PopupMenuListener.class, listener);
179: }
180:
181: public void removePopupMenuListener(final PopupMenuListener listener) {
182: listenerList.remove(PopupMenuListener.class, listener);
183: }
184:
185: public PopupMenuListener[] getPopupMenuListeners() {
186: return (PopupMenuListener[]) getListeners(PopupMenuListener.class);
187: }
188:
189: protected void firePopupMenuCanceled() {
190: final PopupMenuListener[] listeners = getPopupMenuListeners();
191: if (listeners.length == 0) {
192: return;
193: }
194:
195: final PopupMenuEvent event = new PopupMenuEvent(this );
196: for (int i = 0; i < listeners.length; i++) {
197: listeners[i].popupMenuCanceled(event);
198: }
199: }
200:
201: protected void firePopupMenuWillBecomeInvisible() {
202: final PopupMenuListener[] listeners = getPopupMenuListeners();
203: if (listeners.length == 0) {
204: return;
205: }
206:
207: final PopupMenuEvent event = new PopupMenuEvent(this );
208: for (int i = 0; i < listeners.length; i++) {
209: listeners[i].popupMenuWillBecomeInvisible(event);
210: }
211: }
212:
213: protected void firePopupMenuWillBecomeVisible() {
214: final PopupMenuListener[] listeners = getPopupMenuListeners();
215: if (listeners.length == 0) {
216: return;
217: }
218:
219: final PopupMenuEvent event = new PopupMenuEvent(this );
220: for (int i = 0; i < listeners.length; i++) {
221: listeners[i].popupMenuWillBecomeVisible(event);
222: }
223: }
224:
225: public JMenuItem add(final Action action) {
226: final JMenuItem result = add(createActionComponent(action));
227: result.setAction(action);
228: return result;
229: }
230:
231: public JMenuItem add(final String text) {
232: return add(new JMenuItem(text));
233: }
234:
235: public JMenuItem add(final JMenuItem item) {
236: return (JMenuItem) super .add(item);
237: }
238:
239: public void addSeparator() {
240: super .add(new JPopupMenu.Separator());
241: }
242:
243: public void insert(final Component c, final int i) {
244: if (i < 0) {
245: throw new IllegalArgumentException(Messages
246: .getString("swing.21")); //$NON-NLS-1$
247: }
248: if (i <= getComponentCount()) {
249: super .add(c, i);
250: }
251: }
252:
253: public void insert(final Action action, final int i) {
254: if (i < 0) {
255: throw new IllegalArgumentException(Messages
256: .getString("swing.21")); //$NON-NLS-1$
257: }
258: insert(createActionComponent(action),
259: i < getComponentCount() ? i : getComponentCount());
260: }
261:
262: public void remove(final int i) {
263: if (i < 0) {
264: throw new IllegalArgumentException(Messages
265: .getString("swing.21")); //$NON-NLS-1$
266: }
267: if (i >= getComponentCount()) {
268: throw new IllegalArgumentException(Messages
269: .getString("swing.22")); //$NON-NLS-1$
270: }
271:
272: super .remove(i);
273: }
274:
275: /**
276: * @deprecated
277: */
278: public Component getComponentAtIndex(final int i) {
279: return super .getComponent(i);
280: }
281:
282: public int getComponentIndex(final Component c) {
283: for (int i = 0; i < getComponentCount(); i++) {
284: if (getComponent(i) == c) {
285: return i;
286: }
287: }
288: return -1;
289: }
290:
291: public MenuElement[] getSubElements() {
292: return Utilities.getSubElements(this );
293: }
294:
295: protected JMenuItem createActionComponent(final Action action) {
296: return JMenuItem.createJMenuItem(action);
297: }
298:
299: public static boolean getDefaultLightWeightPopupEnabled() {
300: return defaultLightWeightPopupEnabled;
301: }
302:
303: public static void setDefaultLightWeightPopupEnabled(
304: final boolean lightWeightPopupEnabled) {
305: JPopupMenu.defaultLightWeightPopupEnabled = lightWeightPopupEnabled;
306: }
307:
308: public boolean isLightWeightPopupEnabled() {
309: return lightWeightPopupEnabled;
310: }
311:
312: public void setLightWeightPopupEnabled(
313: final boolean lightWeightPopupEnabled) {
314: this .lightWeightPopupEnabled = lightWeightPopupEnabled;
315: }
316:
317: public void setInvoker(final Component invoker) {
318: this .invoker = invoker;
319: }
320:
321: public Component getInvoker() {
322: return invoker;
323: }
324:
325: public Component getComponent() {
326: return this ;
327: }
328:
329: public void setLabel(final String label) {
330: String oldValue = this .label;
331: this .label = label;
332: firePropertyChange(StringConstants.LABEL_PROPERTY_CHANGED,
333: oldValue, label);
334: }
335:
336: public String getLabel() {
337: return label;
338: }
339:
340: public Insets getMargin() {
341: return new Insets(0, 0, 0, 0);
342: }
343:
344: public void setBorderPainted(final boolean painted) {
345: borderPainted = painted;
346: }
347:
348: public boolean isBorderPainted() {
349: return borderPainted;
350: }
351:
352: protected void paintBorder(final Graphics g) {
353: if (isBorderPainted()) {
354: super .paintBorder(g);
355: }
356: }
357:
358: public boolean isPopupTrigger(final MouseEvent event) {
359: return getUI().isPopupTrigger(event);
360: }
361:
362: public void menuSelectionChanged(final boolean isIncluded) {
363: if (isIncluded == isVisible()) {
364: return;
365: }
366:
367: if (isIncluded) {
368: final Component c = getInvoker();
369: if (c instanceof JMenu) {
370: ((JMenu) c).setPopupMenuVisible(true);
371: }
372: } else {
373: setVisible(false);
374: }
375: }
376:
377: public void pack() {
378: final LayoutManager layout = getLayout();
379: if (layout != null) {
380: layout.layoutContainer(this );
381: }
382: }
383:
384: public void processKeyEvent(final KeyEvent event,
385: final MenuElement[] path, final MenuSelectionManager msm) {
386: // seems like it does no useful work
387: }
388:
389: public void processMouseEvent(final MouseEvent event,
390: final MenuElement[] path, final MenuSelectionManager msm) {
391: }
392:
393: public void setSelected(final Component selection) {
394: if (selectionModel != null) {
395: selectionModel
396: .setSelectedIndex(getComponentIndex(selection));
397: }
398: }
399:
400: public void setSelectionModel(final SingleSelectionModel model) {
401: selectionModel = model;
402: }
403:
404: public SingleSelectionModel getSelectionModel() {
405: return selectionModel;
406: }
407:
408: public void setVisible(final boolean visible) {
409: boolean oldValue = isVisible();
410:
411: if (visible) {
412: super .show();
413: MOUSE_EVENTS_HANDLER.registerPopup(this );
414: } else {
415: super .hide();
416: MOUSE_EVENTS_HANDLER.unregisterPopup(this );
417: }
418:
419: if (getUI() != null) {
420: if (visible) {
421: popup = getUI().getPopup(this , location.x, location.y);
422: firePopupMenuWillBecomeVisible();
423: popup.show();
424: startMouseGrab();
425: } else if (popup != null) {
426: firePopupMenuWillBecomeInvisible();
427: popup.hide();
428: endMouseGrab();
429: }
430: }
431:
432: updateSelectionManager(visible);
433: firePropertyChange(StringConstants.VISIBLE_PROPERTY_CHANGED,
434: oldValue, visible);
435: }
436:
437: public void show(final Component invoker, final int x, final int y) {
438: setInvoker(invoker);
439: Point p = new Point(x, y);
440: if (invoker != null) {
441: p = adjustPopupLocation(invoker, p);
442: }
443: setLocation(p.x, p.y);
444: setVisible(true);
445: }
446:
447: public void setLocation(final int x, final int y) {
448: location.move(x, y);
449:
450: Window w = SwingUtilities.getWindowAncestor(this );
451: if (w != null) {
452: w.setLocation(x, y);
453: }
454: }
455:
456: public void setPopupSize(final int width, final int height) {
457: setPreferredSize(new Dimension(width, height));
458: }
459:
460: public void setPopupSize(final Dimension size) {
461: setPreferredSize(size);
462: }
463:
464: public void updateUI() {
465: setUI(UIManager.getUI(this ));
466: }
467:
468: public void setUI(final PopupMenuUI ui) {
469: super .setUI(ui);
470: }
471:
472: public PopupMenuUI getUI() {
473: return (PopupMenuUI) ui;
474: }
475:
476: public String getUIClassID() {
477: return UI_CLASS_ID;
478: }
479:
480: private void updateSelectionManager(final boolean visible) {
481: final MenuSelectionManager manager = MenuSelectionManager
482: .defaultManager();
483: final MenuElement[] oldPath = manager.getSelectedPath();
484: MenuElement[] newPath = null;
485: if (visible) {
486: MenuElement selectableChild = Utilities
487: .getFirstSelectableItem(getSubElements());
488: if (Utilities.isEmptyArray(oldPath)
489: && selectableChild != null) {
490: newPath = new MenuElement[] { this , selectableChild };
491: } else {
492: newPath = Utilities.addToPath(oldPath, this );
493: }
494: manager.setSelectedPath(newPath);
495: }
496: }
497:
498: private void endMouseGrab() {
499: if (numHWPopups > 0) {
500: numHWPopups--;
501: }
502: if (numHWPopups == 0) {
503: ComponentInternals.getComponentInternals().endMouseGrab();
504: }
505: }
506:
507: private void startMouseGrab() {
508: if (numHWPopups == 0) {
509: if (cancelGrabRunnable == null) {
510: cancelGrabRunnable = new Runnable() {
511: public void run() {
512: if (isVisible()) {
513: MenuSelectionManager.defaultManager()
514: .clearSelectedPath();
515: }
516: }
517: };
518: }
519: final Window window = SwingUtilities
520: .getWindowAncestor(this );
521: ComponentInternals.getComponentInternals().startMouseGrab(
522: window, cancelGrabRunnable);
523: }
524: numHWPopups++;
525: }
526:
527: private Point adjustPopupLocation(final Component invoker, Point p) {
528: Point invokerScreenLocation = invoker.getLocationOnScreen();
529: p.translate(invokerScreenLocation.x, invokerScreenLocation.y);
530: Rectangle bounds = new Rectangle(invokerScreenLocation, invoker
531: .getSize());
532: boolean horizontal = (invoker instanceof JMenu) ? !((JMenu) (invoker))
533: .isTopLevelMenu()
534: : false;
535: p = Utilities.adjustPopupLocation(p, getPreferredSize(),
536: bounds, horizontal, invoker.getGraphicsConfiguration());
537: return p;
538: }
539:
540: }
|