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.Container;
  43: import java.awt.Dimension;
  44: import java.awt.GridBagConstraints;
  45: import java.awt.Insets;
  46: import java.awt.Panel;
  47: import java.awt.Point;
  48: import java.awt.event.KeyEvent;
  49: import java.awt.event.MouseEvent;
  50: import java.beans.PropertyChangeEvent;
  51: import java.beans.PropertyChangeListener;
  52: import java.io.IOException;
  53: import java.io.ObjectInputStream;
  54: import java.io.ObjectOutputStream;
  55: import java.util.ArrayList;
  56: import java.util.EventListener;
  57: 
  58: import javax.accessibility.Accessible;
  59: import javax.accessibility.AccessibleContext;
  60: import javax.accessibility.AccessibleRole;
  61: import javax.swing.event.PopupMenuEvent;
  62: import javax.swing.event.PopupMenuListener;
  63: import javax.swing.plaf.PopupMenuUI;
  64: 
  65: /**
  66:  * JPopupMenu is a container that is used to display popup menu's menu
  67:  * items. By default JPopupMenu is a lightweight container, however if it
  68:  * is the case that JPopupMenu's bounds are outside of main window, then
  69:  * heawyweight container will be used to display menu items. It is also
  70:  * possible to change JPopupMenu's default  behavior and set JPopupMenu
  71:  * to always use heavyweight container.
  72:  *
  73:  * JPopupMenu can be displayed anywhere; it is a floating free popup menu.
  74:  * However before JPopupMenu is diplayed, its invoker property should be set.
  75:  * JPopupMenu's invoker is a component relative to which popup menu is
  76:  * displayed.
  77:  *
  78:  * JPopupMenu fires PopupMenuEvents to its registered listeners. Whenever
  79:  * JPopupMenu becomes visible on the screen then PopupMenuEvent indicating
  80:  * that popup menu became visible will be fired. In the case when
  81:  * JPopupMenu becomes invisible or cancelled without selection, then
  82:  * popupMenuBecomeInvisible() or popupMenuCancelled() methods of
  83:  * PopupMenuListeners will be invoked.
  84:  *
  85:  * JPopupMenu also fires PropertyChangeEvents when its bound properties 
  86:  * change.In addittion to inheritted bound properties, JPopupMenu has 
  87:  * 'visible' bound property. When JPopupMenu becomes visible/invisible on
  88:  * the screen it fires PropertyChangeEvents to its registered 
  89:  * PropertyChangeListeners.
  90:  */
  91: public class JPopupMenu extends JComponent implements Accessible, MenuElement
  92: {
  93:   private static final long serialVersionUID = -8336996630009646009L;
  94: 
  95:   /* indicates if popup's menu border should be painted*/
  96:   private boolean borderPainted = true;
  97: 
  98:   /** Flag indicating whether lightweight, mediumweight or heavyweight popup
  99:      is used to display menu items.
 100: 
 101:      These are the possible cases:
 102: 
 103:      1. if DefaultLightWeightPopupEnabled true
 104:          (i)  use lightweight container if popup feets inside top-level window
 105:          (ii) only use heavyweight container (JWindow) if popup doesn't fit.
 106: 
 107:      2. if DefaultLightWeightPopupEnabled false
 108:          (i) if popup fits, use awt.Panel (mediumWeight)
 109:          (ii) if popup doesn't fit, use JWindow (heavyWeight)
 110:   */
 111:   private static boolean DefaultLightWeightPopupEnabled = true;
 112: 
 113:   /* Component that invokes popup menu. */
 114:   transient Component invoker;
 115: 
 116:   /* Label for this popup menu. It is not used in most of the look and feel themes. */
 117:   private String label;
 118: 
 119:   /*Amount of space between menuItem's in JPopupMenu and JPopupMenu's border */
 120:   private Insets margin;
 121: 
 122:   /** Indicates whether ligthWeight container can be used to display popup
 123:      menu. This flag is the same as DefaultLightWeightPopupEnabled, but setting
 124:      this flag can change popup menu after creation of the object */
 125:   private boolean lightWeightPopupEnabled;
 126: 
 127:   /** SelectionModel that keeps track of menu selection. */
 128:   private SingleSelectionModel selectionModel;
 129: 
 130:   /* Popup that is used to display JPopupMenu */
 131:   private transient Popup popup;
 132: 
 133:   /* Location of the popup */
 134:   private Point popupLocation;
 135: 
 136:   /* Field indicating if popup menu is visible or not */
 137:   private boolean visible = false;
 138:   
 139:   /**
 140:    * Creates a new JPopupMenu object.
 141:    */
 142:   public JPopupMenu()
 143:   {
 144:     this(null);
 145:   }
 146: 
 147:   /**
 148:    * Creates a new JPopupMenu with specified label
 149:    *
 150:    * @param label Label for popup menu.
 151:    */
 152:   public JPopupMenu(String label)
 153:   {
 154:     lightWeightPopupEnabled = getDefaultLightWeightPopupEnabled();
 155:     setLabel(label);
 156:     setSelectionModel(new DefaultSingleSelectionModel());
 157:     super.setVisible(false);
 158:     updateUI();
 159:   }
 160: 
 161:   private void readObject(ObjectInputStream stream)
 162:                    throws IOException, ClassNotFoundException
 163:   {
 164:   }
 165: 
 166:   private void writeObject(ObjectOutputStream stream) throws IOException
 167:   {
 168:   }
 169: 
 170:   /**
 171:   * Adds given menu item to the popup menu
 172:   *
 173:   * @param item menu item to add to the popup menu
 174:   *
 175:   * @return menu item that was added to the popup menu
 176:   */
 177:   public JMenuItem add(JMenuItem item)
 178:   {
 179:     this.insert(item, -1);
 180:     return item;
 181:   }
 182: 
 183:   /**
 184:    * Constructs menu item with a specified label and adds it to
 185:    * popup menu
 186:    *
 187:    * @param text label for the menu item to be added
 188:    *
 189:    * @return constructed menu item that was added to the popup menu
 190:    */
 191:   public JMenuItem add(String text)
 192:   {
 193:     JMenuItem item = new JMenuItem(text);
 194:     return add(item);
 195:   }
 196: 
 197:   /**
 198:    * Constructs menu item associated with the specified action
 199:    * and adds it to the popup menu
 200:    *
 201:    * @param action Action for the new menu item
 202:    *
 203:    * @return menu item that was added to the menu
 204:    */
 205:   public JMenuItem add(Action action)
 206:   {
 207:     JMenuItem item = createActionComponent(action);
 208: 
 209:     if (action != null)
 210:       action.addPropertyChangeListener(createActionChangeListener(item));
 211: 
 212:     return add(item);
 213:   }
 214: 
 215:   /**
 216:    * Revomes component at the given index from the menu.
 217:    *
 218:    * @param index index of the component that will be removed in the menu
 219:    */
 220:   public void remove(int index)
 221:   {
 222:     super.remove(index);
 223: 
 224:     GridBagConstraints constraints = new GridBagConstraints();
 225:     constraints.fill = GridBagConstraints.BOTH;
 226:     constraints.weightx = 100.0;
 227:     constraints.weighty = 100.0;
 228: 
 229:     Component[] items = getComponents();
 230:     for (int i = index; i < items.length; i++)
 231:       {
 232:     constraints.gridy = i;
 233:     super.add(items[i], constraints, i);
 234:       }
 235:   }
 236: 
 237:   /**
 238:    * Create menu item associated with the given action
 239:    * and inserts it into the popup menu at the specified index
 240:    *
 241:    * @param action Action for the new menu item
 242:    * @param index index in the popup menu at which to insert new menu item.
 243:    */
 244:   public void insert(Action action, int index)
 245:   {
 246:     JMenuItem item = new JMenuItem(action);
 247:     this.insert(item, index);
 248:   }
 249: 
 250:   /**
 251:    * Insert given component to the popup menu at the
 252:    * specified index
 253:    *
 254:    * @param component Component to insert
 255:    * @param index Index at which to insert given component
 256:    */
 257:   public void insert(Component component, int index)
 258:   {
 259:     GridBagConstraints constraints = new GridBagConstraints();
 260:     constraints.fill = GridBagConstraints.BOTH;
 261:     constraints.weightx = 100.0;
 262:     constraints.weighty = 100.0;
 263: 
 264:     constraints.gridy = index;
 265:     super.add(component, constraints, index);
 266: 
 267:     // need to change constraints for the components that were moved by 1
 268:     // due to the insertion
 269:     if (index != -1)
 270:       {
 271:     Component[] items = getComponents();
 272: 
 273:     for (int i = index + 1; i < items.length; i++)
 274:       {
 275:         constraints.gridy = i;
 276:         super.add(items[i], constraints, i);
 277:       }
 278:       }
 279:   }
 280: 
 281:   /**
 282:    * Returns flag indicating if newly created JPopupMenu will use
 283:    * heavyweight or lightweight container to display its menu items
 284:    *
 285:    * @return true if JPopupMenu will use lightweight container to display
 286:    * menu items by default, and false otherwise.
 287:    */
 288:   public static boolean getDefaultLightWeightPopupEnabled()
 289:   {
 290:     return DefaultLightWeightPopupEnabled;
 291:   }
 292: 
 293:   /**
 294:    * Sets whether JPopupMenu should use ligthWeight container to
 295:    * display it menu items by default
 296:    *
 297:    * @param enabled true if JPopupMenu should use lightweight container
 298:    * for displaying its menu items, and false otherwise.
 299:    */
 300:   public static void setDefaultLightWeightPopupEnabled(boolean enabled)
 301:   {
 302:     DefaultLightWeightPopupEnabled = enabled;
 303:   }
 304: 
 305:   /**
 306:    * This method returns the UI used to display the JPopupMenu.
 307:    *
 308:    * @return The UI used to display the JPopupMenu.
 309:    */
 310:   public PopupMenuUI getUI()
 311:   {
 312:     return (PopupMenuUI) ui;
 313:   }
 314: 
 315:   /**
 316:    * Set the "UI" property of the menu item, which is a look and feel class
 317:    * responsible for handling popupMenu's input events and painting it.
 318:    *
 319:    * @param ui The new "UI" property
 320:    */
 321:   public void setUI(PopupMenuUI ui)
 322:   {
 323:     super.setUI(ui);
 324:   }
 325: 
 326:   /**
 327:    * This method sets this menuItem's UI to the UIManager's default for the
 328:    * current look and feel.
 329:    */
 330:   public void updateUI()
 331:   {
 332:     setUI((PopupMenuUI) UIManager.getUI(this));
 333:     invalidate();
 334:   }
 335: 
 336:   /**
 337:    * This method returns a name to identify which look and feel class will be
 338:    * the UI delegate for the menuItem.
 339:    *
 340:    * @return The Look and Feel classID. "PopupMenuUI"
 341:    */
 342:   public String getUIClassID()
 343:   {
 344:     return "PopupMenuUI";
 345:   }
 346: 
 347:   /**
 348:    * Returns selectionModel used by this popup menu to keep
 349:    * track of the selection.
 350:    *
 351:    * @return popup menu's selection model
 352:    */
 353:   public SingleSelectionModel getSelectionModel()
 354:   {
 355:     return selectionModel;
 356:   }
 357: 
 358:   /**
 359:    * Sets selection model for this popup menu
 360:    *
 361:    * @param model new selection model of this popup menu
 362:    */
 363:   public void setSelectionModel(SingleSelectionModel model)
 364:   {
 365:     selectionModel = model;
 366:   }
 367: 
 368:   /**
 369:    * Creates new menu item associated with a given action.
 370:    *
 371:    * @param action Action used to create new menu item
 372:    *
 373:    * @return new created menu item associated with a given action.
 374:    */
 375:   protected JMenuItem createActionComponent(Action action)
 376:   {
 377:     return new JMenuItem(action);
 378:   }
 379: 
 380:   /**
 381:    * Creates PropertyChangeListener that listens to PropertyChangeEvents
 382:    * occuring in the Action associated with given menu item in this popup menu.
 383:    *
 384:    * @param item MenuItem
 385:    *
 386:    * @return The PropertyChangeListener
 387:    */
 388:   protected PropertyChangeListener createActionChangeListener(JMenuItem item)
 389:   {
 390:     return new ActionChangeListener();
 391:   }
 392: 
 393:   /**
 394:    * Returns true if this popup menu will display its menu item in
 395:    * a lightweight container and false otherwise.
 396:    *
 397:    * @return true if this popup menu will display its menu items
 398:    * in a lightweight container and false otherwise.
 399:    */
 400:   public boolean isLightWeightPopupEnabled()
 401:   {
 402:     return lightWeightPopupEnabled;
 403:   }
 404: 
 405:   /**
 406:    * DOCUMENT ME!
 407:    *
 408:    * @param enabled DOCUMENT ME!
 409:    */
 410:   public void setLightWeightPopupEnabled(boolean enabled)
 411:   {
 412:     lightWeightPopupEnabled = enabled;
 413:   }
 414: 
 415:   /**
 416:    * Returns label for this popup menu
 417:    *
 418:    * @return label for this popup menu
 419:    */
 420:   public String getLabel()
 421:   {
 422:     return label;
 423:   }
 424: 
 425:   /**
 426:    * Sets label for this popup menu. This method fires PropertyChangeEvent
 427:    * when the label property is changed. Please note that most
 428:    * of the Look &amp; Feel will ignore this property.
 429:    *
 430:    * @param label label for this popup menu
 431:    */
 432:   public void setLabel(String label)
 433:   {
 434:     if (label != this.label)
 435:       {
 436:     String oldLabel = this.label;
 437:     this.label = label;
 438:     firePropertyChange("label", oldLabel, label);
 439:       }
 440:   }
 441: 
 442:   /**
 443:    * Adds separator to this popup menu
 444:    */
 445:   public void addSeparator()
 446:   {
 447:     // insert separator at the end of the list of menu items    
 448:     this.insert(new Separator(), -1);
 449:   }
 450: 
 451:   /**
 452:    * Adds popupMenuListener to listen for PopupMenuEvents fired
 453:    * by the JPopupMenu
 454:    *
 455:    * @param listener PopupMenuListener to add to JPopupMenu
 456:    */
 457:   public void addPopupMenuListener(PopupMenuListener listener)
 458:   {
 459:     listenerList.add(PopupMenuListener.class, listener);
 460:   }
 461: 
 462:   /**
 463:    * Removes PopupMenuListener from JPopupMenu's list of listeners
 464:    *
 465:    * @param listener PopupMenuListener which needs to be removed
 466:    */
 467:   public void removePopupMenuListener(PopupMenuListener listener)
 468:   {
 469:     listenerList.remove(PopupMenuListener.class, listener);
 470:   }
 471: 
 472:   /**
 473:    * Returns array of PopupMenuListeners that are listening to JPopupMenu
 474:    *
 475:    * @return Array of PopupMenuListeners that are listening to JPopupMenu
 476:    */
 477:   public PopupMenuListener[] getPopupMenuListeners()
 478:   {
 479:     return ((PopupMenuListener[]) listenerList.getListeners(PopupMenuListener.class));
 480:   }
 481: 
 482:   /**
 483:    * This method calls popupMenuWillBecomeVisible() of popup menu's
 484:    * PopupMenuListeners. This method is invoked just before popup menu
 485:    * will appear on the screen.
 486:    */
 487:   protected void firePopupMenuWillBecomeVisible()
 488:   {
 489:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 490: 
 491:     for (int i = 0; i < ll.length; i++)
 492:       ((PopupMenuListener) ll[i]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
 493:   }
 494: 
 495:   /**
 496:    * This method calls popupMenuWillBecomeInvisible() of popup
 497:    * menu's PopupMenuListeners. This method is invoked just before popup
 498:    * menu will disappear from the screen
 499:    */
 500:   protected void firePopupMenuWillBecomeInvisible()
 501:   {
 502:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 503: 
 504:     for (int i = 0; i < ll.length; i++)
 505:       ((PopupMenuListener) ll[i]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
 506:   }
 507: 
 508:   /**
 509:    * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
 510:    * This method is invoked just before popup menu is cancelled. This happens
 511:    * when popup menu is closed without selecting any of its menu items. This
 512:    * usually happens when the top-level window is resized or moved.
 513:    */
 514:   protected void firePopupMenuCanceled()
 515:   {
 516:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 517: 
 518:     for (int i = 0; i < ll.length; i++)
 519:       ((PopupMenuListener) ll[i]).popupMenuCanceled(new PopupMenuEvent(this));
 520:   }
 521: 
 522:   /**
 523:    * This methods sets popup menu's size to its' preferred size. If the
 524:    * popup menu's size is previously set it will be ignored.
 525:    */
 526:   public void pack()
 527:   {
 528:     super.setSize(null);
 529:   }
 530: 
 531:   /**
 532:    * Return visibility of the popup menu
 533:    *
 534:    * @return true if popup menu is visible on the screen and false otherwise.
 535:    */
 536:   public boolean isVisible()
 537:   {
 538:     return visible;
 539:   }
 540: 
 541:   /**
 542:    * Sets visibility property of this popup menu. If the property is
 543:    * set to true then popup menu will be dispayed and popup menu will
 544:    * hide itself if visible property is set to false.
 545:    *
 546:    * @param visible true if popup menu will become visible and false otherwise.
 547:    */
 548:   public void setVisible(boolean visible)
 549:   {
 550:     if (visible == isVisible())
 551:       return;
 552: 
 553:     boolean old = isVisible();
 554:     this.visible = visible;
 555:     if (old != isVisible())
 556:       {
 557:     firePropertyChange("visible", old, isVisible());
 558:     if (visible)
 559:       {
 560:         firePopupMenuWillBecomeVisible();
 561:         Container rootContainer = (Container) SwingUtilities.getRoot(invoker);
 562: 
 563:         boolean fit = true;
 564:         Dimension size;
 565: 
 566:         // Determine the size of the popup menu
 567:         if (this.getSize().width == 0 && this.getSize().width == 0)
 568:           size = this.getPreferredSize();
 569:         else
 570:           size = this.getSize();
 571: 
 572:         if ((size.width > (rootContainer.getWidth() - popupLocation.x))
 573:             || (size.height > (rootContainer.getHeight() - popupLocation.y)))
 574:           fit = false;
 575:         if (lightWeightPopupEnabled && fit)
 576:           popup = new LightWeightPopup(this);
 577:         else
 578:           {
 579:         if (fit)
 580:           popup = new MediumWeightPopup(this);
 581:         else
 582:           popup = new HeavyWeightPopup(this);
 583:           }
 584:         if (popup instanceof LightWeightPopup
 585:             || popup instanceof MediumWeightPopup)
 586:           {
 587:         JLayeredPane layeredPane;
 588:         layeredPane = SwingUtilities.getRootPane(invoker)
 589:                                     .getLayeredPane();
 590:         Point p = new Point(popupLocation.x, popupLocation.y);
 591:         SwingUtilities.convertPointFromScreen(p, layeredPane);
 592:         popup.show(p.x, p.y, size.width, size.height);  
 593:           }
 594:         else
 595:           {
 596:         // Subtract insets of the top-level container if popup menu's
 597:         // top-left corner is inside it.
 598:         Insets insets = rootContainer.getInsets();
 599:         popup.show(popupLocation.x - insets.left,
 600:                    popupLocation.y - insets.top, size.width,
 601:                    size.height);
 602:           }
 603:       }
 604:     else
 605:       {
 606:         firePopupMenuWillBecomeInvisible();
 607:         popup.hide();
 608:       }
 609:       }
 610:   }
 611: 
 612:   /**
 613:    * Sets location of the popup menu.
 614:    *
 615:    * @param x X coordinate of the popup menu's location
 616:    * @param y Y coordinate of the popup menu's location
 617:    */
 618:   public void setLocation(int x, int y)
 619:   {
 620:     if (popupLocation == null)
 621:       popupLocation = new Point();
 622: 
 623:     popupLocation.x = x;
 624:     popupLocation.y = y;
 625:   }
 626: 
 627:   /**
 628:    * Returns popup menu's invoker.
 629:    *
 630:    * @return popup menu's invoker
 631:    */
 632:   public Component getInvoker()
 633:   {
 634:     return invoker;
 635:   }
 636: 
 637:   /**
 638:    * Sets popup menu's invoker.
 639:    *
 640:    * @param component The new invoker of this popup menu
 641:    */
 642:   public void setInvoker(Component component)
 643:   {
 644:     invoker = component;
 645:   }
 646: 
 647:   /**
 648:    * This method displays JPopupMenu on the screen at the specified
 649:    * location. Note that x and y coordinates given to this method
 650:    * should be expressed in terms of the popup menus' invoker.
 651:    *
 652:    * @param component Invoker for this popup menu
 653:    * @param x x-coordinate of the popup menu relative to the specified invoker
 654:    * @param y y-coordiate of the popup menu relative to the specified invoker
 655:    */
 656:   public void show(Component component, int x, int y)
 657:   {
 658:     setInvoker(component);
 659:     Point p = new Point(x, y);
 660:     SwingUtilities.convertPointToScreen(p, component);
 661:     setLocation(p.x, p.y);
 662:     setVisible(true);
 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 (! changed)
 824:       setVisible(false);
 825:   }
 826: 
 827:   /**
 828:    * Return subcomonents of this popup menu. This method returns only
 829:    * components that implement the <code>MenuElement</code> interface.
 830:    *
 831:    * @return array of menu items belonging to this popup menu
 832:    */
 833:   public MenuElement[] getSubElements()
 834:   {
 835:     Component[] items = getComponents();
 836:     ArrayList subElements = new ArrayList();
 837: 
 838:     for (int i = 0; i < items.length; i++)
 839:       if (items[i] instanceof MenuElement)
 840:     subElements.add(items[i]);
 841: 
 842:     return (MenuElement[])
 843:       subElements.toArray(new MenuElement[subElements.size()]);
 844:   }
 845: 
 846:   /**
 847:    * Method of the MenuElement interface. Returns reference to itself.
 848:    *
 849:    * @return Returns reference to itself
 850:    */
 851:   public Component getComponent()
 852:   {
 853:     return this;
 854:   }
 855: 
 856:   /**
 857:    * Checks if observing mouse event should trigger popup
 858:    * menu to show on the screen.
 859:    *
 860:    * @param event MouseEvent to check
 861:    *
 862:    * @return true if the observing mouse event is popup trigger and false otherwise
 863:    */
 864:   public boolean isPopupTrigger(MouseEvent event)
 865:   {
 866:     return ((PopupMenuUI) getUI()).isPopupTrigger(event);
 867:   }
 868: 
 869:   /**
 870:    * DOCUMENT ME!
 871:    *
 872:    * @return DOCUMENT ME!
 873:    */
 874:   public AccessibleContext getAccessibleContext()
 875:   {
 876:     if (accessibleContext == null)
 877:       accessibleContext = new AccessibleJPopupMenu();
 878: 
 879:     return accessibleContext;
 880:   }
 881: 
 882:   /**
 883:    * This interface is used to display menu items of the JPopupMenu
 884:    */
 885:   private interface Popup
 886:   {
 887:     /**
 888:      * Displays container on the screen
 889:      *
 890:      * @param x x-coordinate of popup menu's location on the screen
 891:      * @param y y-coordinate of popup menu's location on the screen
 892:      * @param width width of the container that is used to display menu
 893:      * item's for popup menu
 894:      * @param height height of the container that is used to display menu
 895:      * item's for popup menu
 896:      */
 897:     void show(int x, int y, int width, int height);
 898: 
 899:     /**
 900:      * Hides container used to display popup menu item's from the screen
 901:      */
 902:     void hide();
 903:   }
 904: 
 905:   /**
 906:    * This class represents Popup menu that uses light weight container
 907:    * to display its contents.
 908:    */
 909:   private class LightWeightPopup extends Container implements Popup
 910:   {
 911:     /**
 912:      * Creates a new LightWeightPopup menu
 913:      *
 914:      * @param c Container containing menu items
 915:      */
 916:     private Component c;
 917: 
 918:     public LightWeightPopup(Container c)
 919:     {
 920:       this.c = c;
 921:     }
 922: 
 923:     /**
 924:      * Displayes lightweight container with menu items to the screen
 925:      *
 926:      * @param x x-coordinate of lightweight container on the screen
 927:      * @param y y-coordinate of lightweight container on the screen
 928:      * @param width width of the lightweight container
 929:      * @param height height of the lightweight container
 930:      */
 931:     public void show(int x, int y, int width, int height)
 932:     {
 933:       JLayeredPane layeredPane;
 934:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 935:       c.setBounds(x, y, width, height);
 936:       layeredPane.add(c, JLayeredPane.POPUP_LAYER, 0);
 937:     }
 938: 
 939:     /**
 940:      * Hides lightweight container from the screen
 941:      */
 942:     public void hide()
 943:     {
 944:       // FIXME: Right now the lightweight container is removed from JLayered
 945:       // pane. It is probably would be better in order to improve performance
 946:       // to make the container invisible instead of removing it everytime.
 947:       JLayeredPane layeredPane;
 948:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 949:       int index = layeredPane.getIndexOf(c);
 950:       layeredPane.remove(index);
 951:     }
 952:   }
 953: 
 954:   /**
 955:    * MediumWeightPopup is an AWT Panel with JPopupMenu's menu items.
 956:    * It is used to display JPopupMenu's menu items on the screen
 957:    */
 958:   private class MediumWeightPopup extends Panel implements Popup
 959:   {
 960:     /**
 961:      * Creates a new MediumWeightPopup object.
 962:      *
 963:      * @param c Container with JPopupMenu's menu items
 964:      */
 965:     public MediumWeightPopup(Container c)
 966:     {
 967:       this.add(c);
 968:     }
 969: 
 970:     /**
 971:      * Displays AWT Panel with its components on the screen
 972:      *
 973:      * @param x x-coordinate of the upper-left corner of the panel's
 974:      * @param y y-coordinate of the upper-left corner of the panel's
 975:      * @param width width of the panel
 976:      * @param height height of the panel
 977:      */
 978:     public void show(int x, int y, int width, int height)
 979:     {
 980:       JLayeredPane layeredPane;
 981:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 982:       layeredPane.add(this, JLayeredPane.POPUP_LAYER, 0);
 983:       this.setBounds(x, y, width, height);
 984:     }
 985: 
 986:     /**
 987:      * Hides This panel from the screen
 988:      */
 989:     public void hide()
 990:     {
 991:       // FIXME: Right now the lightweight container is removed from JLayered
 992:       // pane. It is probably would be better in order to improve performance
 993:       // to make the container invisible instead of removing it everytime.
 994:       JLayeredPane layeredPane;
 995:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 996:       int index = layeredPane.getIndexOf(this);
 997:       layeredPane.remove(index);
 998:     }
 999:   }
1000: 
1001:   /**
1002:    * HeavyWeightPopup is JWindow that is used to display JPopupMenu menu item's
1003:    * on the screen
1004:    */
1005:   private class HeavyWeightPopup extends JWindow implements Popup
1006:   {
1007:     /**
1008:      * Creates a new HeavyWeightPopup object.
1009:      *
1010:      * @param c Container containing menu items
1011:      */
1012:     public HeavyWeightPopup(Container c)
1013:     {
1014:       this.setContentPane(c);
1015:     }
1016: 
1017:     /**
1018:      * Displays JWindow container JPopupMenu's menu items to the screen
1019:      *
1020:      * @param x x-coordinate of JWindow containing menu items
1021:      * @param y y-coordinate of JWindow containing menu items
1022:      * @param width width of the JWindow
1023:      * @param height height of the JWindow
1024:      */
1025:     public void show(int x, int y, int width, int height)
1026:     {
1027:       this.setBounds(x, y, width, height);
1028:       this.show();
1029:     }
1030:   }
1031: 
1032:   /**
1033:    * This is the separator that can be used in popup menu.
1034:    */
1035:   public static class Separator extends JSeparator
1036:   {
1037:     public Separator()
1038:     {
1039:     }
1040: 
1041:     public String getUIClassID()
1042:     {
1043:       return "PopupMenuSeparatorUI";
1044:     }
1045:   }
1046: 
1047:   protected class AccessibleJPopupMenu extends AccessibleJComponent
1048:   {
1049:     private static final long serialVersionUID = 7423261328879849768L;
1050: 
1051:     protected AccessibleJPopupMenu()
1052:     {
1053:     }
1054: 
1055:     public AccessibleRole getAccessibleRole()
1056:     {
1057:       return AccessibleRole.POPUP_MENU;
1058:     }
1059:   }
1060: 
1061:   /* This class resizes popup menu and repaints popup menu appropriately if one
1062:    of item's action has changed */
1063:   protected class ActionChangeListener implements PropertyChangeListener
1064:   {
1065:     public void propertyChange(PropertyChangeEvent evt)
1066:     {
1067:       JPopupMenu.this.revalidate();
1068:       JPopupMenu.this.repaint();
1069:     }
1070:   }
1071: }