Source for javax.swing.JPopupMenu

   1: /* JPopupMenu.java --
   2:    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Component;
  42: import java.awt.Dimension;
  43: import java.awt.Insets;
  44: import java.awt.Point;
  45: import java.awt.event.KeyEvent;
  46: import java.awt.event.MouseEvent;
  47: import java.beans.PropertyChangeEvent;
  48: import java.beans.PropertyChangeListener;
  49: import java.util.ArrayList;
  50: import java.util.EventListener;
  51: 
  52: import javax.accessibility.Accessible;
  53: import javax.accessibility.AccessibleContext;
  54: import javax.accessibility.AccessibleRole;
  55: import javax.swing.event.MenuKeyListener;
  56: import javax.swing.event.PopupMenuEvent;
  57: import javax.swing.event.PopupMenuListener;
  58: import javax.swing.plaf.PopupMenuUI;
  59: 
  60: /**
  61:  * JPopupMenu is a container that is used to display popup menu's menu
  62:  * items. By default JPopupMenu is a lightweight container, however if it
  63:  * is the case that JPopupMenu's bounds are outside of main window, then
  64:  * heawyweight container will be used to display menu items. It is also
  65:  * possible to change JPopupMenu's default  behavior and set JPopupMenu
  66:  * to always use heavyweight container.
  67:  *
  68:  * JPopupMenu can be displayed anywhere; it is a floating free popup menu.
  69:  * However before JPopupMenu is diplayed, its invoker property should be set.
  70:  * JPopupMenu's invoker is a component relative to which popup menu is
  71:  * displayed.
  72:  *
  73:  * JPopupMenu fires PopupMenuEvents to its registered listeners. Whenever
  74:  * JPopupMenu becomes visible on the screen then PopupMenuEvent indicating
  75:  * that popup menu became visible will be fired. In the case when
  76:  * JPopupMenu becomes invisible or cancelled without selection, then
  77:  * popupMenuBecomeInvisible() or popupMenuCancelled() methods of
  78:  * PopupMenuListeners will be invoked.
  79:  *
  80:  * JPopupMenu also fires PropertyChangeEvents when its bound properties 
  81:  * change.In addittion to inheritted bound properties, JPopupMenu has 
  82:  * 'visible' bound property. When JPopupMenu becomes visible/invisible on
  83:  * the screen it fires PropertyChangeEvents to its registered 
  84:  * PropertyChangeListeners.
  85:  */
  86: public class JPopupMenu extends JComponent implements Accessible, MenuElement
  87: {
  88:   private static final long serialVersionUID = -8336996630009646009L;
  89: 
  90:   /* indicates if popup's menu border should be painted*/
  91:   private boolean borderPainted = true;
  92: 
  93:   /** Flag indicating whether lightweight, mediumweight or heavyweight popup
  94:      is used to display menu items.
  95: 
  96:      These are the possible cases:
  97: 
  98:      1. if DefaultLightWeightPopupEnabled true
  99:          (i)  use lightweight container if popup feets inside top-level window
 100:          (ii) only use heavyweight container (JDialog) if popup doesn't fit.
 101: 
 102:      2. if DefaultLightWeightPopupEnabled false
 103:          (i) if popup fits, use awt.Panel (mediumWeight)
 104:          (ii) if popup doesn't fit, use JDialog (heavyWeight)
 105:   */
 106:   private static boolean DefaultLightWeightPopupEnabled = true;
 107: 
 108:   /* Component that invokes popup menu. */
 109:   transient Component invoker;
 110: 
 111:   /* Label for this popup menu. It is not used in most of the look and feel themes. */
 112:   private String label;
 113: 
 114:   /*Amount of space between menuItem's in JPopupMenu and JPopupMenu's border */
 115:   private Insets margin;
 116: 
 117:   /** Indicates whether ligthWeight container can be used to display popup
 118:      menu. This flag is the same as DefaultLightWeightPopupEnabled, but setting
 119:      this flag can change popup menu after creation of the object */
 120:   private boolean lightWeightPopupEnabled;
 121: 
 122:   /** SelectionModel that keeps track of menu selection. */
 123:   protected SingleSelectionModel selectionModel;
 124: 
 125:   /* Popup that is used to display JPopupMenu */
 126:   private transient Popup popup;
 127: 
 128:   /**
 129:    * Location of the popup, X coordinate.
 130:    */
 131:   private int popupLocationX;
 132: 
 133:   /**
 134:    * Location of the popup, Y coordinate.
 135:    */
 136:   private int popupLocationY;
 137: 
 138:   /* Field indicating if popup menu is visible or not */
 139:   private boolean visible = false;
 140:   
 141:   /**
 142:    * Creates a new JPopupMenu object.
 143:    */
 144:   public JPopupMenu()
 145:   {
 146:     this(null);
 147:   }
 148: 
 149:   /**
 150:    * Creates a new JPopupMenu with specified label
 151:    *
 152:    * @param label Label for popup menu.
 153:    */
 154:   public JPopupMenu(String label)
 155:   {
 156:     lightWeightPopupEnabled = getDefaultLightWeightPopupEnabled();
 157:     setLabel(label);
 158:     setSelectionModel(new DefaultSingleSelectionModel());
 159:     super.setVisible(false);
 160:     updateUI();
 161:   }
 162: 
 163:   /**
 164:   * Adds given menu item to the popup menu
 165:   *
 166:   * @param item menu item to add to the popup menu
 167:   *
 168:   * @return menu item that was added to the popup menu
 169:   */
 170:   public JMenuItem add(JMenuItem item)
 171:   {
 172:     this.insert(item, -1);
 173:     return item;
 174:   }
 175: 
 176:   /**
 177:    * Constructs menu item with a specified label and adds it to
 178:    * popup menu
 179:    *
 180:    * @param text label for the menu item to be added
 181:    *
 182:    * @return constructed menu item that was added to the popup menu
 183:    */
 184:   public JMenuItem add(String text)
 185:   {
 186:     JMenuItem item = new JMenuItem(text);
 187:     return add(item);
 188:   }
 189: 
 190:   /**
 191:    * Constructs menu item associated with the specified action
 192:    * and adds it to the popup menu
 193:    *
 194:    * @param action Action for the new menu item
 195:    *
 196:    * @return menu item that was added to the menu
 197:    */
 198:   public JMenuItem add(Action action)
 199:   {
 200:     JMenuItem item = createActionComponent(action);
 201: 
 202:     if (action != null)
 203:       action.addPropertyChangeListener(createActionChangeListener(item));
 204: 
 205:     return add(item);
 206:   }
 207: 
 208:   /**
 209:    * Revomes component at the given index from the menu.
 210:    *
 211:    * @param index index of the component that will be removed in the menu
 212:    */
 213:   public void remove(int index)
 214:   {
 215:     super.remove(index);
 216:     revalidate();
 217:   }
 218: 
 219:   /**
 220:    * Create menu item associated with the given action
 221:    * and inserts it into the popup menu at the specified index
 222:    *
 223:    * @param action Action for the new menu item
 224:    * @param index index in the popup menu at which to insert new menu item.
 225:    */
 226:   public void insert(Action action, int index)
 227:   {
 228:     JMenuItem item = new JMenuItem(action);
 229:     this.insert(item, index);
 230:   }
 231: 
 232:   /**
 233:    * Insert given component to the popup menu at the
 234:    * specified index
 235:    *
 236:    * @param component Component to insert
 237:    * @param index Index at which to insert given component
 238:    */
 239:   public void insert(Component component, int index)
 240:   {
 241:     super.add(component, index);
 242:   }
 243: 
 244:   /**
 245:    * Returns flag indicating if newly created JPopupMenu will use
 246:    * heavyweight or lightweight container to display its menu items
 247:    *
 248:    * @return true if JPopupMenu will use lightweight container to display
 249:    * menu items by default, and false otherwise.
 250:    */
 251:   public static boolean getDefaultLightWeightPopupEnabled()
 252:   {
 253:     return DefaultLightWeightPopupEnabled;
 254:   }
 255: 
 256:   /**
 257:    * Sets whether JPopupMenu should use ligthWeight container to
 258:    * display it menu items by default
 259:    *
 260:    * @param enabled true if JPopupMenu should use lightweight container
 261:    * for displaying its menu items, and false otherwise.
 262:    */
 263:   public static void setDefaultLightWeightPopupEnabled(boolean enabled)
 264:   {
 265:     DefaultLightWeightPopupEnabled = enabled;
 266:   }
 267: 
 268:   /**
 269:    * This method returns the UI used to display the JPopupMenu.
 270:    *
 271:    * @return The UI used to display the JPopupMenu.
 272:    */
 273:   public PopupMenuUI getUI()
 274:   {
 275:     return (PopupMenuUI) ui;
 276:   }
 277: 
 278:   /**
 279:    * Set the "UI" property of the menu item, which is a look and feel class
 280:    * responsible for handling popupMenu's input events and painting it.
 281:    *
 282:    * @param ui The new "UI" property
 283:    */
 284:   public void setUI(PopupMenuUI ui)
 285:   {
 286:     super.setUI(ui);
 287:   }
 288: 
 289:   /**
 290:    * This method sets this menuItem's UI to the UIManager's default for the
 291:    * current look and feel.
 292:    */
 293:   public void updateUI()
 294:   {
 295:     setUI((PopupMenuUI) UIManager.getUI(this));
 296:   }
 297: 
 298:   /**
 299:    * This method returns a name to identify which look and feel class will be
 300:    * the UI delegate for the menuItem.
 301:    *
 302:    * @return The Look and Feel classID. "PopupMenuUI"
 303:    */
 304:   public String getUIClassID()
 305:   {
 306:     return "PopupMenuUI";
 307:   }
 308: 
 309:   /**
 310:    * Returns selectionModel used by this popup menu to keep
 311:    * track of the selection.
 312:    *
 313:    * @return popup menu's selection model
 314:    */
 315:   public SingleSelectionModel getSelectionModel()
 316:   {
 317:     return selectionModel;
 318:   }
 319: 
 320:   /**
 321:    * Sets selection model for this popup menu
 322:    *
 323:    * @param model new selection model of this popup menu
 324:    */
 325:   public void setSelectionModel(SingleSelectionModel model)
 326:   {
 327:     selectionModel = model;
 328:   }
 329: 
 330:   /**
 331:    * Creates new menu item associated with a given action.
 332:    *
 333:    * @param action Action used to create new menu item
 334:    *
 335:    * @return new created menu item associated with a given action.
 336:    */
 337:   protected JMenuItem createActionComponent(Action action)
 338:   {
 339:     return new JMenuItem(action);
 340:   }
 341: 
 342:   /**
 343:    * Creates PropertyChangeListener that listens to PropertyChangeEvents
 344:    * occuring in the Action associated with given menu item in this popup menu.
 345:    *
 346:    * @param item MenuItem
 347:    *
 348:    * @return The PropertyChangeListener
 349:    */
 350:   protected PropertyChangeListener createActionChangeListener(JMenuItem item)
 351:   {
 352:     return new ActionChangeListener();
 353:   }
 354: 
 355:   /**
 356:    * Returns true if this popup menu will display its menu item in
 357:    * a lightweight container and false otherwise.
 358:    *
 359:    * @return true if this popup menu will display its menu items
 360:    * in a lightweight container and false otherwise.
 361:    */
 362:   public boolean isLightWeightPopupEnabled()
 363:   {
 364:     return lightWeightPopupEnabled;
 365:   }
 366: 
 367:   /**
 368:    * DOCUMENT ME!
 369:    *
 370:    * @param enabled DOCUMENT ME!
 371:    */
 372:   public void setLightWeightPopupEnabled(boolean enabled)
 373:   {
 374:     lightWeightPopupEnabled = enabled;
 375:   }
 376: 
 377:   /**
 378:    * Returns label for this popup menu
 379:    *
 380:    * @return label for this popup menu
 381:    */
 382:   public String getLabel()
 383:   {
 384:     return label;
 385:   }
 386: 
 387:   /**
 388:    * Sets label for this popup menu. This method fires PropertyChangeEvent
 389:    * when the label property is changed. Please note that most
 390:    * of the Look & Feel will ignore this property.
 391:    *
 392:    * @param label label for this popup menu
 393:    */
 394:   public void setLabel(String label)
 395:   {
 396:     if (label != this.label)
 397:       {
 398:     String oldLabel = this.label;
 399:     this.label = label;
 400:     firePropertyChange("label", oldLabel, label);
 401:       }
 402:   }
 403: 
 404:   /**
 405:    * Adds separator to this popup menu
 406:    */
 407:   public void addSeparator()
 408:   {
 409:     // insert separator at the end of the list of menu items    
 410:     this.insert(new Separator(), -1);
 411:   }
 412: 
 413:   /**
 414:    * Adds a MenuKeyListener to the popup.
 415:    * 
 416:    * @param l - the listener to add.
 417:    */
 418:   public void addMenuKeyListener(MenuKeyListener l)
 419:   {
 420:     listenerList.add(MenuKeyListener.class, l);
 421:   }
 422:   
 423:   /**
 424:    * Removes a MenuKeyListener from the popup.
 425:    * 
 426:    * @param l - the listener to remove.
 427:    */
 428:   public void removeMenuKeyListener(MenuKeyListener l)
 429:   {
 430:     listenerList.remove(MenuKeyListener.class, l);
 431:   }
 432:   
 433:   /**
 434:    * Returns array of getMenuKeyListeners that are listening to JPopupMenu.
 435:    * 
 436:    * @return array of getMenuKeyListeners that are listening to JPopupMenu
 437:    */
 438:   public MenuKeyListener[] getMenuKeyListeners()
 439:   {
 440:     return ((MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class));
 441:   }
 442:   
 443:   /**
 444:    * Adds popupMenuListener to listen for PopupMenuEvents fired
 445:    * by the JPopupMenu
 446:    *
 447:    * @param listener PopupMenuListener to add to JPopupMenu
 448:    */
 449:   public void addPopupMenuListener(PopupMenuListener listener)
 450:   {
 451:     listenerList.add(PopupMenuListener.class, listener);
 452:   }
 453: 
 454:   /**
 455:    * Removes PopupMenuListener from JPopupMenu's list of listeners
 456:    *
 457:    * @param listener PopupMenuListener which needs to be removed
 458:    */
 459:   public void removePopupMenuListener(PopupMenuListener listener)
 460:   {
 461:     listenerList.remove(PopupMenuListener.class, listener);
 462:   }
 463: 
 464:   /**
 465:    * Returns array of PopupMenuListeners that are listening to JPopupMenu
 466:    *
 467:    * @return Array of PopupMenuListeners that are listening to JPopupMenu
 468:    */
 469:   public PopupMenuListener[] getPopupMenuListeners()
 470:   {
 471:     return ((PopupMenuListener[]) listenerList.getListeners(PopupMenuListener.class));
 472:   }
 473: 
 474:   /**
 475:    * This method calls popupMenuWillBecomeVisible() of popup menu's
 476:    * PopupMenuListeners. This method is invoked just before popup menu
 477:    * will appear on the screen.
 478:    */
 479:   protected void firePopupMenuWillBecomeVisible()
 480:   {
 481:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 482: 
 483:     for (int i = 0; i < ll.length; i++)
 484:       ((PopupMenuListener) ll[i]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
 485:   }
 486: 
 487:   /**
 488:    * This method calls popupMenuWillBecomeInvisible() of popup
 489:    * menu's PopupMenuListeners. This method is invoked just before popup
 490:    * menu will disappear from the screen
 491:    */
 492:   protected void firePopupMenuWillBecomeInvisible()
 493:   {
 494:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 495: 
 496:     for (int i = 0; i < ll.length; i++)
 497:       ((PopupMenuListener) ll[i]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
 498:   }
 499: 
 500:   /**
 501:    * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
 502:    * This method is invoked just before popup menu is cancelled. This happens
 503:    * when popup menu is closed without selecting any of its menu items. This
 504:    * usually happens when the top-level window is resized or moved.
 505:    */
 506:   protected void firePopupMenuCanceled()
 507:   {
 508:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 509: 
 510:     for (int i = 0; i < ll.length; i++)
 511:       ((PopupMenuListener) ll[i]).popupMenuCanceled(new PopupMenuEvent(this));
 512:   }
 513: 
 514:   /**
 515:    * This methods sets popup menu's size to its' preferred size. If the
 516:    * popup menu's size is previously set it will be ignored.
 517:    */
 518:   public void pack()
 519:   {
 520:     // Hook up this call so that it gets executed on the event thread in order
 521:     // to avoid synchronization problems when calling the layout manager.
 522:     if (! SwingUtilities.isEventDispatchThread())
 523:       {
 524:         SwingUtilities.invokeLater(new Runnable()
 525:           {
 526:             public void run()
 527:             {
 528:               show();
 529:             }
 530:           });
 531:       }
 532: 
 533:     setSize(getPreferredSize());
 534:   }
 535: 
 536:   /**
 537:    * Return visibility of the popup menu
 538:    *
 539:    * @return true if popup menu is visible on the screen and false otherwise.
 540:    */
 541:   public boolean isVisible()
 542:   {
 543:     return visible;
 544:   }
 545: 
 546:   /**
 547:    * Sets visibility property of this popup menu. If the property is
 548:    * set to true then popup menu will be dispayed and popup menu will
 549:    * hide itself if visible property is set to false.
 550:    *
 551:    * @param visible true if popup menu will become visible and false otherwise.
 552:    */
 553:   public void setVisible(final boolean visible)
 554:   {
 555:     // Hook up this call so that it gets executed on the event thread in order
 556:     // to avoid synchronization problems when calling the layout manager.
 557:     if (! SwingUtilities.isEventDispatchThread())
 558:       {
 559:         SwingUtilities.invokeLater(new Runnable()
 560:           {
 561:             public void run()
 562:             {
 563:               setVisible(visible);
 564:             }
 565:           });
 566:       }
 567: 
 568:     if (visible == isVisible())
 569:       return;
 570: 
 571:     boolean old = isVisible();
 572:     this.visible = visible;
 573:     if (old != isVisible())
 574:       {
 575:         if (visible)
 576:           {
 577:             if (invoker != null && !(invoker instanceof JMenu))
 578:               {
 579:                 MenuElement[] menuEls;
 580:                 if (getSubElements().length > 0)
 581:                   {
 582:                     menuEls = new MenuElement[2];
 583:                     menuEls[0] = this;
 584:                     menuEls[1] = getSubElements()[0];
 585:                 }
 586:                 else
 587:                   {
 588:                     menuEls = new MenuElement[1];
 589:                     menuEls[0] = this;
 590:                   }
 591:                 MenuSelectionManager.defaultManager().setSelectedPath(menuEls);
 592:               }
 593:             firePopupMenuWillBecomeVisible();
 594:             PopupFactory pf = PopupFactory.getSharedInstance();
 595:             pack();
 596:             popup = pf.getPopup(invoker, this, popupLocationX, popupLocationY);
 597:             popup.show();
 598:           }
 599:         else
 600:           {
 601:             getSelectionModel().clearSelection();
 602:             firePopupMenuWillBecomeInvisible();
 603:             popup.hide();
 604:           }
 605:         firePropertyChange("visible", old, isVisible());
 606:       }
 607:   }
 608: 
 609:   /**
 610:    * Sets location of the popup menu.
 611:    *
 612:    * @param x X coordinate of the popup menu's location
 613:    * @param y Y coordinate of the popup menu's location
 614:    */
 615:   public void setLocation(int x, int y)
 616:   {
 617:     popupLocationX = x;
 618:     popupLocationY = y;
 619:     // Handle the case when the popup is already showing. In this case we need
 620:     // to fetch a new popup from PopupFactory and use this. See the general
 621:     // contract of the PopupFactory.
 622:   }
 623: 
 624:   /**
 625:    * Returns popup menu's invoker.
 626:    *
 627:    * @return popup menu's invoker
 628:    */
 629:   public Component getInvoker()
 630:   {
 631:     return invoker;
 632:   }
 633: 
 634:   /**
 635:    * Sets popup menu's invoker.
 636:    *
 637:    * @param component The new invoker of this popup menu
 638:    */
 639:   public void setInvoker(Component component)
 640:   {
 641:     invoker = component;
 642:   }
 643: 
 644:   /**
 645:    * This method displays JPopupMenu on the screen at the specified
 646:    * location. Note that x and y coordinates given to this method
 647:    * should be expressed in terms of the popup menus' invoker.
 648:    *
 649:    * @param component Invoker for this popup menu
 650:    * @param x x-coordinate of the popup menu relative to the specified invoker
 651:    * @param y y-coordiate of the popup menu relative to the specified invoker
 652:    */
 653:   public void show(Component component, int x, int y)
 654:   {
 655:     if (component.isShowing())
 656:       {
 657:         setInvoker(component);
 658:         Point p = new Point(x, y);
 659:         SwingUtilities.convertPointToScreen(p, component);
 660:         setLocation(p.x, p.y);
 661:         setVisible(true);
 662:       }
 663:   }
 664: 
 665:   /**
 666:    * Returns component located at the specified index in the popup menu
 667:    *
 668:    * @param index index of the component to return
 669:    *
 670:    * @return component located at the specified index in the popup menu
 671:    *
 672:    * @deprecated Replaced by getComponent(int)
 673:    */
 674:   public Component getComponentAtIndex(int index)
 675:   {
 676:     return getComponent(index);
 677:   }
 678: 
 679:   /**
 680:    * Returns index of the specified component in the popup menu
 681:    *
 682:    * @param component Component to look for
 683:    *
 684:    * @return index of the specified component in the popup menu
 685:    */
 686:   public int getComponentIndex(Component component)
 687:   {
 688:     Component[] items = getComponents();
 689: 
 690:     for (int i = 0; i < items.length; i++)
 691:       {
 692:     if (items[i].equals(component))
 693:       return i;
 694:       }
 695: 
 696:     return -1;
 697:   }
 698: 
 699:   /**
 700:    * Sets size of the popup
 701:    *
 702:    * @param size Dimensions representing new size of the popup menu
 703:    */
 704:   public void setPopupSize(Dimension size)
 705:   {
 706:     super.setSize(size);
 707:   }
 708: 
 709:   /**
 710:    * Sets size of the popup menu
 711:    *
 712:    * @param width width for the new size
 713:    * @param height height for the new size
 714:    */
 715:   public void setPopupSize(int width, int height)
 716:   {
 717:     super.setSize(width, height);
 718:   }
 719: 
 720:   /**
 721:    * Selects specified component in this popup menu.
 722:    *
 723:    * @param selected component to select
 724:    */
 725:   public void setSelected(Component selected)
 726:   {
 727:     int index = getComponentIndex(selected);
 728:     selectionModel.setSelectedIndex(index);
 729:   }
 730: 
 731:   /**
 732:    * Checks if this popup menu paints its border.
 733:    *
 734:    * @return true if this popup menu paints its border and false otherwise.
 735:    */
 736:   public boolean isBorderPainted()
 737:   {
 738:     return borderPainted;
 739:   }
 740: 
 741:   /**
 742:    * Sets if the border of the popup menu should be
 743:    * painter or not.
 744:    *
 745:    * @param painted true if the border should be painted and false otherwise
 746:    */
 747:   public void setBorderPainted(boolean painted)
 748:   {
 749:     borderPainted = painted;
 750:   }
 751: 
 752:   /**
 753:    * Returns margin for this popup menu.
 754:    *
 755:    * @return margin for this popup menu.
 756:    */
 757:   public Insets getMargin()
 758:   {
 759:     return margin;
 760:   }
 761: 
 762:   /**
 763:    * A string that describes this JPopupMenu. Normally only used
 764:    * for debugging.
 765:    *
 766:    * @return A string describing this JMenuItem
 767:    */
 768:   protected String paramString()
 769:   {
 770:     StringBuffer sb = new StringBuffer();
 771:     sb.append(super.paramString());
 772:     sb.append(",label=");
 773:     if (getLabel() != null)
 774:       sb.append(getLabel());
 775:     sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
 776:     sb.append(",margin=");
 777:     if (getMargin() != null)
 778:       sb.append(margin);
 779:     sb.append(",paintBorder=").append(isBorderPainted());
 780:     return sb.toString();
 781:   }
 782: 
 783:   /**
 784:   * Process mouse events forwarded from MenuSelectionManager. This method 
 785:   * doesn't do anything. It is here to conform to the MenuElement interface.
 786:   *
 787:   * @param event event forwarded from MenuSelectionManager
 788:   * @param path path to the menu element from which event was generated
 789:   * @param manager MenuSelectionManager for the current menu hierarchy
 790:   */
 791:   public void processMouseEvent(MouseEvent event, MenuElement[] path,
 792:                                 MenuSelectionManager manager)
 793:   {
 794:     // Empty Implementation. This method is needed for the implementation
 795:     // of MenuElement interface
 796:   }
 797: 
 798:   /**
 799:    * Process key events forwarded from MenuSelectionManager. This method
 800:    * doesn't do anything. It is here to conform to the MenuElement interface.
 801:    *
 802:    * @param event event forwarded from MenuSelectionManager
 803:    * @param path path to the menu element from which event was generated
 804:    * @param manager MenuSelectionManager for the current menu hierarchy
 805:    *
 806:    */
 807:   public void processKeyEvent(KeyEvent event, MenuElement[] path,
 808:                               MenuSelectionManager manager)
 809:   {
 810:     // Empty Implementation. This method is needed for the implementation
 811:     // of MenuElement interface
 812:   }
 813: 
 814:   /**
 815:    * Method of MenuElement Interface. It is invoked when
 816:    * popupMenu's selection has changed
 817:    *
 818:    * @param changed true if this popupMenu is part of current menu
 819:    * hierarchy and false otherwise.
 820:    */
 821:   public void menuSelectionChanged(boolean changed)
 822:   {
 823:     if (invoker instanceof JMenu)
 824:       {
 825:         // We need to special case this since the JMenu calculates the
 826:         // position etc of the popup.
 827:         JMenu menu = (JMenu) invoker;
 828:         menu.setPopupMenuVisible(changed);
 829:       }
 830:     else if (! changed)
 831:       setVisible(false);
 832:   }
 833: 
 834:   /**
 835:    * Return subcomonents of this popup menu. This method returns only
 836:    * components that implement the <code>MenuElement</code> interface.
 837:    *
 838:    * @return array of menu items belonging to this popup menu
 839:    */
 840:   public MenuElement[] getSubElements()
 841:   {
 842:     Component[] items = getComponents();
 843:     ArrayList subElements = new ArrayList();
 844: 
 845:     for (int i = 0; i < items.length; i++)
 846:       if (items[i] instanceof MenuElement)
 847:     subElements.add(items[i]);
 848: 
 849:     return (MenuElement[])
 850:       subElements.toArray(new MenuElement[subElements.size()]);
 851:   }
 852: 
 853:   /**
 854:    * Method of the MenuElement interface. Returns reference to itself.
 855:    *
 856:    * @return Returns reference to itself
 857:    */
 858:   public Component getComponent()
 859:   {
 860:     return this;
 861:   }
 862: 
 863:   /**
 864:    * Checks if observing mouse event should trigger popup
 865:    * menu to show on the screen.
 866:    *
 867:    * @param event MouseEvent to check
 868:    *
 869:    * @return true if the observing mouse event is popup trigger and false otherwise
 870:    */
 871:   public boolean isPopupTrigger(MouseEvent event)
 872:   {
 873:     return ((PopupMenuUI) getUI()).isPopupTrigger(event);
 874:   }
 875: 
 876:   /**
 877:    * DOCUMENT ME!
 878:    *
 879:    * @return DOCUMENT ME!
 880:    */
 881:   public AccessibleContext getAccessibleContext()
 882:   {
 883:     if (accessibleContext == null)
 884:       accessibleContext = new AccessibleJPopupMenu();
 885: 
 886:     return accessibleContext;
 887:   }
 888: 
 889:   /**
 890:    * This is the separator that can be used in popup menu.
 891:    */
 892:   public static class Separator extends JSeparator
 893:   {
 894:     public Separator()
 895:     {
 896:       super();
 897:     }
 898: 
 899:     public String getUIClassID()
 900:     {
 901:       return "PopupMenuSeparatorUI";
 902:     }
 903:   }
 904: 
 905:   /**
 906:    * Returns <code>true</code> if the component is guaranteed to be painted
 907:    * on top of others. This returns false by default and is overridden by
 908:    * components like JMenuItem, JPopupMenu and JToolTip to return true for
 909:    * added efficiency.
 910:    *
 911:    * @return <code>true</code> if the component is guaranteed to be painted
 912:    *         on top of others
 913:    */
 914:   boolean onTop()
 915:   {
 916:     return true;
 917:   }
 918: 
 919:   protected class AccessibleJPopupMenu extends AccessibleJComponent
 920:   {
 921:     private static final long serialVersionUID = 7423261328879849768L;
 922: 
 923:     protected AccessibleJPopupMenu()
 924:     {
 925:       // Nothing to do here.
 926:     }
 927: 
 928:     public AccessibleRole getAccessibleRole()
 929:     {
 930:       return AccessibleRole.POPUP_MENU;
 931:     }
 932:   }
 933: 
 934:   /* This class resizes popup menu and repaints popup menu appropriately if one
 935:    of item's action has changed */
 936:   private class ActionChangeListener implements PropertyChangeListener
 937:   {
 938:     public void propertyChange(PropertyChangeEvent evt)
 939:     {
 940:       // We used to have a revalidate() and repaint() call here. However I think
 941:       // this is not needed. Instead, a new Popup has to be fetched from the
 942:       // PopupFactory and used here.
 943:     }
 944:   }
 945: }