Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)
Frames | No Frames

Source for javax.swing.plaf.basic.BasicComboBoxUI

 1:  /* BasicComboBoxUI.java --
 2:  Copyright (C) 2004, 2005, 2006, 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.plaf.basic;
 40: 
 41:  import java.awt.Color;
 42:  import java.awt.Component;
 43:  import java.awt.Container;
 44:  import java.awt.Dimension;
 45:  import java.awt.Font;
 46:  import java.awt.Graphics;
 47:  import java.awt.Insets;
 48:  import java.awt.LayoutManager;
 49:  import java.awt.Rectangle;
 50:  import java.awt.event.FocusEvent;
 51:  import java.awt.event.FocusListener;
 52:  import java.awt.event.ItemEvent;
 53:  import java.awt.event.ItemListener;
 54:  import java.awt.event.KeyAdapter;
 55:  import java.awt.event.KeyEvent;
 56:  import java.awt.event.KeyListener;
 57:  import java.awt.event.MouseListener;
 58:  import java.awt.event.MouseMotionListener;
 59:  import java.beans.PropertyChangeEvent;
 60:  import java.beans.PropertyChangeListener;
 61: 
 62:  import javax.accessibility.Accessible;
 63:  import javax.accessibility.AccessibleContext;
 64:  import javax.swing.CellRendererPane;
 65:  import javax.swing.ComboBoxEditor;
 66:  import javax.swing.ComboBoxModel;
 67:  import javax.swing.DefaultListCellRenderer;
 68:  import javax.swing.InputMap;
 69:  import javax.swing.JButton;
 70:  import javax.swing.JComboBox;
 71:  import javax.swing.JComponent;
 72:  import javax.swing.JList;
 73:  import javax.swing.ListCellRenderer;
 74:  import javax.swing.LookAndFeel;
 75:  import javax.swing.SwingUtilities;
 76:  import javax.swing.UIManager;
 77:  import javax.swing.event.ListDataEvent;
 78:  import javax.swing.event.ListDataListener;
 79:  import javax.swing.plaf.ComboBoxUI;
 80:  import javax.swing.plaf.ComponentUI;
 81:  import javax.swing.plaf.UIResource;
 82: 
 83:  /**
 84:  * A UI delegate for the {@link JComboBox} component.
 85:  *
 86:  * @author Olga Rodimina
 87:  * @author Robert Schuster
 88:  */
 89:  public class BasicComboBoxUI extends ComboBoxUI
 90: {
 91:  /**
 92:  * The arrow button that is displayed in the right side of JComboBox. This
 93:  * button is used to hide and show combo box's list of items.
 94:  */
 95:  protected JButton arrowButton;
 96: 
 97:  /**
 98:  * The combo box represented by this UI delegate.
 99:  */
 100:  protected JComboBox comboBox;
 101: 
 102:  /**
 103:  * The component that is responsible for displaying/editing the selected 
 104:  * item of the combo box. 
 105:  * 
 106:  * @see BasicComboBoxEditor#getEditorComponent()
 107:  */
 108:  protected Component editor;
 109: 
 110:  /**
 111:  * A listener listening to focus events occurring in the {@link JComboBox}.
 112:  */
 113:  protected FocusListener focusListener;
 114: 
 115:  /**
 116:  * A flag indicating whether JComboBox currently has the focus.
 117:  */
 118:  protected boolean hasFocus;
 119: 
 120:  /**
 121:  * A listener listening to item events fired by the {@link JComboBox}.
 122:  */
 123:  protected ItemListener itemListener;
 124: 
 125:  /**
 126:  * A listener listening to key events that occur while {@link JComboBox} has
 127:  * the focus.
 128:  */
 129:  protected KeyListener keyListener;
 130: 
 131:  /**
 132:  * List used when rendering selected item of the combo box. The selection
 133:  * and foreground colors for combo box renderer are configured from this
 134:  * list.
 135:  */
 136:  protected JList listBox;
 137: 
 138:  /**
 139:  * ListDataListener listening to JComboBox model
 140:  */
 141:  protected ListDataListener listDataListener;
 142: 
 143:  /**
 144:  * Popup list containing the combo box's menu items.
 145:  */
 146:  protected ComboPopup popup;
 147:  
 148:  protected KeyListener popupKeyListener;
 149:  
 150:  protected MouseListener popupMouseListener;
 151:  
 152:  protected MouseMotionListener popupMouseMotionListener;
 153: 
 154:  /**
 155:  * Listener listening to changes in the bound properties of JComboBox
 156:  */
 157:  protected PropertyChangeListener propertyChangeListener;
 158: 
 159:  /* Size of the largest item in the comboBox
 160:  * This is package-private to avoid an accessor method.
 161:  */
 162:  Dimension displaySize = new Dimension();
 163: 
 164:  /**
 165:  * Used to render the combo box values.
 166:  */
 167:  protected CellRendererPane currentValuePane;
 168: 
 169:  /**
 170:  * The current minimum size if isMinimumSizeDirty is false.
 171:  * Setup by getMinimumSize() and invalidated by the various listeners.
 172:  */
 173:  protected Dimension cachedMinimumSize;
 174: 
 175:  /**
 176:  * Indicates whether or not the cachedMinimumSize field is valid or not.
 177:  */
 178:  protected boolean isMinimumSizeDirty = true;
 179: 
 180:  /**
 181:  * Creates a new <code>BasicComboBoxUI</code> object.
 182:  */
 183:  public BasicComboBoxUI()
 184:  {
 185:  currentValuePane = new CellRendererPane();
 186:  cachedMinimumSize = new Dimension();
 187:  }
 188: 
 189:  /**
 190:  * A factory method to create a UI delegate for the given 
 191:  * {@link JComponent}, which should be a {@link JComboBox}.
 192:  *
 193:  * @param c The {@link JComponent} a UI is being created for.
 194:  *
 195:  * @return A UI delegate for the {@link JComponent}.
 196:  */
 197:  public static ComponentUI createUI(JComponent c)
 198:  {
 199:  return new BasicComboBoxUI();
 200:  }
 201: 
 202:  /**
 203:  * Installs the UI for the given {@link JComponent}.
 204:  *
 205:  * @param c the JComponent to install a UI for.
 206:  * 
 207:  * @see #uninstallUI(JComponent)
 208:  */
 209:  public void installUI(JComponent c)
 210:  {
 211:  super.installUI(c);
 212: 
 213:  if (c instanceof JComboBox)
 214:  {
 215:  isMinimumSizeDirty = true;
 216:  comboBox = (JComboBox) c;
 217:  installDefaults();
 218:  popup = createPopup();
 219:  listBox = popup.getList();
 220: 
 221:  // Set editor and renderer for the combo box. Editor is used
 222:  // only if combo box becomes editable, otherwise renderer is used
 223:  // to paint the selected item; combobox is not editable by default.
 224:  ListCellRenderer renderer = comboBox.getRenderer();
 225:  if (renderer == null || renderer instanceof UIResource)
 226:  comboBox.setRenderer(createRenderer());
 227: 
 228:  ComboBoxEditor currentEditor = comboBox.getEditor();
 229:  if (currentEditor == null || currentEditor instanceof UIResource)
 230:  {
 231:  currentEditor = createEditor();
 232:  comboBox.setEditor(currentEditor);
 233:  } 
 234: 
 235:  installComponents();
 236:  installListeners();
 237:  comboBox.setLayout(createLayoutManager());
 238:  comboBox.setFocusable(true);
 239:  installKeyboardActions();
 240:  comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP,
 241:  Boolean.TRUE);
 242:  }
 243:  }
 244: 
 245:  /**
 246:  * Uninstalls the UI for the given {@link JComponent}.
 247:  *
 248:  * @param c The JComponent that is having this UI removed.
 249:  * 
 250:  * @see #installUI(JComponent)
 251:  */
 252:  public void uninstallUI(JComponent c)
 253:  {
 254:  setPopupVisible(comboBox, false);
 255:  popup.uninstallingUI();
 256:  uninstallKeyboardActions();
 257:  comboBox.setLayout(null);
 258:  uninstallComponents();
 259:  uninstallListeners();
 260:  uninstallDefaults();
 261:  comboBox = null;
 262:  }
 263: 
 264:  /**
 265:  * Installs the defaults that are defined in the {@link BasicLookAndFeel} 
 266:  * for this {@link JComboBox}.
 267:  * 
 268:  * @see #uninstallDefaults()
 269:  */
 270:  protected void installDefaults()
 271:  {
 272:  LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background",
 273:  "ComboBox.foreground", "ComboBox.font");
 274:  LookAndFeel.installBorder(comboBox, "ComboBox.border");
 275:  }
 276: 
 277:  /**
 278:  * Creates and installs the listeners for this UI.
 279:  * 
 280:  * @see #uninstallListeners()
 281:  */
 282:  protected void installListeners()
 283:  {
 284:  // install combo box's listeners
 285:  propertyChangeListener = createPropertyChangeListener();
 286:  comboBox.addPropertyChangeListener(propertyChangeListener);
 287: 
 288:  focusListener = createFocusListener();
 289:  comboBox.addFocusListener(focusListener);
 290: 
 291:  itemListener = createItemListener();
 292:  comboBox.addItemListener(itemListener);
 293: 
 294:  keyListener = createKeyListener();
 295:  comboBox.addKeyListener(keyListener);
 296: 
 297:  // install listeners that listen to combo box model
 298:  listDataListener = createListDataListener();
 299:  comboBox.getModel().addListDataListener(listDataListener);
 300: 
 301:  // Install mouse and key listeners from the popup.
 302:  popupMouseListener = popup.getMouseListener();
 303:  comboBox.addMouseListener(popupMouseListener);
 304: 
 305:  popupMouseMotionListener = popup.getMouseMotionListener();
 306:  comboBox.addMouseMotionListener(popupMouseMotionListener);
 307: 
 308:  popupKeyListener = popup.getKeyListener();
 309:  comboBox.addKeyListener(popupKeyListener);
 310:  }
 311: 
 312:  /**
 313:  * Uninstalls the defaults and sets any objects created during
 314:  * install to <code>null</code>.
 315:  * 
 316:  * @see #installDefaults()
 317:  */
 318:  protected void uninstallDefaults()
 319:  {
 320:  if (comboBox.getFont() instanceof UIResource)
 321:  comboBox.setFont(null);
 322: 
 323:  if (comboBox.getForeground() instanceof UIResource)
 324:  comboBox.setForeground(null);
 325:  
 326:  if (comboBox.getBackground() instanceof UIResource)
 327:  comboBox.setBackground(null);
 328: 
 329:  LookAndFeel.uninstallBorder(comboBox);
 330:  }
 331: 
 332:  /**
 333:  * Detaches all the listeners we attached in {@link #installListeners}.
 334:  * 
 335:  * @see #installListeners()
 336:  */
 337:  protected void uninstallListeners()
 338:  {
 339:  comboBox.removePropertyChangeListener(propertyChangeListener);
 340:  propertyChangeListener = null;
 341: 
 342:  comboBox.removeFocusListener(focusListener);
 343:  listBox.removeFocusListener(focusListener);
 344:  focusListener = null;
 345: 
 346:  comboBox.removeItemListener(itemListener);
 347:  itemListener = null;
 348: 
 349:  comboBox.removeKeyListener(keyListener);
 350:  keyListener = null;
 351: 
 352:  comboBox.getModel().removeListDataListener(listDataListener);
 353:  listDataListener = null;
 354: 
 355:  if (popupMouseListener != null)
 356:  comboBox.removeMouseListener(popupMouseListener);
 357:  popupMouseListener = null;
 358: 
 359:  if (popupMouseMotionListener != null)
 360:  comboBox.removeMouseMotionListener(popupMouseMotionListener);
 361:  popupMouseMotionListener = null;
 362: 
 363:  if (popupKeyListener != null)
 364:  comboBox.removeKeyListener(popupKeyListener);
 365:  popupKeyListener = null;
 366:  }
 367: 
 368:  /**
 369:  * Creates the popup that will contain list of combo box's items.
 370:  *
 371:  * @return popup containing list of combo box's items
 372:  */
 373:  protected ComboPopup createPopup()
 374:  {
 375:  return new BasicComboPopup(comboBox);
 376:  }
 377: 
 378:  /**
 379:  * Creates a {@link KeyListener} to listen to key events.
 380:  *
 381:  * @return KeyListener that listens to key events.
 382:  */
 383:  protected KeyListener createKeyListener()
 384:  {
 385:  return new KeyHandler();
 386:  }
 387: 
 388:  /**
 389:  * Creates the {@link FocusListener} that will listen to changes in this
 390:  * JComboBox's focus.
 391:  *
 392:  * @return the FocusListener.
 393:  */
 394:  protected FocusListener createFocusListener()
 395:  {
 396:  return new FocusHandler();
 397:  }
 398: 
 399:  /**
 400:  * Creates a {@link ListDataListener} to listen to the combo box's data model.
 401:  *
 402:  * @return The new listener.
 403:  */
 404:  protected ListDataListener createListDataListener()
 405:  {
 406:  return new ListDataHandler();
 407:  }
 408: 
 409:  /**
 410:  * Creates an {@link ItemListener} that will listen to the changes in
 411:  * the JComboBox's selection.
 412:  *
 413:  * @return The ItemListener
 414:  */
 415:  protected ItemListener createItemListener()
 416:  {
 417:  return new ItemHandler();
 418:  }
 419: 
 420:  /**
 421:  * Creates a {@link PropertyChangeListener} to listen to the changes in
 422:  * the JComboBox's bound properties.
 423:  *
 424:  * @return The PropertyChangeListener
 425:  */
 426:  protected PropertyChangeListener createPropertyChangeListener()
 427:  {
 428:  return new PropertyChangeHandler();
 429:  }
 430: 
 431:  /**
 432:  * Creates and returns a layout manager for the combo box. Subclasses can 
 433:  * override this method to provide a different layout.
 434:  *
 435:  * @return a layout manager for the combo box.
 436:  */
 437:  protected LayoutManager createLayoutManager()
 438:  {
 439:  return new ComboBoxLayoutManager();
 440:  }
 441: 
 442:  /**
 443:  * Creates a component that will be responsible for rendering the
 444:  * selected component in the combo box.
 445:  *
 446:  * @return A renderer for the combo box.
 447:  */
 448:  protected ListCellRenderer createRenderer()
 449:  {
 450:  return new BasicComboBoxRenderer.UIResource();
 451:  }
 452: 
 453:  /**
 454:  * Creates the component that will be responsible for displaying/editing
 455:  * the selected item in the combo box. This editor is used only when combo 
 456:  * box is editable.
 457:  *
 458:  * @return A new component that will be responsible for displaying/editing
 459:  * the selected item in the combo box.
 460:  */
 461:  protected ComboBoxEditor createEditor()
 462:  {
 463:  return new BasicComboBoxEditor.UIResource();
 464:  }
 465: 
 466:  /**
 467:  * Installs the components for this JComboBox. ArrowButton, main
 468:  * part of combo box (upper part) and popup list of items are created and
 469:  * configured here.
 470:  */
 471:  protected void installComponents()
 472:  {
 473:  // create and install arrow button
 474:  arrowButton = createArrowButton();
 475:  comboBox.add(arrowButton);
 476:  if (arrowButton != null)
 477:  configureArrowButton();
 478: 
 479:  if (comboBox.isEditable())
 480:  addEditor();
 481: 
 482:  comboBox.add(currentValuePane);
 483:  }
 484: 
 485:  /**
 486:  * Uninstalls components from this {@link JComboBox}.
 487:  * 
 488:  * @see #installComponents()
 489:  */
 490:  protected void uninstallComponents()
 491:  {
 492:  // Unconfigure arrow button.
 493:  if (arrowButton != null)
 494:  {
 495:  unconfigureArrowButton();
 496:  }
 497: 
 498:  // Unconfigure editor.
 499:  if (editor != null)
 500:  {
 501:  unconfigureEditor();
 502:  }
 503: 
 504:  comboBox.removeAll();
 505:  arrowButton = null;
 506:  }
 507: 
 508:  /**
 509:  * Adds the current editor to the combo box.
 510:  */
 511:  public void addEditor()
 512:  {
 513:  removeEditor();
 514:  editor = comboBox.getEditor().getEditorComponent();
 515:  if (editor != null)
 516:  {
 517:  configureEditor();
 518:  comboBox.add(editor);
 519:  }
 520:  }
 521: 
 522:  /**
 523:  * Removes the current editor from the combo box.
 524:  */
 525:  public void removeEditor()
 526:  {
 527:  if (editor != null)
 528:  {
 529:  unconfigureEditor();
 530:  comboBox.remove(editor);
 531:  }
 532:  }
 533: 
 534:  /**
 535:  * Configures the editor for this combo box.
 536:  */
 537:  protected void configureEditor()
 538:  {
 539:  editor.setFont(comboBox.getFont());
 540:  if (popupKeyListener != null)
 541:  editor.addKeyListener(popupKeyListener);
 542:  if (keyListener != null)
 543:  editor.addKeyListener(keyListener);
 544:  comboBox.configureEditor(comboBox.getEditor(),
 545:  comboBox.getSelectedItem());
 546:  }
 547: 
 548:  /**
 549:  * Unconfigures the editor for this combo box. 
 550:  */
 551:  protected void unconfigureEditor()
 552:  {
 553:  if (popupKeyListener != null)
 554:  editor.removeKeyListener(popupKeyListener);
 555:  if (keyListener != null)
 556:  editor.removeKeyListener(keyListener);
 557:  }
 558: 
 559:  /**
 560:  * Configures the arrow button.
 561:  * 
 562:  * @see #configureArrowButton()
 563:  */
 564:  public void configureArrowButton()
 565:  {
 566:  if (arrowButton != null)
 567:  {
 568:  arrowButton.setEnabled(comboBox.isEnabled());
 569:  arrowButton.setFocusable(false);
 570:  arrowButton.addMouseListener(popup.getMouseListener());
 571:  arrowButton.addMouseMotionListener(popup.getMouseMotionListener());
 572:  
 573:  // Mark the button as not closing the popup, we handle this ourselves.
 574:  arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP,
 575:  Boolean.TRUE);
 576:  }
 577:  }
 578: 
 579:  /**
 580:  * Unconfigures the arrow button.
 581:  * 
 582:  * @see #configureArrowButton()
 583:  *
 584:  * @specnote The specification says this method is implementation specific
 585:  * and should not be used or overridden.
 586:  */
 587:  public void unconfigureArrowButton()
 588:  {
 589:  if (arrowButton != null)
 590:  {
 591:  if (popupMouseListener != null)
 592:  arrowButton.removeMouseListener(popupMouseListener);
 593:  if (popupMouseMotionListener != null)
 594:  arrowButton.removeMouseMotionListener(popupMouseMotionListener);
 595:  }
 596:  }
 597: 
 598:  /**
 599:  * Creates an arrow button for this {@link JComboBox}. The arrow button is
 600:  * displayed at the right end of the combo box and is used to display/hide
 601:  * the drop down list of items.
 602:  *
 603:  * @return A new button.
 604:  */
 605:  protected JButton createArrowButton()
 606:  {
 607:  return new BasicArrowButton(BasicArrowButton.SOUTH);
 608:  }
 609: 
 610:  /**
 611:  * Returns <code>true</code> if the popup is visible, and <code>false</code>
 612:  * otherwise.
 613:  *
 614:  * @param c The JComboBox to check
 615:  *
 616:  * @return <code>true</code> if popup part of the JComboBox is visible and 
 617:  * <code>false</code> otherwise.
 618:  */
 619:  public boolean isPopupVisible(JComboBox c)
 620:  {
 621:  return popup.isVisible();
 622:  }
 623: 
 624:  /**
 625:  * Displays/hides the {@link JComboBox}'s list of items on the screen.
 626:  *
 627:  * @param c The combo box, for which list of items should be
 628:  * displayed/hidden
 629:  * @param v true if show popup part of the jcomboBox and false to hide.
 630:  */
 631:  public void setPopupVisible(JComboBox c, boolean v)
 632:  {
 633:  if (v)
 634:  popup.show();
 635:  else
 636:  popup.hide();
 637:  }
 638: 
 639:  /**
 640:  * JComboBox is focus traversable if it is editable and not otherwise.
 641:  *
 642:  * @param c combo box for which to check whether it is focus traversable
 643:  *
 644:  * @return true if focus tranversable and false otherwise
 645:  */
 646:  public boolean isFocusTraversable(JComboBox c)
 647:  {
 648:  if (!comboBox.isEditable())
 649:  return true;
 650: 
 651:  return false;
 652:  }
 653: 
 654:  /**
 655:  * Paints given menu item using specified graphics context
 656:  *
 657:  * @param g The graphics context used to paint this combo box
 658:  * @param c comboBox which needs to be painted.
 659:  */
 660:  public void paint(Graphics g, JComponent c)
 661:  {
 662:  hasFocus = comboBox.hasFocus();
 663:  if (! comboBox.isEditable())
 664:  {
 665:  Rectangle rect = rectangleForCurrentValue();
 666:  paintCurrentValueBackground(g, rect, hasFocus);
 667:  paintCurrentValue(g, rect, hasFocus);
 668:  }
 669:  }
 670: 
 671:  /**
 672:  * Returns preferred size for the combo box.
 673:  *
 674:  * @param c comboBox for which to get preferred size
 675:  *
 676:  * @return The preferred size for the given combo box
 677:  */
 678:  public Dimension getPreferredSize(JComponent c)
 679:  {
 680:  return getMinimumSize(c);
 681:  }
 682: 
 683:  /**
 684:  * Returns the minimum size for this {@link JComboBox} for this
 685:  * look and feel. Also makes sure cachedMinimimSize is setup correctly.
 686:  *
 687:  * @param c The {@link JComponent} to find the minimum size for.
 688:  *
 689:  * @return The dimensions of the minimum size.
 690:  */
 691:  public Dimension getMinimumSize(JComponent c)
 692:  {
 693:  if (isMinimumSizeDirty)
 694:  {
 695:  Insets i = getInsets();
 696:  Dimension d = getDisplaySize();
 697:  d.width += i.left + i.right + d.height;
 698:  cachedMinimumSize = new Dimension(d.width, d.height + i.top + i.bottom);
 699:  isMinimumSizeDirty = false;
 700:  }
 701:  return new Dimension(cachedMinimumSize);
 702:  }
 703: 
 704:  /**
 705:  * Returns the maximum size for this {@link JComboBox} for this
 706:  * look and feel.
 707:  *
 708:  * @param c The {@link JComponent} to find the maximum size for
 709:  *
 710:  * @return The maximum size (<code>Dimension(32767, 32767)</code>).
 711:  */
 712:  public Dimension getMaximumSize(JComponent c)
 713:  {
 714:  return new Dimension(32767, 32767);
 715:  }
 716: 
 717:  /**
 718:  * Returns the number of accessible children of the combobox.
 719:  *
 720:  * @param c the component (combobox) to check, ignored
 721:  *
 722:  * @return the number of accessible children of the combobox
 723:  */
 724:  public int getAccessibleChildrenCount(JComponent c)
 725:  {
 726:  int count = 1;
 727:  if (comboBox.isEditable())
 728:  count = 2;
 729:  return count;
 730:  }
 731: 
 732:  /**
 733:  * Returns the accessible child with the specified index.
 734:  *
 735:  * @param c the component, this is ignored
 736:  * @param i the index of the accessible child to return
 737:  */
 738:  public Accessible getAccessibleChild(JComponent c, int i)
 739:  {
 740:  Accessible child = null;
 741:  switch (i)
 742:  {
 743:  case 0: // The popup.
 744:  if (popup instanceof Accessible)
 745:  {
 746:  AccessibleContext ctx = ((Accessible) popup).getAccessibleContext();
 747:  ctx.setAccessibleParent(comboBox);
 748:  child = (Accessible) popup;
 749:  }
 750:  break;
 751:  case 1: // The editor, if any.
 752:  if (comboBox.isEditable() && editor instanceof Accessible)
 753:  {
 754:  AccessibleContext ctx =
 755:  ((Accessible) editor).getAccessibleContext();
 756:  ctx.setAccessibleParent(comboBox);
 757:  child = (Accessible) editor;
 758:  }
 759:  break;
 760:  }
 761:  return child;
 762:  }
 763: 
 764:  /**
 765:  * Returns true if the specified key is a navigation key and false otherwise
 766:  *
 767:  * @param keyCode a key for which to check whether it is navigation key or
 768:  * not.
 769:  *
 770:  * @return true if the specified key is a navigation key and false otherwis
 771:  */
 772:  protected boolean isNavigationKey(int keyCode)
 773:  {
 774:  return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN
 775:  || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT
 776:  || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE
 777:  || keyCode == KeyEvent.VK_TAB;
 778:  }
 779: 
 780:  /**
 781:  * Selects next possible item relative to the current selection
 782:  * to be next selected item in the combo box.
 783:  */
 784:  protected void selectNextPossibleValue()
 785:  {
 786:  int index = comboBox.getSelectedIndex();
 787:  if (index != comboBox.getItemCount() - 1)
 788:  comboBox.setSelectedIndex(index + 1);
 789:  }
 790: 
 791:  /**
 792:  * Selects previous item relative to current selection to be
 793:  * next selected item.
 794:  */
 795:  protected void selectPreviousPossibleValue()
 796:  {
 797:  int index = comboBox.getSelectedIndex();
 798:  if (index > 0)
 799:  comboBox.setSelectedIndex(index - 1);
 800:  }
 801: 
 802:  /**
 803:  * Displays combo box popup if the popup is not currently shown
 804:  * on the screen and hides it if it is currently shown
 805:  */
 806:  protected void toggleOpenClose()
 807:  {
 808:  setPopupVisible(comboBox, ! isPopupVisible(comboBox));
 809:  }
 810: 
 811:  /**
 812:  * Returns the bounds in which comboBox's selected item will be
 813:  * displayed.
 814:  *
 815:  * @return rectangle bounds in which comboBox's selected Item will be
 816:  * displayed
 817:  */
 818:  protected Rectangle rectangleForCurrentValue()
 819:  {
 820:  int w = comboBox.getWidth();
 821:  int h = comboBox.getHeight();
 822:  Insets i = comboBox.getInsets();
 823:  int arrowSize = h - (i.top + i.bottom);
 824:  if (arrowButton != null)
 825:  arrowSize = arrowButton.getWidth();
 826:  return new Rectangle(i.left, i.top, w - (i.left + i.right + arrowSize),
 827:  h - (i.top + i.left));
 828:  }
 829: 
 830:  /**
 831:  * Returns the insets of the current border.
 832:  *
 833:  * @return Insets representing space between combo box and its border
 834:  */
 835:  protected Insets getInsets()
 836:  {
 837:  return comboBox.getInsets();
 838:  }
 839: 
 840:  /**
 841:  * Paints currently selected value in the main part of the combo
 842:  * box (part without popup).
 843:  *
 844:  * @param g graphics context
 845:  * @param bounds Rectangle representing the size of the area in which
 846:  * selected item should be drawn
 847:  * @param hasFocus true if combo box has focus and false otherwise
 848:  */
 849:  public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus)
 850:  {
 851:  /* Gets the component to be drawn for the current value.
 852:  * If there is currently no selected item we will take an empty
 853:  * String as replacement.
 854:  */
 855:  ListCellRenderer renderer = comboBox.getRenderer();
 856:  if (comboBox.getSelectedIndex() != -1)
 857:  {
 858:  Component comp;
 859:  if (hasFocus && ! isPopupVisible(comboBox))
 860:  {
 861:  comp = renderer.getListCellRendererComponent(listBox,
 862:  comboBox.getSelectedItem(), -1, true, false);
 863:  }
 864:  else
 865:  {
 866:  comp = renderer.getListCellRendererComponent(listBox,
 867:  comboBox.getSelectedItem(), -1, false, false);
 868:  Color bg = UIManager.getColor("ComboBox.disabledForeground");
 869:  comp.setBackground(bg);
 870:  }
 871:  comp.setFont(comboBox.getFont());
 872:  if (hasFocus && ! isPopupVisible(comboBox))
 873:  {
 874:  comp.setForeground(listBox.getSelectionForeground());
 875:  comp.setBackground(listBox.getSelectionBackground());
 876:  }
 877:  else if (comboBox.isEnabled())
 878:  {
 879:  comp.setForeground(comboBox.getForeground());
 880:  comp.setBackground(comboBox.getBackground());
 881:  }
 882:  else
 883:  {
 884:  Color fg = UIManager.getColor("ComboBox.disabledForeground");
 885:  comp.setForeground(fg);
 886:  Color bg = UIManager.getColor("ComboBox.disabledBackground");
 887:  comp.setBackground(bg);
 888:  }
 889:  currentValuePane.paintComponent(g, comp, comboBox, bounds.x, bounds.y,
 890:  bounds.width, bounds.height);
 891:  }
 892:  }
 893: 
 894:  /**
 895:  * Paints the background of part of the combo box, where currently
 896:  * selected value is displayed. If the combo box has focus this method
 897:  * should also paint focus rectangle around the combo box.
 898:  *
 899:  * @param g graphics context
 900:  * @param bounds Rectangle representing the size of the largest item in the
 901:  * comboBox
 902:  * @param hasFocus true if combo box has fox and false otherwise
 903:  */
 904:  public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
 905:  boolean hasFocus)
 906:  {
 907:  Color saved = g.getColor();
 908:  if (comboBox.isEnabled())
 909:  g.setColor(UIManager.getColor("UIManager.background"));
 910:  else
 911:  g.setColor(UIManager.getColor("UIManager.disabledBackground"));
 912:  g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
 913:  g.setColor(saved);
 914:  }
 915: 
 916:  private static final ListCellRenderer DEFAULT_RENDERER
 917:  = new DefaultListCellRenderer();
 918: 
 919:  /**
 920:  * Returns the default size for the display area of a combo box that does 
 921:  * not contain any elements. This method returns the width and height of
 922:  * a single space in the current font, plus a margin of 1 pixel. 
 923:  *
 924:  * @return The default display size.
 925:  * 
 926:  * @see #getDisplaySize()
 927:  */
 928:  protected Dimension getDefaultSize()
 929:  {
 930:  Component comp = DEFAULT_RENDERER.getListCellRendererComponent(listBox,
 931:  " ", -1, false, false);
 932:  currentValuePane.add(comp);
 933:  comp.setFont(comboBox.getFont());
 934:  Dimension d = comp.getPreferredSize();
 935:  currentValuePane.remove(comp);
 936:  return d;
 937:  }
 938: 
 939:  /**
 940:  * Returns the size of the display area for the combo box. This size will be 
 941:  * the size of the combo box, not including the arrowButton.
 942:  *
 943:  * @return The size of the display area for the combo box.
 944:  */
 945:  protected Dimension getDisplaySize()
 946:  {
 947:  Dimension dim = new Dimension();
 948:  ListCellRenderer renderer = comboBox.getRenderer();
 949:  if (renderer == null)
 950:  {
 951:  renderer = DEFAULT_RENDERER;
 952:  }
 953:  
 954:  Object prototype = comboBox.getPrototypeDisplayValue();
 955:  if (prototype != null)
 956:  {
 957:  Component comp = renderer.getListCellRendererComponent(listBox, 
 958:  prototype, -1, false, false);
 959:  currentValuePane.add(comp);
 960:  comp.setFont(comboBox.getFont());
 961:  Dimension renderSize = comp.getPreferredSize();
 962:  currentValuePane.remove(comp);
 963:  dim.height = renderSize.height;
 964:  dim.width = renderSize.width;
 965:  }
 966:  else
 967:  {
 968:  ComboBoxModel model = comboBox.getModel();
 969:  int size = model.getSize();
 970:  if (size > 0)
 971:  {
 972:  for (int i = 0; i < size; ++i)
 973:  {
 974:  Component comp = renderer.getListCellRendererComponent(listBox, 
 975:  model.getElementAt(i), -1, false, false);
 976:  currentValuePane.add(comp);
 977:  comp.setFont(comboBox.getFont());
 978:  Dimension renderSize = comp.getPreferredSize();
 979:  currentValuePane.remove(comp);
 980:  dim.width = Math.max(dim.width, renderSize.width);
 981:  dim.height = Math.max(dim.height, renderSize.height);
 982:  }
 983:  }
 984:  else
 985:  {
 986:  dim = getDefaultSize();
 987:  if (comboBox.isEditable())
 988:  dim.width = 100;
 989:  }
 990:  }
 991:  if (comboBox.isEditable())
 992:  {
 993:  Dimension editSize = editor.getPreferredSize();
 994:  dim.width = Math.max(dim.width, editSize.width);
 995:  dim.height = Math.max(dim.height, editSize.height);
 996:  }
 997:  displaySize.setSize(dim.width, dim.height);
 998:  return dim;
 999:  }
