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:     this.setSize(this.getPreferredSize());
 236:   }
 237: 
 238:   /**
 239:    * Create menu item associated with the given action
 240:    * and inserts it into the popup menu at the specified index
 241:    *
 242:    * @param action Action for the new menu item
 243:    * @param index index in the popup menu at which to insert new menu item.
 244:    */
 245:   public void insert(Action action, int index)
 246:   {
 247:     JMenuItem item = new JMenuItem(action);
 248:     this.insert(item, index);
 249:   }
 250: 
 251:   /**
 252:    * Insert given component to the popup menu at the
 253:    * specified index
 254:    *
 255:    * @param component Component to insert
 256:    * @param index Index at which to insert given component
 257:    */
 258:   public void insert(Component component, int index)
 259:   {
 260:     GridBagConstraints constraints = new GridBagConstraints();
 261:     constraints.fill = GridBagConstraints.BOTH;
 262:     constraints.weightx = 100.0;
 263:     constraints.weighty = 100.0;
 264: 
 265:     constraints.gridy = index;
 266:     super.add(component, constraints, index);
 267: 
 268:     // need to change constraints for the components that were moved by 1
 269:     // due to the insertion
 270:     if (index != -1)
 271:       {
 272:     Component[] items = getComponents();
 273: 
 274:     for (int i = index + 1; i < items.length; i++)
 275:       {
 276:         constraints.gridy = i;
 277:         super.add(items[i], constraints, i);
 278:       }
 279:       }
 280:     this.setSize(this.getPreferredSize());
 281:   }
 282: 
 283:   /**
 284:    * Returns flag indicating if newly created JPopupMenu will use
 285:    * heavyweight or lightweight container to display its menu items
 286:    *
 287:    * @return true if JPopupMenu will use lightweight container to display
 288:    * menu items by default, and false otherwise.
 289:    */
 290:   public static boolean getDefaultLightWeightPopupEnabled()
 291:   {
 292:     return DefaultLightWeightPopupEnabled;
 293:   }
 294: 
 295:   /**
 296:    * Sets whether JPopupMenu should use ligthWeight container to
 297:    * display it menu items by default
 298:    *
 299:    * @param enabled true if JPopupMenu should use lightweight container
 300:    * for displaying its menu items, and false otherwise.
 301:    */
 302:   public static void setDefaultLightWeightPopupEnabled(boolean enabled)
 303:   {
 304:     DefaultLightWeightPopupEnabled = enabled;
 305:   }
 306: 
 307:   /**
 308:    * This method returns the UI used to display the JPopupMenu.
 309:    *
 310:    * @return The UI used to display the JPopupMenu.
 311:    */
 312:   public PopupMenuUI getUI()
 313:   {
 314:     return (PopupMenuUI) ui;
 315:   }
 316: 
 317:   /**
 318:    * Set the "UI" property of the menu item, which is a look and feel class
 319:    * responsible for handling popupMenu's input events and painting it.
 320:    *
 321:    * @param ui The new "UI" property
 322:    */
 323:   public void setUI(PopupMenuUI ui)
 324:   {
 325:     super.setUI(ui);
 326:   }
 327: 
 328:   /**
 329:    * This method sets this menuItem's UI to the UIManager's default for the
 330:    * current look and feel.
 331:    */
 332:   public void updateUI()
 333:   {
 334:     setUI((PopupMenuUI) UIManager.getUI(this));
 335:     invalidate();
 336:   }
 337: 
 338:   /**
 339:    * This method returns a name to identify which look and feel class will be
 340:    * the UI delegate for the menuItem.
 341:    *
 342:    * @return The Look and Feel classID. "PopupMenuUI"
 343:    */
 344:   public String getUIClassID()
 345:   {
 346:     return "PopupMenuUI";
 347:   }
 348: 
 349:   /**
 350:    * Returns selectionModel used by this popup menu to keep
 351:    * track of the selection.
 352:    *
 353:    * @return popup menu's selection model
 354:    */
 355:   public SingleSelectionModel getSelectionModel()
 356:   {
 357:     return selectionModel;
 358:   }
 359: 
 360:   /**
 361:    * Sets selection model for this popup menu
 362:    *
 363:    * @param model new selection model of this popup menu
 364:    */
 365:   public void setSelectionModel(SingleSelectionModel model)
 366:   {
 367:     selectionModel = model;
 368:   }
 369: 
 370:   /**
 371:    * Creates new menu item associated with a given action.
 372:    *
 373:    * @param action Action used to create new menu item
 374:    *
 375:    * @return new created menu item associated with a given action.
 376:    */
 377:   protected JMenuItem createActionComponent(Action action)
 378:   {
 379:     return new JMenuItem(action);
 380:   }
 381: 
 382:   /**
 383:    * Creates PropertyChangeListener that listens to PropertyChangeEvents
 384:    * occuring in the Action associated with given menu item in this popup menu.
 385:    *
 386:    * @param item MenuItem
 387:    *
 388:    * @return The PropertyChangeListener
 389:    */
 390:   protected PropertyChangeListener createActionChangeListener(JMenuItem item)
 391:   {
 392:     return new ActionChangeListener();
 393:   }
 394: 
 395:   /**
 396:    * Returns true if this popup menu will display its menu item in
 397:    * a lightweight container and false otherwise.
 398:    *
 399:    * @return true if this popup menu will display its menu items
 400:    * in a lightweight container and false otherwise.
 401:    */
 402:   public boolean isLightWeightPopupEnabled()
 403:   {
 404:     return lightWeightPopupEnabled;
 405:   }
 406: 
 407:   /**
 408:    * DOCUMENT ME!
 409:    *
 410:    * @param enabled DOCUMENT ME!
 411:    */
 412:   public void setLightWeightPopupEnabled(boolean enabled)
 413:   {
 414:     lightWeightPopupEnabled = enabled;
 415:   }
 416: 
 417:   /**
 418:    * Returns label for this popup menu
 419:    *
 420:    * @return label for this popup menu
 421:    */
 422:   public String getLabel()
 423:   {
 424:     return label;
 425:   }
 426: 
 427:   /**
 428:    * Sets label for this popup menu. This method fires PropertyChangeEvent
 429:    * when the label property is changed. Please note that most
 430:    * of the Look &amp; Feel will ignore this property.
 431:    *
 432:    * @param label label for this popup menu
 433:    */
 434:   public void setLabel(String label)
 435:   {
 436:     if (label != this.label)
 437:       {
 438:     String oldLabel = this.label;
 439:     this.label = label;
 440:     firePropertyChange("label", oldLabel, label);
 441:       }
 442:   }
 443: 
 444:   /**
 445:    * Adds separator to this popup menu
 446:    */
 447:   public void addSeparator()
 448:   {
 449:     // insert separator at the end of the list of menu items    
 450:     this.insert(new Separator(), -1);
 451:   }
 452: 
 453:   /**
 454:    * Adds popupMenuListener to listen for PopupMenuEvents fired
 455:    * by the JPopupMenu
 456:    *
 457:    * @param listener PopupMenuListener to add to JPopupMenu
 458:    */
 459:   public void addPopupMenuListener(PopupMenuListener listener)
 460:   {
 461:     listenerList.add(PopupMenuListener.class, listener);
 462:   }
 463: 
 464:   /**
 465:    * Removes PopupMenuListener from JPopupMenu's list of listeners
 466:    *
 467:    * @param listener PopupMenuListener which needs to be removed
 468:    */
 469:   public void removePopupMenuListener(PopupMenuListener listener)
 470:   {
 471:     listenerList.remove(PopupMenuListener.class, listener);
 472:   }
 473: 
 474:   /**
 475:    * Returns array of PopupMenuListeners that are listening to JPopupMenu
 476:    *
 477:    * @return Array of PopupMenuListeners that are listening to JPopupMenu
 478:    */
 479:   public PopupMenuListener[] getPopupMenuListeners()
 480:   {
 481:     return ((PopupMenuListener[]) listenerList.getListeners(PopupMenuListener.class));
 482:   }
 483: 
 484:   /**
 485:    * This method calls popupMenuWillBecomeVisible() of popup menu's
 486:    * PopupMenuListeners. This method is invoked just before popup menu
 487:    * will appear on the screen.
 488:    */
 489:   protected void firePopupMenuWillBecomeVisible()
 490:   {
 491:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 492: 
 493:     for (int i = 0; i < ll.length; i++)
 494:       ((PopupMenuListener) ll[i]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
 495:   }
 496: 
 497:   /**
 498:    * This method calls popupMenuWillBecomeInvisible() of popup
 499:    * menu's PopupMenuListeners. This method is invoked just before popup
 500:    * menu will disappear from the screen
 501:    */
 502:   protected void firePopupMenuWillBecomeInvisible()
 503:   {
 504:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 505: 
 506:     for (int i = 0; i < ll.length; i++)
 507:       ((PopupMenuListener) ll[i]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
 508:   }
 509: 
 510:   /**
 511:    * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
 512:    * This method is invoked just before popup menu is cancelled. This happens
 513:    * when popup menu is closed without selecting any of its menu items. This
 514:    * usually happens when the top-level window is resized or moved.
 515:    */
 516:   protected void firePopupMenuCanceled()
 517:   {
 518:     EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
 519: 
 520:     for (int i = 0; i < ll.length; i++)
 521:       ((PopupMenuListener) ll[i]).popupMenuCanceled(new PopupMenuEvent(this));
 522:   }
 523: 
 524:   /**
 525:    * This methods sets popup menu's size to its' preferred size. If the
 526:    * popup menu's size is previously set it will be ignored.
 527:    */
 528:   public void pack()
 529:   {
 530:     super.setSize(null);
 531:   }
 532: 
 533:   /**
 534:    * Return visibility of the popup menu
 535:    *
 536:    * @return true if popup menu is visible on the screen and false otherwise.
 537:    */
 538:   public boolean isVisible()
 539:   {
 540:     return visible;
 541:   }
 542: 
 543:   /**
 544:    * Sets visibility property of this popup menu. If the property is
 545:    * set to true then popup menu will be dispayed and popup menu will
 546:    * hide itself if visible property is set to false.
 547:    *
 548:    * @param visible true if popup menu will become visible and false otherwise.
 549:    */
 550:   public void setVisible(boolean visible)
 551:   {
 552:     if (visible == isVisible())
 553:       return;
 554: 
 555:     boolean old = isVisible();
 556:     this.visible = visible;
 557:     if (old != isVisible())
 558:       {
 559:     firePropertyChange("visible", old, isVisible());
 560:     if (visible)
 561:       {
 562:         firePopupMenuWillBecomeVisible();
 563:         Container rootContainer = (Container) SwingUtilities.getRoot(invoker);
 564: 
 565:         boolean fit = true;
 566:         Dimension size;
 567: 
 568:         // Determine the size of the popup menu
 569:         if (this.getSize().width == 0 && this.getSize().width == 0)
 570:           size = this.getPreferredSize();
 571:         else
 572:           size = this.getSize();
 573: 
 574:         if ((size.width > (rootContainer.getWidth() - popupLocation.x))
 575:             || (size.height > (rootContainer.getHeight() - popupLocation.y)))
 576:           fit = false;
 577:         if (lightWeightPopupEnabled && fit)
 578:           popup = new LightWeightPopup(this);
 579:         else
 580:           {
 581:         if (fit)
 582:           popup = new MediumWeightPopup(this);
 583:         else
 584:           popup = new HeavyWeightPopup(this);
 585:           }
 586:         if (popup instanceof LightWeightPopup
 587:             || popup instanceof MediumWeightPopup)
 588:           {
 589:         JLayeredPane layeredPane;
 590:         layeredPane = SwingUtilities.getRootPane(invoker)
 591:                                     .getLayeredPane();
 592:         Point p = new Point(popupLocation.x, popupLocation.y);
 593:         SwingUtilities.convertPointFromScreen(p, layeredPane);
 594:         popup.show(p.x, p.y, size.width, size.height);  
 595:           }
 596:         else
 597:           {
 598:         // Subtract insets of the top-level container if popup menu's
 599:         // top-left corner is inside it.
 600:         Insets insets = rootContainer.getInsets();
 601:         popup.show(popupLocation.x - insets.left,
 602:                    popupLocation.y - insets.top, size.width,
 603:                    size.height);
 604:           }
 605:       }
 606:     else
 607:       {
 608:         firePopupMenuWillBecomeInvisible();
 609:         popup.hide();
 610:       }
 611:       }
 612:   }
 613: 
 614:   /**
 615:    * Sets location of the popup menu.
 616:    *
 617:    * @param x X coordinate of the popup menu's location
 618:    * @param y Y coordinate of the popup menu's location
 619:    */
 620:   public void setLocation(int x, int y)
 621:   {
 622:     if (popupLocation == null)
 623:       popupLocation = new Point();
 624: 
 625:     popupLocation.x = x;
 626:     popupLocation.y = y;
 627:   }
 628: 
 629:   /**
 630:    * Returns popup menu's invoker.
 631:    *
 632:    * @return popup menu's invoker
 633:    */
 634:   public Component getInvoker()
 635:   {
 636:     return invoker;
 637:   }
 638: 
 639:   /**
 640:    * Sets popup menu's invoker.
 641:    *
 642:    * @param component The new invoker of this popup menu
 643:    */
 644:   public void setInvoker(Component component)
 645:   {
 646:     invoker = component;
 647:   }
 648: 
 649:   /**
 650:    * This method displays JPopupMenu on the screen at the specified
 651:    * location. Note that x and y coordinates given to this method
 652:    * should be expressed in terms of the popup menus' invoker.
 653:    *
 654:    * @param component Invoker for this popup menu
 655:    * @param x x-coordinate of the popup menu relative to the specified invoker
 656:    * @param y y-coordiate of the popup menu relative to the specified invoker
 657:    */
 658:   public void show(Component component, int x, int y)
 659:   {
 660:     setInvoker(component);
 661:     Point p = new Point(x, y);
 662:     SwingUtilities.convertPointToScreen(p, component);
 663:     setLocation(p.x, p.y);
 664:     setVisible(true);
 665:   }
 666: 
 667:   /**
 668:    * Returns component located at the specified index in the popup menu
 669:    *
 670:    * @param index index of the component to return
 671:    *
 672:    * @return component located at the specified index in the popup menu
 673:    *
 674:    * @deprecated Replaced by getComponent(int)
 675:    */
 676:   public Component getComponentAtIndex(int index)
 677:   {
 678:     return getComponent(index);
 679:   }
 680: 
 681:   /**
 682:    * Returns index of the specified component in the popup menu
 683:    *
 684:    * @param component Component to look for
 685:    *
 686:    * @return index of the specified component in the popup menu
 687:    */
 688:   public int getComponentIndex(Component component)
 689:   {
 690:     Component[] items = getComponents();
 691: 
 692:     for (int i = 0; i < items.length; i++)
 693:       {
 694:     if (items[i].equals(component))
 695:       return i;
 696:       }
 697: 
 698:     return -1;
 699:   }
 700: 
 701:   /**
 702:    * Sets size of the popup
 703:    *
 704:    * @param size Dimensions representing new size of the popup menu
 705:    */
 706:   public void setPopupSize(Dimension size)
 707:   {
 708:     super.setSize(size);
 709:   }
 710: 
 711:   /**
 712:    * Sets size of the popup menu
 713:    *
 714:    * @param width width for the new size
 715:    * @param height height for the new size
 716:    */
 717:   public void setPopupSize(int width, int height)
 718:   {
 719:     super.setSize(width, height);
 720:   }
 721: 
 722:   /**
 723:    * Selects specified component in this popup menu.
 724:    *
 725:    * @param selected component to select
 726:    */
 727:   public void setSelected(Component selected)
 728:   {
 729:     int index = getComponentIndex(selected);
 730:     selectionModel.setSelectedIndex(index);
 731:   }
 732: 
 733:   /**
 734:    * Checks if this popup menu paints its border.
 735:    *
 736:    * @return true if this popup menu paints its border and false otherwise.
 737:    */
 738:   public boolean isBorderPainted()
 739:   {
 740:     return borderPainted;
 741:   }
 742: 
 743:   /**
 744:    * Sets if the border of the popup menu should be
 745:    * painter or not.
 746:    *
 747:    * @param painted true if the border should be painted and false otherwise
 748:    */
 749:   public void setBorderPainted(boolean painted)
 750:   {
 751:     borderPainted = painted;
 752:   }
 753: 
 754:   /**
 755:    * Returns margin for this popup menu.
 756:    *
 757:    * @return margin for this popup menu.
 758:    */
 759:   public Insets getMargin()
 760:   {
 761:     return margin;
 762:   }
 763: 
 764:   /**
 765:    * A string that describes this JPopupMenu. Normally only used
 766:    * for debugging.
 767:    *
 768:    * @return A string describing this JMenuItem
 769:    */
 770:   protected String paramString()
 771:   {
 772:     StringBuffer sb = new StringBuffer();
 773:     sb.append(super.paramString());
 774:     sb.append(",label=");
 775:     if (getLabel() != null)
 776:       sb.append(getLabel());
 777:     sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
 778:     sb.append(",margin=");
 779:     if (getMargin() != null)
 780:       sb.append(margin);
 781:     sb.append(",paintBorder=").append(isBorderPainted());
 782:     return sb.toString();
 783:   }
 784: 
 785:   /**
 786:   * Process mouse events forwarded from MenuSelectionManager. This method 
 787:   * doesn't do anything. It is here to conform to the MenuElement interface.
 788:   *
 789:   * @param event event forwarded from MenuSelectionManager
 790:   * @param path path to the menu element from which event was generated
 791:   * @param manager MenuSelectionManager for the current menu hierarchy
 792:   */
 793:   public void processMouseEvent(MouseEvent event, MenuElement[] path,
 794:                                 MenuSelectionManager manager)
 795:   {
 796:     // Empty Implementation. This method is needed for the implementation
 797:     // of MenuElement interface
 798:   }
 799: 
 800:   /**
 801:    * Process key events forwarded from MenuSelectionManager. This method
 802:    * doesn't do anything. It is here to conform to the MenuElement interface.
 803:    *
 804:    * @param event event forwarded from MenuSelectionManager
 805:    * @param path path to the menu element from which event was generated
 806:    * @param manager MenuSelectionManager for the current menu hierarchy
 807:    *
 808:    */
 809:   public void processKeyEvent(KeyEvent event, MenuElement[] path,
 810:                               MenuSelectionManager manager)
 811:   {
 812:     // Empty Implementation. This method is needed for the implementation
 813:     // of MenuElement interface
 814:   }
 815: 
 816:   /**
 817:    * Method of MenuElement Interface. It is invoked when
 818:    * popupMenu's selection has changed
 819:    *
 820:    * @param changed true if this popupMenu is part of current menu
 821:    * hierarchy and false otherwise.
 822:    */
 823:   public void menuSelectionChanged(boolean changed)
 824:   {
 825:     if (! changed)
 826:       setVisible(false);
 827:   }
 828: 
 829:   /**
 830:    * Return subcomonents of this popup menu. This method returns only
 831:    * components that implement the <code>MenuElement</code> interface.
 832:    *
 833:    * @return array of menu items belonging to this popup menu
 834:    */
 835:   public MenuElement[] getSubElements()
 836:   {
 837:     Component[] items = getComponents();
 838:     ArrayList subElements = new ArrayList();
 839: 
 840:     for (int i = 0; i < items.length; i++)
 841:       if (items[i] instanceof MenuElement)
 842:     subElements.add(items[i]);
 843: 
 844:     return (MenuElement[])
 845:       subElements.toArray(new MenuElement[subElements.size()]);
 846:   }
 847: 
 848:   /**
 849:    * Method of the MenuElement interface. Returns reference to itself.
 850:    *
 851:    * @return Returns reference to itself
 852:    */
 853:   public Component getComponent()
 854:   {
 855:     return this;
 856:   }
 857: 
 858:   /**
 859:    * Checks if observing mouse event should trigger popup
 860:    * menu to show on the screen.
 861:    *
 862:    * @param event MouseEvent to check
 863:    *
 864:    * @return true if the observing mouse event is popup trigger and false otherwise
 865:    */
 866:   public boolean isPopupTrigger(MouseEvent event)
 867:   {
 868:     return ((PopupMenuUI) getUI()).isPopupTrigger(event);
 869:   }
 870: 
 871:   /**
 872:    * DOCUMENT ME!
 873:    *
 874:    * @return DOCUMENT ME!
 875:    */
 876:   public AccessibleContext getAccessibleContext()
 877:   {
 878:     if (accessibleContext == null)
 879:       accessibleContext = new AccessibleJPopupMenu();
 880: 
 881:     return accessibleContext;
 882:   }
 883: 
 884:   /**
 885:    * This interface is used to display menu items of the JPopupMenu
 886:    */
 887:   private interface Popup
 888:   {
 889:     /**
 890:      * Displays container on the screen
 891:      *
 892:      * @param x x-coordinate of popup menu's location on the screen
 893:      * @param y y-coordinate of popup menu's location on the screen
 894:      * @param width width of the container that is used to display menu
 895:      * item's for popup menu
 896:      * @param height height of the container that is used to display menu
 897:      * item's for popup menu
 898:      */
 899:     void show(int x, int y, int width, int height);
 900: 
 901:     /**
 902:      * Hides container used to display popup menu item's from the screen
 903:      */
 904:     void hide();
 905:   }
 906: 
 907:   /**
 908:    * This class represents Popup menu that uses light weight container
 909:    * to display its contents.
 910:    */
 911:   private class LightWeightPopup extends Container implements Popup
 912:   {
 913:     private Component c;
 914: 
 915:     /**
 916:      * Creates a new LightWeightPopup menu
 917:      *
 918:      * @param c Container containing menu items
 919:      */
 920:     public LightWeightPopup(Container c)
 921:     {
 922:       this.c = c;
 923:     }
 924: 
 925:     /**
 926:      * Displayes lightweight container with menu items to the screen
 927:      *
 928:      * @param x x-coordinate of lightweight container on the screen
 929:      * @param y y-coordinate of lightweight container on the screen
 930:      * @param width width of the lightweight container
 931:      * @param height height of the lightweight container
 932:      */
 933:     public void show(int x, int y, int width, int height)
 934:     {
 935:       JLayeredPane layeredPane;
 936:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 937:       c.setBounds(x, y, width, height);
 938:       layeredPane.add(c, JLayeredPane.POPUP_LAYER, 0);
 939:     }
 940: 
 941:     /**
 942:      * Hides lightweight container from the screen
 943:      */
 944:     public void hide()
 945:     {
 946:       // FIXME: Right now the lightweight container is removed from JLayered
 947:       // pane. It is probably would be better in order to improve performance
 948:       // to make the container invisible instead of removing it everytime.
 949:       JLayeredPane layeredPane;
 950:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 951:       int index = layeredPane.getIndexOf(c);
 952:       layeredPane.remove(index);
 953:     }
 954:   }
 955: 
 956:   /**
 957:    * MediumWeightPopup is an AWT Panel with JPopupMenu's menu items.
 958:    * It is used to display JPopupMenu's menu items on the screen
 959:    */
 960:   private class MediumWeightPopup extends Panel implements Popup
 961:   {
 962:     /**
 963:      * Creates a new MediumWeightPopup object.
 964:      *
 965:      * @param c Container with JPopupMenu's menu items
 966:      */
 967:     public MediumWeightPopup(Container c)
 968:     {
 969:       this.add(c);
 970:     }
 971: 
 972:     /**
 973:      * Displays AWT Panel with its components on the screen
 974:      *
 975:      * @param x x-coordinate of the upper-left corner of the panel's
 976:      * @param y y-coordinate of the upper-left corner of the panel's
 977:      * @param width width of the panel
 978:      * @param height height of the panel
 979:      */
 980:     public void show(int x, int y, int width, int height)
 981:     {
 982:       JLayeredPane layeredPane;
 983:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 984:       layeredPane.add(this, JLayeredPane.POPUP_LAYER, 0);
 985:       this.setBounds(x, y, width, height);
 986:     }
 987: 
 988:     /**
 989:      * Hides This panel from the screen
 990:      */
 991:     public void hide()
 992:     {
 993:       // FIXME: Right now the lightweight container is removed from JLayered
 994:       // pane. It is probably would be better in order to improve performance
 995:       // to make the container invisible instead of removing it everytime.
 996:       JLayeredPane layeredPane;
 997:       layeredPane = SwingUtilities.getRootPane(invoker).getLayeredPane();
 998:       int index = layeredPane.getIndexOf(this);
 999:       layeredPane.remove(index);
1000:     }
1001:   }
1002: 
1003:   /**
1004:    * HeavyWeightPopup is JWindow that is used to display JPopupMenu menu item's
1005:    * on the screen
1006:    */
1007:   private class HeavyWeightPopup extends JWindow implements Popup
1008:   {
1009:     /**
1010:      * Creates a new HeavyWeightPopup object.
1011:      *
1012:      * @param c Container containing menu items
1013:      */
1014:     public HeavyWeightPopup(Container c)
1015:     {
1016:       this.setContentPane(c);
1017:     }
1018: 
1019:     /**
1020:      * Displays JWindow container JPopupMenu's menu items to the screen
1021:      *
1022:      * @param x x-coordinate of JWindow containing menu items
1023:      * @param y y-coordinate of JWindow containing menu items
1024:      * @param width width of the JWindow
1025:      * @param height height of the JWindow
1026:      */
1027:     public void show(int x, int y, int width, int height)
1028:     {
1029:       this.setBounds(x, y, width, height);
1030:       this.show();
1031:     }
1032:   }
1033: 
1034:   /**
1035:    * This is the separator that can be used in popup menu.
1036:    */
1037:   public static class Separator extends JSeparator
1038:   {
1039:     public Separator()
1040:     {
1041:     }
1042: 
1043:     public String getUIClassID()
1044:     {
1045:       return "PopupMenuSeparatorUI";
1046:     }
1047:   }
1048: 
1049:   protected class AccessibleJPopupMenu extends AccessibleJComponent
1050:   {
1051:     private static final long serialVersionUID = 7423261328879849768L;
1052: 
1053:     protected AccessibleJPopupMenu()
1054:     {
1055:     }
1056: 
1057:     public AccessibleRole getAccessibleRole()
1058:     {
1059:       return AccessibleRole.POPUP_MENU;
1060:     }
1061:   }
1062: 
1063:   /* This class resizes popup menu and repaints popup menu appropriately if one
1064:    of item's action has changed */
1065:   protected class ActionChangeListener implements PropertyChangeListener
1066:   {
1067:     public void propertyChange(PropertyChangeEvent evt)
1068:     {
1069:       JPopupMenu.this.revalidate();
1070:       JPopupMenu.this.repaint();
1071:     }
1072:   }
1073: }