Source for javax.swing.plaf.basic.BasicComboBoxUI

   1: /* BasicComboBoxUI.java --
   2:    Copyright (C) 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.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.Graphics;
  46: import java.awt.Insets;
  47: import java.awt.LayoutManager;
  48: import java.awt.Rectangle;
  49: import java.awt.event.FocusEvent;
  50: import java.awt.event.FocusListener;
  51: import java.awt.event.ItemEvent;
  52: import java.awt.event.ItemListener;
  53: import java.awt.event.KeyAdapter;
  54: import java.awt.event.KeyEvent;
  55: import java.awt.event.KeyListener;
  56: import java.awt.event.MouseAdapter;
  57: import java.awt.event.MouseEvent;
  58: import java.awt.event.MouseListener;
  59: import java.awt.event.MouseMotionListener;
  60: import java.beans.PropertyChangeEvent;
  61: import java.beans.PropertyChangeListener;
  62: 
  63: import javax.accessibility.Accessible;
  64: import javax.swing.CellRendererPane;
  65: import javax.swing.ComboBoxEditor;
  66: import javax.swing.ComboBoxModel;
  67: import javax.swing.JButton;
  68: import javax.swing.JComboBox;
  69: import javax.swing.JComponent;
  70: import javax.swing.JList;
  71: import javax.swing.ListCellRenderer;
  72: import javax.swing.UIDefaults;
  73: import javax.swing.UIManager;
  74: import javax.swing.event.ListDataEvent;
  75: import javax.swing.event.ListDataListener;
  76: import javax.swing.plaf.ComboBoxUI;
  77: import javax.swing.plaf.ComponentUI;
  78: 
  79: /**
  80:  * UI Delegate for JComboBox
  81:  *
  82:  * @author Olga Rodimina
  83:  * @author Robert Schuster
  84:  */
  85: public class BasicComboBoxUI extends ComboBoxUI
  86: {
  87:   /**
  88:    * This arrow button that is displayed in the rigth side of JComboBox. This
  89:    * button is used to hide and show combo box's list of items
  90:    */
  91:   protected JButton arrowButton;
  92: 
  93:   /**
  94:    * The combo box for which this UI delegate is for
  95:    */
  96:   protected JComboBox comboBox;
  97: 
  98:   /**
  99:    * Component that is responsible for displaying/editting  selected item of
 100:    * the combo box. By default JTextField is used as an editor for the
 101:    * JComboBox
 102:    */
 103:   protected Component editor;
 104: 
 105:   /**
 106:    * Listener listening to focus events occuring in the JComboBox
 107:    */
 108:   protected FocusListener focusListener;
 109: 
 110:   /**
 111:    * tells whether JComboBox currently has focus
 112:    */
 113:   protected boolean hasFocus;
 114: 
 115:   /**
 116:    * Listener listening to item events fired by the JComboBox
 117:    */
 118:   protected ItemListener itemListener;
 119: 
 120:   /**
 121:    * KeyListener listening to key events that occur while JComboBox has focus
 122:    */
 123:   protected KeyListener keyListener;
 124: 
 125:   /**
 126:    * MouseListener listening to mouse events occuring in the combo box
 127:    */
 128:   private MouseListener mouseListener;
 129: 
 130:   /**
 131:    * List used when rendering selected item of the combo box. The selection
 132:    * and foreground colors for combo box renderer  are configured from this
 133:    * list
 134:    */
 135:   protected JList listBox;
 136: 
 137:   /**
 138:    * ListDataListener listening to JComboBox model
 139:    */
 140:   protected ListDataListener listDataListener;
 141: 
 142:   /**
 143:    * Popup list containing combo box's menu items
 144:    */
 145:   protected ComboPopup popup;
 146:   protected KeyListener popupKeyListener;
 147:   protected MouseListener popupMouseListener;
 148:   protected MouseMotionListener popupMouseMotionListener;
 149: 
 150:   /**
 151:    * Listener listening to changes in the bound properties of JComboBox
 152:    */
 153:   protected PropertyChangeListener propertyChangeListener;
 154: 
 155:   /**
 156:    * Colors that are used to render selected item in the combo box.
 157:    */
 158:   private Color shadow;
 159:   private Color darkShadow;
 160:   private Color highlight;
 161:   private Color lightHighlight;
 162: 
 163:   /* Size of the largest item in the comboBox
 164:    * This is package-private to avoid an accessor method.
 165:    */
 166:   Dimension largestItemSize;
 167: 
 168:   // It seems that JComboBox doesn't have a border set explicitely. So we just
 169:   // paint the border everytime combo box is displayed. 
 170: 
 171:   /* border insets for this JComboBox
 172:    * This is package-private to avoid an accessor method. */
 173:   static final Insets borderInsets = new Insets(2, 2, 2, 2);
 174: 
 175:   // Width of the arrow button  
 176:   // This is package-private to avoid an accessor method.
 177:   // FIXME: has wrong name for a constant.
 178:   static final int arrowButtonWidth = 15;
 179: 
 180:   // FIXME: This fields aren't used anywhere at this moment.
 181:   protected Dimension cachedMinimumSize;
 182:   protected CellRendererPane currentValuePane;
 183:   protected boolean isMinimumSizeDirty;
 184: 
 185:   /**
 186:    * Creates a new BasicComboBoxUI object.
 187:    */
 188:   public BasicComboBoxUI()
 189:   {
 190:   }
 191: 
 192:   /**
 193:    * Factory method to create a BasicComboBoxUI for the given {@link
 194:    * JComponent}, which should be a {@link JComboBox}.
 195:    *
 196:    * @param c The {@link JComponent} a UI is being created for.
 197:    *
 198:    * @return A BasicComboBoxUI for the {@link JComponent}.
 199:    */
 200:   public static ComponentUI createUI(JComponent c)
 201:   {
 202:     return new BasicComboBoxUI();
 203:   }
 204: 
 205:   /**
 206:    * This method installs the UI for the given JComponent.
 207:    *
 208:    * @param c The JComponent to install a UI for.
 209:    */
 210:   public void installUI(JComponent c)
 211:   {
 212:     super.installUI(c);
 213: 
 214:     if (c instanceof JComboBox)
 215:       {
 216:     comboBox = (JComboBox) c;
 217:     comboBox.setOpaque(true);
 218:     comboBox.setLayout(createLayoutManager());
 219:     installDefaults();
 220:     installComponents();
 221:     installListeners();
 222:     installKeyboardActions();
 223:       }
 224:   }
 225: 
 226:   /**
 227:    * This method uninstalls the UI.
 228:    *
 229:    * @param c The JComponent that is having this UI removed.
 230:    */
 231:   public void uninstallUI(JComponent c)
 232:   {
 233:     uninstallKeyboardActions();
 234:     uninstallListeners();
 235:     uninstallComponents();
 236:     uninstallDefaults();
 237:     comboBox = null;
 238:   }
 239: 
 240:   /**
 241:    * This method installs the defaults that are defined in  the Basic look and
 242:    * feel for this {@link JComboBox}.
 243:    */
 244:   protected void installDefaults()
 245:   {
 246:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 247: 
 248:     comboBox.setBackground(defaults.getColor("ComboBox.background"));
 249:     comboBox.setFont(defaults.getFont("ComboBox.font"));
 250:     comboBox.setForeground(defaults.getColor("ComboBox.foreground"));
 251: 
 252:     // Set default color that should be used to to render selected item
 253:     // of the combo box.
 254:     shadow = defaults.getColor("Button.shadow");
 255:     darkShadow = defaults.getColor("Button.darkShadow");
 256:     lightHighlight = defaults.getColor("Button.light");
 257:     highlight = defaults.getColor("Button.highlight");
 258:   }
 259: 
 260:   /**
 261:    * This method creates and installs the listeners for this UI.
 262:    */
 263:   protected void installListeners()
 264:   {
 265:     // install combo box's listeners
 266:     propertyChangeListener = createPropertyChangeListener();
 267:     comboBox.addPropertyChangeListener(propertyChangeListener);
 268: 
 269:     focusListener = createFocusListener();
 270:     comboBox.addFocusListener(focusListener);
 271: 
 272:     itemListener = createItemListener();
 273:     comboBox.addItemListener(itemListener);
 274: 
 275:     keyListener = createKeyListener();
 276:     comboBox.addKeyListener(keyListener);
 277: 
 278:     mouseListener = createMouseListener();
 279:     comboBox.addMouseListener(mouseListener);
 280: 
 281:     // install listeners that listen to combo box model
 282:     listDataListener = createListDataListener();
 283:     comboBox.getModel().addListDataListener(listDataListener);
 284: 
 285:     configureArrowButton();
 286:   }
 287: 
 288:   /**
 289:    * This method uninstalls the defaults and sets any objects created during
 290:    * install to null
 291:    */
 292:   protected void uninstallDefaults()
 293:   {
 294:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 295: 
 296:     comboBox.setBackground(null);
 297:     comboBox.setFont(null);
 298:     comboBox.setForeground(null);
 299: 
 300:     shadow = null;
 301:     darkShadow = null;
 302:     lightHighlight = null;
 303:     highlight = null;
 304:   }
 305: 
 306:   /**
 307:    * Detaches all the listeners we attached in {@link #installListeners}.
 308:    */
 309:   protected void uninstallListeners()
 310:   {
 311:     comboBox.removePropertyChangeListener(propertyChangeListener);
 312:     propertyChangeListener = null;
 313: 
 314:     comboBox.removeFocusListener(focusListener);
 315:     focusListener = null;
 316: 
 317:     comboBox.removeItemListener(itemListener);
 318:     itemListener = null;
 319: 
 320:     comboBox.removeKeyListener(keyListener);
 321:     keyListener = null;
 322: 
 323:     comboBox.removeMouseListener(mouseListener);
 324:     mouseListener = null;
 325: 
 326:     comboBox.getModel().removeListDataListener(listDataListener);
 327:     listDataListener = null;
 328: 
 329:     unconfigureArrowButton();
 330:   }
 331: 
 332:   /**
 333:    * This method creates popup that will contain list of combo box's items
 334:    *
 335:    * @return popup containing list of combo box's items
 336:    */
 337:   protected ComboPopup createPopup()
 338:   {
 339:     return new BasicComboPopup(comboBox);
 340:   }
 341: 
 342:   /**
 343:    * Creates KeyListener to listen to key events.
 344:    *
 345:    * @return KeyListener that listens to key events.
 346:    */
 347:   protected KeyListener createKeyListener()
 348:   {
 349:     return new KeyHandler();
 350:   }
 351: 
 352:   /**
 353:    * This method create MouseListener that will listen to mouse event occuring
 354:    * in combo box.
 355:    *
 356:    * @return the MouseListener
 357:    */
 358:   private MouseListener createMouseListener()
 359:   {
 360:     return new MouseHandler();
 361:   }
 362: 
 363:   /**
 364:    * This method create FocusListener that will listen to changes in this
 365:    * JComboBox's focus.
 366:    *
 367:    * @return theFocusListener
 368:    */
 369:   protected FocusListener createFocusListener()
 370:   {
 371:     return new FocusHandler();
 372:   }
 373: 
 374:   /**
 375:    * This method create ListDataListener to listen to ComboBox's  data model
 376:    *
 377:    * @return ListDataListener
 378:    */
 379:   protected ListDataListener createListDataListener()
 380:   {
 381:     return new ListDataHandler();
 382:   }
 383: 
 384:   /**
 385:    * This method creates ItemListener that will listen to to the changes in
 386:    * the JComboBox's selection.
 387:    *
 388:    * @return the ItemListener
 389:    */
 390:   protected ItemListener createItemListener()
 391:   {
 392:     return new ItemHandler();
 393:   }
 394: 
 395:   /**
 396:    * This method creates PropertyChangeListener to listen to  the changes in
 397:    * the JComboBox's bound properties.
 398:    *
 399:    * @return the PropertyChangeListener
 400:    */
 401:   protected PropertyChangeListener createPropertyChangeListener()
 402:   {
 403:     return new PropertyChangeHandler();
 404:   }
 405: 
 406:   /**
 407:    * This method returns layout manager for the combo box.
 408:    *
 409:    * @return layout manager for the combo box
 410:    */
 411:   protected LayoutManager createLayoutManager()
 412:   {
 413:     return new ComboBoxLayoutManager();
 414:   }
 415: 
 416:   /**
 417:    * This method creates component that will be responsible for rendering the
 418:    * selected component in the combo box.
 419:    *
 420:    * @return render for the combo box
 421:    */
 422:   protected ListCellRenderer createRenderer()
 423:   {
 424:     return new BasicComboBoxRenderer();
 425:   }
 426: 
 427:   /**
 428:    * Creates component that will be responsible for displaying/editting
 429:    * selected item in the combo box. This editor is used only when combo box
 430:    * is editable.
 431:    *
 432:    * @return component that will be responsible for  displaying/editting
 433:    *         selected item in the combo box.
 434:    */
 435:   protected ComboBoxEditor createEditor()
 436:   {
 437:     return new BasicComboBoxEditor();
 438:   }
 439: 
 440:   /**
 441:    * This method installs components for this JComboBox. ArrowButton, main
 442:    * part of combo box (upper part) and  popup list of items are created and
 443:    * configured here.
 444:    */
 445:   protected void installComponents()
 446:   {
 447:     // create and install arrow button
 448:     arrowButton = createArrowButton();
 449: 
 450:     comboBox.add(arrowButton);
 451: 
 452:     // Set list that will be used by BasicComboBoxRender 
 453:     // in order to determine the right colors when rendering
 454:     listBox = new JList();
 455: 
 456:     Color background = arrowButton.getBackground();
 457:     listBox.setBackground(background);
 458:     listBox.setSelectionBackground(background.darker());
 459: 
 460:     Color foreground = arrowButton.getForeground();
 461:     listBox.setForeground(foreground);
 462:     listBox.setSelectionForeground(foreground);
 463: 
 464:     // set editor and renderer for the combo box. Editor is used
 465:     // only if combo box becomes editable, otherwise renderer is used
 466:     // to paint the selected item; combobox is not editable by default. 
 467:     comboBox.setRenderer(createRenderer());
 468: 
 469:     comboBox.setEditor(createEditor());
 470:     editor = comboBox.getEditor().getEditorComponent();
 471: 
 472:     // create drop down list of items
 473:     popup = createPopup();
 474: 
 475:     comboBox.revalidate();
 476:   }
 477: 
 478:   /**
 479:    * This method uninstalls components from this JComboBox
 480:    */
 481:   protected void uninstallComponents()
 482:   {
 483:     // uninstall arrow button
 484:     unconfigureArrowButton();
 485:     comboBox.remove(arrowButton);
 486:     arrowButton = null;
 487: 
 488:     listBox = null;
 489:     popup = null;
 490: 
 491:     comboBox.setRenderer(null);
 492: 
 493:     comboBox.setEditor(null);
 494:     editor = null;
 495:   }
 496: 
 497:   /**
 498:    * This method adds editor to the combo box
 499:    */
 500:   public void addEditor()
 501:   {
 502:     comboBox.add(editor);
 503:   }
 504: 
 505:   /**
 506:    * This method removes editor from the combo box
 507:    */
 508:   public void removeEditor()
 509:   {
 510:     comboBox.remove(editor);
 511:   }
 512: 
 513:   /**
 514:    * This method configures editor for this combo box.
 515:    */
 516:   protected void configureEditor()
 517:   {
 518:     // FIXME: Need to implement. Set font and add listeners.
 519:   }
 520: 
 521:   /**
 522:    * This method removes all the listeners for the editor.
 523:    */
 524:   protected void unconfigureEditor()
 525:   {
 526:     // FIXME: Need to implement    
 527:   }
 528: 
 529:   /**
 530:    * This method adds listeners to the arrow button part of the combo box.
 531:    */
 532:   public void configureArrowButton()
 533:   {
 534:     arrowButton.addMouseListener(mouseListener);
 535:   }
 536: 
 537:   /**
 538:    * This method removes listeners from the arrow button part of the combo
 539:    * box.
 540:    */
 541:   public void unconfigureArrowButton()
 542:   {
 543:     arrowButton.removeMouseListener(mouseListener);
 544:   }
 545: 
 546:   /**
 547:    * This method create arrow button for this JComboBox. Arrow button is
 548:    * responsible for displaying / hiding drop down list of items  when it is
 549:    * clicked.
 550:    *
 551:    * @return JButton arrow button for this JComboBox.
 552:    */
 553:   protected JButton createArrowButton()
 554:   {
 555:     return new BasicArrowButton(BasicArrowButton.SOUTH);
 556:   }
 557: 
 558:   /**
 559:    * This method checks if popup part of the combo box is visible on the
 560:    * screen
 561:    *
 562:    * @param c The JComboBox to check
 563:    *
 564:    * @return true if popup part of the JComboBox is visible and false
 565:    *         otherwise.
 566:    */
 567:   public boolean isPopupVisible(JComboBox c)
 568:   {
 569:     return popup.isVisible();
 570:   }
 571: 
 572:   /**
 573:    * Displays/Hides JComboBox's list of items on the screen.
 574:    *
 575:    * @param c The combo box, for which list of items should be
 576:    *        displayed/hidden
 577:    * @param v true if show popup part of the jcomboBox and false to hide.
 578:    */
 579:   public void setPopupVisible(JComboBox c, boolean v)
 580:   {
 581:     if (v)
 582:       popup.show();
 583:     else
 584:       popup.hide();
 585:   }
 586: 
 587:   /**
 588:    * JComboBox is focus traversable if it is editable and not otherwise.
 589:    *
 590:    * @param c combo box for which to check whether it is focus traversable
 591:    *
 592:    * @return true if focus tranversable and false otherwise
 593:    */
 594:   public boolean isFocusTraversable(JComboBox c)
 595:   {
 596:     if (comboBox.isEditable())
 597:       return true;
 598: 
 599:     return false;
 600:   }
 601: 
 602:   /**
 603:    * Paints given menu item using specified graphics context
 604:    *
 605:    * @param g The graphics context used to paint this combo box
 606:    * @param c comboBox which needs to be painted.
 607:    */
 608:   public void paint(Graphics g, JComponent c)
 609:   {
 610:     if (c instanceof JComboBox)
 611:       {
 612:     JComboBox cb = (JComboBox) c;
 613: 
 614:     paintBorder(g, comboBox.getBounds(), hasFocus);
 615: 
 616:     Rectangle rect = rectangleForCurrentValue();
 617:     paintCurrentValueBackground(g, rect, hasFocus);
 618:     paintCurrentValue(g, rect, hasFocus);
 619:       }
 620:   }
 621: 
 622:   private void paintBorder(Graphics g, Rectangle bounds, boolean hasFocus)
 623:   {
 624:     int x = 0;
 625:     int y = 0;
 626:     int width = bounds.width;
 627:     int height = bounds.height;
 628: 
 629:     Color oldColor = g.getColor();
 630: 
 631:     if (! arrowButton.getModel().isPressed())
 632:       BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height, Color.gray,
 633:                                         Color.white, Color.gray, Color.white);
 634:     else
 635:       {
 636:     g.setColor(darkShadow);
 637:     g.drawRect(x, y, width, height);
 638:     g.setColor(shadow);
 639:     g.drawRect(x + 1, y + 1, width - 3, height - 3);
 640:       }
 641:     g.setColor(oldColor);
 642:   }
 643: 
 644:   /**
 645:    * Returns preferred size for the given menu item.
 646:    *
 647:    * @param c comboBox for which to get preferred size
 648:    *
 649:    * @return $Dimension$ preferred size for the given combo box
 650:    */
 651:   public Dimension getPreferredSize(JComponent c)
 652:   {
 653:     // return null to indicate that combo box's layout will determin its
 654:     // preferred size
 655:     return null;
 656:   }
 657: 
 658:   /**
 659:    * This method returns the minimum size for this {@link JComboBox} for this
 660:    * look and feel.
 661:    *
 662:    * @param c The {@link JComponent} to find the minimum size for.
 663:    *
 664:    * @return The dimensions of the minimum size.
 665:    */
 666:   public Dimension getMinimumSize(JComponent c)
 667:   {
 668:     return null;
 669:   }
 670: 
 671:   /**
 672:    * This method returns the maximum size for this {@link JComboBox} for this
 673:    * look and feel.
 674:    *
 675:    * @param c The {@link JComponent} to find the maximum size for
 676:    *
 677:    * @return The dimensions of the minimum size.
 678:    */
 679:   public Dimension getMaximumSize(JComponent c)
 680:   {
 681:     return null;
 682:   }
 683: 
 684:   public int getAccessibleChildrenCount(JComponent c)
 685:   {
 686:     // FIXME: Need to implement
 687:     return 0;
 688:   }
 689: 
 690:   public Accessible getAccessibleChild(JComponent c, int i)
 691:   {
 692:     // FIXME: Need to implement
 693:     return null;
 694:   }
 695: 
 696:   /**
 697:    * Returns true if the specified key is a navigation key and false otherwise
 698:    *
 699:    * @param keyCode a key for which to check whether it is navigation key or
 700:    *        not.
 701:    *
 702:    * @return true if the specified key is a navigation key and false otherwis
 703:    */
 704:   protected boolean isNavigationKey(int keyCode)
 705:   {
 706:     return false;
 707:   }
 708: 
 709:   /**
 710:    * This method selects next possible item relative to the current selection
 711:    * to be next selected item in the combo box.
 712:    */
 713:   protected void selectNextPossibleValue()
 714:   {
 715:     int index = comboBox.getSelectedIndex();
 716:     if (index != comboBox.getItemCount() - 1)
 717:       comboBox.setSelectedIndex(index + 1);
 718:   }
 719: 
 720:   /**
 721:    * This method selects previous item relative to current selection to be
 722:    * next selected item.
 723:    */
 724:   protected void selectPreviousPossibleValue()
 725:   {
 726:     int index = comboBox.getSelectedIndex();
 727:     if (index != 0)
 728:       comboBox.setSelectedIndex(index - 1);
 729:   }
 730: 
 731:   /**
 732:    * This method displays combo box popup if the popup is not currently shown
 733:    * on the screen and hides it if it is  currently shown
 734:    */
 735:   protected void toggleOpenClose()
 736:   {
 737:     setPopupVisible(comboBox, ! isPopupVisible(comboBox));
 738:   }
 739: 
 740:   /**
 741:    * This method returns bounds in which comboBox's selected Item will be
 742:    * displayed
 743:    *
 744:    * @return rectangle bounds in which comboBox's selected Item will be
 745:    *         displayed
 746:    */
 747:   protected Rectangle rectangleForCurrentValue()
 748:   {
 749:     Rectangle cbBounds = comboBox.getBounds();
 750: 
 751:     // Subtract width or the arrow button and border insets        
 752:     Rectangle rectForCurrentValue = new Rectangle(cbBounds.x
 753:                                                   + borderInsets.left,
 754:                                                   cbBounds.y
 755:                                                   + borderInsets.top,
 756:                                                   cbBounds.width
 757:                                                   - arrowButtonWidth
 758:                                                   - borderInsets.left
 759:                                                   - borderInsets.right,
 760:                                                   cbBounds.height
 761:                                                   - borderInsets.top
 762:                                                   - borderInsets.bottom);
 763: 
 764:     return rectForCurrentValue;
 765:   }
 766: 
 767:   /**
 768:    * This method returns insets of the current border.
 769:    *
 770:    * @return Insets representing space between combo box and its border
 771:    */
 772:   protected Insets getInsets()
 773:   {
 774:     return new Insets(0, 0, 0, 0);
 775:   }
 776: 
 777:   /**
 778:    * This method paints currently selected value in the main part of the combo
 779:    * box (part without popup).
 780:    *
 781:    * @param g graphics context
 782:    * @param bounds Rectangle representing the size of the area in which
 783:    *        selected item should be drawn
 784:    * @param hasFocus true if combo box has focus and false otherwise
 785:    */
 786:   public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus)
 787:   {
 788:     if (! comboBox.isEditable())
 789:       {
 790:     Object currentValue = comboBox.getSelectedItem();
 791:     boolean isPressed = arrowButton.getModel().isPressed();
 792: 
 793:     /* Gets the component to be drawn for the current value.
 794:      * If there is currently no selected item we will take an empty
 795:      * String as replacement.
 796:      */
 797:     Component comp = comboBox.getRenderer()
 798:                                  .getListCellRendererComponent(listBox,
 799:                                                                (currentValue != null ? currentValue : ""),
 800:                                                                -1,
 801:                                                                isPressed,
 802:                                                                hasFocus);
 803:     if (! comboBox.isEnabled())
 804:           comp.setEnabled(false);
 805: 
 806:     g.translate(borderInsets.left, borderInsets.top);
 807:         comp.setBounds(0, 0, bounds.width, bounds.height);
 808:         comp.paint(g);
 809:         g.translate(-borderInsets.left, -borderInsets.top);
 810:         
 811:     comboBox.revalidate();
 812:       }
 813:     else
 814:       comboBox.getEditor().setItem(comboBox.getSelectedItem());
 815:   }
 816: 
 817:   /**
 818:    * This method paints background of part of the combo box, where currently
 819:    * selected value is displayed. If the combo box has focus this method
 820:    * should also paint focus rectangle around the combo box.
 821:    *
 822:    * @param g graphics context
 823:    * @param bounds Rectangle representing the size of the largest item  in the
 824:    *        comboBox
 825:    * @param hasFocus true if combo box has fox and false otherwise
 826:    */
 827:   public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
 828:                                           boolean hasFocus)
 829:   {
 830:     // background is painted by renderer, so it seems that nothing
 831:     // should be done here.
 832:   }
 833: 
 834:   /**
 835:    * Returns default size for the combo box that doesn't contain any elements
 836:    * in it
 837:    *
 838:    * @return Default size of the combo box with no elements in it.
 839:    */
 840:   protected Dimension getDefaultSize()
 841:   {
 842:     return new Dimension(6, 17);
 843:   }
 844: 
 845:   /**
 846:    * Returns size of the largest item in the combo box. This size will be the
 847:    * size of the combo box, not including the arrowButton.
 848:    *
 849:    * @return dimensions of the largest item in the combo box.
 850:    */
 851:   protected Dimension getLargestItemSize()
 852:   {
 853:     ComboBoxModel model = comboBox.getModel();
 854:     int numItems = model.getSize();
 855: 
 856:     // if combo box doesn't have any items then simply
 857:     // return its default size
 858:     if (numItems == 0)
 859:       {
 860:     largestItemSize = getDefaultSize();
 861:     return largestItemSize;
 862:       }
 863: 
 864:     Dimension size = new Dimension(0, 0);
 865: 
 866:     // ComboBox's display size should be equal to the 
 867:     // size of the largest item in the combo box. 
 868:     ListCellRenderer renderer = comboBox.getRenderer();
 869: 
 870:     for (int i = 0; i < numItems; i++)
 871:       {
 872:     Object item = model.getElementAt(i);
 873:     String s = item.toString();
 874:     Component comp = renderer.getListCellRendererComponent(listBox, item,
 875:                                                            -1, false, false);
 876: 
 877:     if (comp.getPreferredSize().getWidth() > size.getWidth())
 878:       size = comp.getPreferredSize();
 879:       }
 880: 
 881:     largestItemSize = size;
 882:     return largestItemSize;
 883:   }
 884: 
 885:   /**
 886:    * This method installs the keyboard actions for the JComboBox as specified
 887:    * by the look and feel.
 888:    */
 889:   protected void installKeyboardActions()
 890:   {
 891:     // FIXME: Need to implement.
 892:   }
 893: 
 894:   /**
 895:    * This method uninstalls the keyboard actions for the JComboBox there were
 896:    * installed by in {@link #installListeners}.
 897:    */
 898:   protected void uninstallKeyboardActions()
 899:   {
 900:     // FIXME: Need to implement.
 901:   }
 902: 
 903:   /**
 904:    * This class is Layout Manager for this combo box.
 905:    */
 906:   public class ComboBoxLayoutManager extends Object implements LayoutManager
 907:   {
 908:     /**
 909:      * Creates a new ComboBoxLayoutManager object.
 910:      */
 911:     public ComboBoxLayoutManager()
 912:     {
 913:     }
 914: 
 915:     public void addLayoutComponent(String name, Component comp)
 916:     {
 917:       // Do nothing
 918:     }
 919: 
 920:     public void removeLayoutComponent(Component comp)
 921:     {
 922:       // Do nothing
 923:     }
 924: 
 925:     /**
 926:      * Returns preferred layout size of the JComboBox.
 927:      *
 928:      * @param parent Container for which preferred size should be calculated
 929:      *
 930:      * @return preferred size for the given container
 931:      */
 932:     public Dimension preferredLayoutSize(Container parent)
 933:     {
 934:       Dimension d = new Dimension(0, 0);
 935: 
 936:       if (largestItemSize == null)
 937:     largestItemSize = getLargestItemSize();
 938: 
 939:       // add size for the area that will display selected item
 940:       d.width += largestItemSize.getWidth();
 941:       d.height += largestItemSize.getHeight();
 942: 
 943:       // add size of the arrow button
 944:       d.width += arrowButtonWidth;
 945: 
 946:       // add width and height of the border
 947:       d.width += borderInsets.left + borderInsets.right;
 948:       d.height += borderInsets.left + borderInsets.right;
 949: 
 950:       // Add combo box's insets     
 951:       Insets insets = parent.getInsets();
 952:       d.width += insets.left + insets.right;
 953:       d.width += insets.left + insets.right;
 954: 
 955:       return d;
 956:     }
 957: 
 958:     public Dimension minimumLayoutSize(Container parent)
 959:     {
 960:       return preferredLayoutSize(parent);
 961:     }
 962: 
 963:     /**
 964:      * This method layouts out the components in the container.  It puts arrow
 965:      * button right end part of the comboBox. If the comboBox is editable
 966:      * then editor is placed to the left of arrow  button, starting from the
 967:      * beginning.
 968:      *
 969:      * @param parent Container that should be layed out.
 970:      */
 971:     public void layoutContainer(Container parent)
 972:     {
 973:       // Position editor component to the left of arrow button if combo box is 
 974:       // editable
 975:       int editorWidth = comboBox.getBounds().width - arrowButtonWidth - 2;
 976: 
 977:       if (comboBox.isEditable())
 978:     editor.setBounds(borderInsets.left, borderInsets.top, editorWidth,
 979:                      comboBox.getBounds().height - borderInsets.left
 980:                      - borderInsets.top);
 981: 
 982:       arrowButton.setBounds(editorWidth, 2, arrowButtonWidth,
 983:                             comboBox.getBounds().height - 4);
 984:       comboBox.revalidate();
 985:     }
 986:   }
 987: 
 988:   /**
 989:    * This class handles focus changes occuring in the combo box. This class is
 990:    * responsible for repainting combo box whenever focus is gained or lost
 991:    * and also for hiding popup list of items whenever combo box loses its
 992:    * focus.
 993:    */
 994:   public class FocusHandler extends Object implements FocusListener
 995:   {
 996:     /**
 997:      * Creates a new FocusHandler object.
 998:      */
 999:     public FocusHandler()
1000:     {
1001:     }
1002: 
1003:     /**
1004:      * This mehtod is invoked when combo box gains focus. It repaints main
1005:      * part of combo box  accordingally.
1006:      *
1007:      * @param e the FocusEvent
1008:      */
1009:     public void focusGained(FocusEvent e)
1010:     {
1011:       hasFocus = true;
1012:       comboBox.repaint();
1013:     }
1014: 
1015:     /**
1016:      * This method is invoked when combo box loses focus It repaint main part
1017:      * of combo box accordingally and  hides popup list of items.
1018:      *
1019:      * @param e the FocusEvent
1020:      */
1021:     public void focusLost(FocusEvent e)
1022:     {
1023:       hasFocus = false;
1024:       comboBox.repaint();
1025:       popup.hide();
1026:     }
1027:   }
1028: 
1029:   /**
1030:    * This class handles ItemEvent fired by the JComboBox when its selected
1031:    * item changes.
1032:    */
1033:   public class ItemHandler extends Object implements ItemListener
1034:   {
1035:     /**
1036:      * Creates a new ItemHandler object.
1037:      */
1038:     public ItemHandler()
1039:     {
1040:     }
1041: 
1042:     /**
1043:      * This method is invoked when selected item becomes deselected or when
1044:      * new item becomes selected.
1045:      *
1046:      * @param e the ItemEvent representing item's state change.
1047:      */
1048:     public void itemStateChanged(ItemEvent e)
1049:     {
1050:       comboBox.repaint();
1051:     }
1052:   }
1053: 
1054:   /**
1055:    * KeyHandler handles key events occuring while JComboBox has focus.
1056:    */
1057:   public class KeyHandler extends KeyAdapter
1058:   {
1059:     public KeyHandler()
1060:     {
1061:     }
1062: 
1063:     /*
1064:      * This method is invoked whenever key is pressed while JComboBox is in
1065:      * focus.
1066:      */
1067:     public void keyPressed(KeyEvent e)
1068:     {
1069:       // FIXME: This method calls JComboBox.selectWithKeyChar if the key that was 
1070:       // pressed is not a navigation key. 
1071:     }
1072:   }
1073: 
1074:   /**
1075:    * This class handles to the changes occuring in the JComboBox's data model
1076:    */
1077:   public class ListDataHandler extends Object implements ListDataListener
1078:   {
1079:     /**
1080:      * Creates a new ListDataHandler object.
1081:      */
1082:     public ListDataHandler()
1083:     {
1084:     }
1085: 
1086:     /**
1087:      * This method is invoked content's of JComboBox's data model  are changed
1088:      *
1089:      * @param e ListDataEvent describing the change.
1090:      */
1091:     public void contentsChanged(ListDataEvent e)
1092:     {
1093:       // if the item is selected or deselected
1094:     }
1095: 
1096:     /**
1097:      * This method is invoked when items were added to the JComboBox's data
1098:      * model.
1099:      *
1100:      * @param e ListDataEvent describing the change.
1101:      */
1102:     public void intervalAdded(ListDataEvent e)
1103:     {
1104:       // must determine if the size of the combo box should change
1105:       int start = e.getIndex0();
1106:       int end = e.getIndex1();
1107: 
1108:       ComboBoxModel model = comboBox.getModel();
1109:       ListCellRenderer renderer = comboBox.getRenderer();
1110: 
1111:       if (largestItemSize == null)
1112:     largestItemSize = new Dimension(0, 0);
1113: 
1114:       for (int i = start; i < end; i++)
1115:         {
1116:       Object item = model.getElementAt(i);
1117:       Component comp = renderer.getListCellRendererComponent(new JList(),
1118:                                                              item, -1,
1119:                                                              false, false);
1120:       if (comp.getPreferredSize().getWidth() > largestItemSize.getWidth())
1121:         largestItemSize = comp.getPreferredSize();
1122:         }
1123:     }
1124: 
1125:     /**
1126:      * This method is invoked when items were removed from the JComboBox's
1127:      * data model.
1128:      *
1129:      * @param e ListDataEvent describing the change.
1130:      */
1131:     public void intervalRemoved(ListDataEvent e)
1132:     {
1133:       // recalculate display size of the JComboBox.
1134:       largestItemSize = getLargestItemSize();
1135:       comboBox.repaint();
1136:     }
1137:   }
1138: 
1139:   /**
1140:    * This class handles PropertyChangeEvents fired by JComboBox.
1141:    */
1142:   public class PropertyChangeHandler extends Object
1143:     implements PropertyChangeListener
1144:   {
1145:     public PropertyChangeHandler()
1146:     {
1147:     }
1148: 
1149:     /**
1150:      * This method is invoked whenever bound property of JComboBox changes.
1151:      */
1152:     public void propertyChange(PropertyChangeEvent e)
1153:     {
1154:       if (e.getPropertyName().equals("enabled"))
1155:         {
1156:       arrowButton.setEnabled(comboBox.isEnabled());
1157: 
1158:       if (comboBox.isEditable())
1159:         comboBox.getEditor().getEditorComponent().setEnabled(comboBox
1160:                                                              .isEnabled());
1161:         }
1162:       else if (e.getPropertyName().equals("editable"))
1163:         {
1164:       if (comboBox.isEditable())
1165:         {
1166:           configureEditor();
1167:           addEditor();
1168:         }
1169:       else
1170:         {
1171:           unconfigureEditor();
1172:           removeEditor();
1173:         }
1174: 
1175:       comboBox.revalidate();
1176:       comboBox.repaint();
1177:         }
1178:       else if (e.getPropertyName().equals("dataModel"))
1179:         {
1180:       // remove ListDataListener from old model and add it to new model
1181:       ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
1182:       if (oldModel != null)
1183:         oldModel.removeListDataListener(listDataListener);
1184: 
1185:       if ((ComboBoxModel) e.getNewValue() != null)
1186:         comboBox.getModel().addListDataListener(listDataListener);
1187:         }
1188: 
1189:       // FIXME: Need to handle changes in other bound properties.    
1190:     }
1191:   }
1192: 
1193:   /**
1194:    * MouseHandler listens to mouse events occuring in the combo box. This
1195:    * class is responsible for repainting this JComboBox whenever the mouse is
1196:    * being pressed or released over it.
1197:    */
1198:   private class MouseHandler extends MouseAdapter
1199:   {
1200:     /**
1201:      * This method is invoked when mouse is pressed over the combo box. It
1202:      * repaints the combo box accordinglly
1203:      *
1204:      * @param e the MouseEvent
1205:      */
1206:     public void mousePressed(MouseEvent e)
1207:     {
1208:       if (comboBox.isEnabled())
1209:         {
1210:       if (e.getSource() instanceof JComboBox)
1211:         {
1212:           arrowButton.getModel().setPressed(true);
1213:           arrowButton.getModel().setArmed(true);
1214:         }
1215: 
1216:       comboBox.repaint();
1217: 
1218:       if (e.getSource() instanceof BasicArrowButton)
1219:         toggleOpenClose();
1220:         }
1221:     }
1222: 
1223:     /**
1224:      * This method is invoked when mouse is released over the combo box. It
1225:      * repaints the combo box accordinglly
1226:      *
1227:      * @param e the MouseEvent
1228:      */
1229:     public void mouseReleased(MouseEvent e)
1230:     {
1231:       if (comboBox.isEnabled())
1232:         {
1233:       if (e.getSource() instanceof JComboBox)
1234:         {
1235:           arrowButton.getModel().setPressed(false);
1236:           arrowButton.getModel().setArmed(false);
1237:         }
1238: 
1239:       comboBox.repaint();
1240:         }
1241:     }
1242:   }
1243: }