1000: 
1001:  /**
1002:  * Installs the keyboard actions for the {@link JComboBox} as specified
1003:  * by the look and feel.
1004:  */
1005:  protected void installKeyboardActions()
1006:  {
1007:  SwingUtilities.replaceUIInputMap(comboBox,
1008:  JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1009:  (InputMap) UIManager.get("ComboBox.ancestorInputMap"));
1010:  // Install any action maps here.
1011:  }
1012:  
1013:  /**
1014:  * Uninstalls the keyboard actions for the {@link JComboBox} there were
1015:  * installed by in {@link #installListeners}.
1016:  */
1017:  protected void uninstallKeyboardActions()
1018:  {
1019:  SwingUtilities.replaceUIInputMap(comboBox,
1020:  JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1021:  // Uninstall any action maps here.
1022:  }
1023: 
1024:  /**
1025:  * A {@link LayoutManager} used to position the sub-components of the
1026:  * {@link JComboBox}.
1027:  * 
1028:  * @see BasicComboBoxUI#createLayoutManager()
1029:  */
1030:  public class ComboBoxLayoutManager implements LayoutManager
1031:  {
1032:  /**
1033:  * Creates a new ComboBoxLayoutManager object.
1034:  */
1035:  public ComboBoxLayoutManager()
1036:  {
1037:  // Nothing to do here.
1038:  }
1039: 
1040:  /**
1041:  * Adds a component to the layout. This method does nothing, since the
1042:  * layout manager doesn't need to track the components.
1043:  * 
1044:  * @param name the name to associate the component with (ignored).
1045:  * @param comp the component (ignored).
1046:  */
1047:  public void addLayoutComponent(String name, Component comp)
1048:  {
1049:  // Do nothing
1050:  }
1051: 
1052:  /**
1053:  * Removes a component from the layout. This method does nothing, since
1054:  * the layout manager doesn't need to track the components.
1055:  * 
1056:  * @param comp the component.
1057:  */
1058:  public void removeLayoutComponent(Component comp)
1059:  {
1060:  // Do nothing
1061:  }
1062: 
1063:  /**
1064:  * Returns preferred layout size of the JComboBox.
1065:  *
1066:  * @param parent the Container for which the preferred size should be 
1067:  * calculated.
1068:  *
1069:  * @return The preferred size for the given container
1070:  */
1071:  public Dimension preferredLayoutSize(Container parent)
1072:  {
1073:  return parent.getPreferredSize();
1074:  }
1075: 
1076:  /**
1077:  * Returns the minimum layout size.
1078:  * 
1079:  * @param parent the container.
1080:  * 
1081:  * @return The minimum size.
1082:  */
1083:  public Dimension minimumLayoutSize(Container parent)
1084:  {
1085:  return parent.getMinimumSize();
1086:  }
1087: 
1088:  /**
1089:  * Arranges the components in the container. It puts arrow
1090:  * button right end part of the comboBox. If the comboBox is editable
1091:  * then editor is placed to the left of arrow button, starting from the
1092:  * beginning.
1093:  *
1094:  * @param parent Container that should be layed out.
1095:  */
1096:  public void layoutContainer(Container parent)
1097:  {
1098:  // Position editor component to the left of arrow button if combo box is 
1099:  // editable
1100:  Insets i = getInsets();
1101:  int arrowSize = comboBox.getHeight() - (i.top + i.bottom);
1102: 
1103:  if (arrowButton != null)
1104:  arrowButton.setBounds(comboBox.getWidth() - (i.right + arrowSize),
1105:  i.top, arrowSize, arrowSize);
1106:  if (editor != null)
1107:  editor.setBounds(rectangleForCurrentValue());
1108:  }
1109:  }
1110: 
1111:  /**
1112:  * Handles focus changes occuring in the combo box. This class is
1113:  * responsible for repainting combo box whenever focus is gained or lost
1114:  * and also for hiding popup list of items whenever combo box loses its
1115:  * focus.
1116:  */
1117:  public class FocusHandler extends Object implements FocusListener
1118:  {
1119:  /**
1120:  * Creates a new FocusHandler object.
1121:  */
1122:  public FocusHandler()
1123:  {
1124:  // Nothing to do here.
1125:  }
1126: 
1127:  /**
1128:  * Invoked when combo box gains focus. It repaints main
1129:  * part of combo box accordingly.
1130:  *
1131:  * @param e the FocusEvent
1132:  */
1133:  public void focusGained(FocusEvent e)
1134:  {
1135:  hasFocus = true;
1136:  comboBox.repaint();
1137:  }
1138: 
1139:  /**
1140:  * Invoked when the combo box loses focus. It repaints the main part
1141:  * of the combo box accordingly and hides the popup list of items.
1142:  *
1143:  * @param e the FocusEvent
1144:  */
1145:  public void focusLost(FocusEvent e)
1146:  {
1147:  hasFocus = false;
1148:  if (! e.isTemporary() && comboBox.isLightWeightPopupEnabled())
1149:  setPopupVisible(comboBox, false);
1150:  comboBox.repaint();
1151:  }
1152:  }
1153: 
1154:  /**
1155:  * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its 
1156:  * selected item changes.
1157:  */
1158:  public class ItemHandler extends Object implements ItemListener
1159:  {
1160:  /**
1161:  * Creates a new ItemHandler object.
1162:  */
1163:  public ItemHandler()
1164:  {
1165:  // Nothing to do here.
1166:  }
1167: 
1168:  /**
1169:  * Invoked when selected item becomes deselected or when
1170:  * new item becomes selected.
1171:  *
1172:  * @param e the ItemEvent representing item's state change.
1173:  */
1174:  public void itemStateChanged(ItemEvent e)
1175:  {
1176:  ComboBoxModel model = comboBox.getModel();
1177:  Object v = model.getSelectedItem();
1178:  if (editor != null)
1179:  comboBox.configureEditor(comboBox.getEditor(), v);
1180:  comboBox.repaint();
1181:  }
1182:  }
1183: 
1184:  /**
1185:  * KeyHandler handles key events occuring while JComboBox has focus.
1186:  */
1187:  public class KeyHandler extends KeyAdapter
1188:  {
1189:  public KeyHandler()
1190:  {
1191:  // Nothing to do here.
1192:  }
1193: 
1194:  /**
1195:  * Invoked whenever key is pressed while JComboBox is in focus.
1196:  */
1197:  public void keyPressed(KeyEvent e)
1198:  {
1199:  if (comboBox.getModel().getSize() != 0 && comboBox.isEnabled())
1200:  {
1201:  if (! isNavigationKey(e.getKeyCode()))
1202:  {
1203:  if (! comboBox.isEditable())
1204:  if (comboBox.selectWithKeyChar(e.getKeyChar()))
1205:  e.consume();
1206:  }
1207:  else
1208:  {
1209:  if (e.getKeyCode() == KeyEvent.VK_UP && comboBox.isPopupVisible())
1210:  selectPreviousPossibleValue();
1211:  else if (e.getKeyCode() == KeyEvent.VK_DOWN)
1212:  {
1213:  if (comboBox.isPopupVisible())
1214:  selectNextPossibleValue();
1215:  else
1216:  comboBox.showPopup();
1217:  }
1218:  else if (e.getKeyCode() == KeyEvent.VK_ENTER
1219:  || e.getKeyCode() == KeyEvent.VK_ESCAPE)
1220:  popup.hide();
1221:  }
1222:  }
1223:  }
1224:  }
1225: 
1226:  /**
1227:  * Handles the changes occurring in the JComboBox's data model.
1228:  */
1229:  public class ListDataHandler extends Object implements ListDataListener
1230:  {
1231:  /**
1232:  * Creates a new ListDataHandler object.
1233:  */
1234:  public ListDataHandler()
1235:  {
1236:  // Nothing to do here.
1237:  }
1238: 
1239:  /**
1240:  * Invoked if the content's of JComboBox's data model are changed.
1241:  *
1242:  * @param e ListDataEvent describing the change.
1243:  */
1244:  public void contentsChanged(ListDataEvent e)
1245:  {
1246:  if (e.getIndex0() != -1 || e.getIndex1() != -1)
1247:  {
1248:  isMinimumSizeDirty = true;
1249:  comboBox.revalidate();
1250:  }
1251:  if (editor != null)
1252:  comboBox.configureEditor(comboBox.getEditor(),
1253:  comboBox.getSelectedItem());
1254:  comboBox.repaint();
1255:  }
1256: 
1257:  /**
1258:  * Invoked when items are added to the JComboBox's data model.
1259:  *
1260:  * @param e ListDataEvent describing the change.
1261:  */
1262:  public void intervalAdded(ListDataEvent e)
1263:  {
1264:  int start = e.getIndex0();
1265:  int end = e.getIndex1();
1266:  if (start == 0 && comboBox.getItemCount() - (end - start + 1) == 0)
1267:  contentsChanged(e);
1268:  else if (start != -1 || end != -1)
1269:  {
1270:  ListCellRenderer renderer = comboBox.getRenderer();
1271:  ComboBoxModel model = comboBox.getModel();
1272:  int w = displaySize.width;
1273:  int h = displaySize.height;
1274:  // TODO: Optimize using prototype here.
1275:  for (int i = start; i <= end; ++i)
1276:  {
1277:  Component comp = renderer.getListCellRendererComponent(listBox,
1278:  model.getElementAt(i), -1, false, false);
1279:  currentValuePane.add(comp);
1280:  comp.setFont(comboBox.getFont());
1281:  Dimension dim = comp.getPreferredSize();
1282:  w = Math.max(w, dim.width);
1283:  h = Math.max(h, dim.height);
1284:  currentValuePane.remove(comp);
1285:  }
1286:  if (displaySize.width < w || displaySize.height < h)
1287:  {
1288:  if (displaySize.width < w)
1289:  displaySize.width = w;
1290:  if (displaySize.height < h)
1291:  displaySize.height = h;
1292:  comboBox.revalidate();
1293:  if (editor != null)
1294:  {
1295:  comboBox.configureEditor(comboBox.getEditor(),
1296:  comboBox.getSelectedItem());
1297:  }
1298:  }
1299:  }
1300:  
1301:  }
1302: 
1303:  /**
1304:  * Invoked when items are removed from the JComboBox's
1305:  * data model.
1306:  *
1307:  * @param e ListDataEvent describing the change.
1308:  */
1309:  public void intervalRemoved(ListDataEvent e)
1310:  {
1311:  contentsChanged(e);
1312:  }
1313:  }
1314: 
1315:  /**
1316:  * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}.
1317:  */
1318:  public class PropertyChangeHandler extends Object
1319:  implements PropertyChangeListener
1320:  {
1321:  /**
1322:  * Creates a new instance.
1323:  */
1324:  public PropertyChangeHandler()
1325:  {
1326:  // Nothing to do here.
1327:  }
1328: 
1329:  /**
1330:  * Invoked whenever bound property of JComboBox changes.
1331:  * 
1332:  * @param e the event.
1333:  */
1334:  public void propertyChange(PropertyChangeEvent e)
1335:  {
1336:  // Lets assume every change invalidates the minimumsize.
1337:  String propName = e.getPropertyName();
1338:  if (propName.equals("enabled"))
1339:  {
1340:  boolean enabled = comboBox.isEnabled();
1341:  if (editor != null)
1342:  editor.setEnabled(enabled);
1343:  if (arrowButton != null)
1344:  arrowButton.setEnabled(enabled);
1345: 
1346:  comboBox.repaint();
1347:  }
1348:  else if (propName.equals("editor") && comboBox.isEditable())
1349:  {
1350:  addEditor();
1351:  comboBox.revalidate();
1352:  }
1353:  else if (e.getPropertyName().equals("editable"))
1354:  {
1355:  if (comboBox.isEditable())
1356:  {
1357:  addEditor();
1358:  }
1359:  else
1360:  {
1361:  removeEditor();
1362:  }
1363: 
1364:  comboBox.revalidate();
1365:  }
1366:  else if (propName.equals("model"))
1367:  {
1368:  // remove ListDataListener from old model and add it to new model
1369:  ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
1370:  if (oldModel != null && listDataListener != null)
1371:  oldModel.removeListDataListener(listDataListener);
1372: 
1373:  ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
1374:  if (newModel != null && listDataListener != null)
1375:  comboBox.getModel().addListDataListener(listDataListener);
1376: 
1377:  if (editor != null)
1378:  {
1379:  comboBox.configureEditor(comboBox.getEditor(),
1380:  comboBox.getSelectedItem());
1381:  }
1382:  isMinimumSizeDirty = true;
1383:  comboBox.revalidate();
1384:  comboBox.repaint();
1385:  }
1386:  else if (propName.equals("font"))
1387:  {
1388:  Font font = (Font) e.getNewValue();
1389:  if (editor != null)
1390:  {
1391:  editor.setFont(font);
1392:  }
1393:  listBox.setFont(font);
1394:  isMinimumSizeDirty = true;
1395:  comboBox.revalidate();
1396:  }
1397:  else if (propName.equals("prototypeDisplayValue"))
1398:  {
1399:  isMinimumSizeDirty = true;
1400:  comboBox.revalidate();
1401:  }
1402:  else if (propName.equals("renderer"))
1403:  {
1404:  isMinimumSizeDirty = true;
1405:  comboBox.revalidate();
1406:  }
1407:  // FIXME: Need to handle changes in other bound properties. 
1408:  }
1409:  }
1410: }
Overview Package Class Use Source Tree Index Deprecated About
GNU Classpath (0.95)

AltStyle によって変換されたページ (->オリジナル) /