1 /*
   2  * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing;
  27 
  28 import java.awt.Component;
  29 import java.awt.Dimension;
  30 import java.awt.Graphics;
  31 import java.awt.Insets;
  32 import java.awt.Point;
  33 import java.awt.Rectangle;
  34 import java.awt.event.*;
  35 import java.beans.Transient;
  36 import java.util.Vector;
  37 import java.util.Enumeration;
  38 
  39 import java.io.Serializable;
  40 import java.io.ObjectOutputStream;
  41 import java.io.ObjectInputStream;
  42 import java.io.IOException;
  43 
  44 import javax.swing.event.*;
  45 import javax.swing.border.Border;
  46 import javax.swing.plaf.*;
  47 import javax.accessibility.*;
  48 
  49 /**
  50  * An implementation of a menu bar. You add <code>JMenu</code> objects to the
  51  * menu bar to construct a menu. When the user selects a <code>JMenu</code>
  52  * object, its associated <code>JPopupMenu</code> is displayed, allowing the
  53  * user to select one of the <code>JMenuItems</code> on it.
  54  * <p>
  55  * For information and examples of using menu bars see
  56  * <a
  57  href="http://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
  58  * a section in <em>The Java Tutorial.</em>
  59  * <p>
  60  * <strong>Warning:</strong> Swing is not thread safe. For more
  61  * information see <a
  62  * href="package-summary.html#threading">Swing's Threading
  63  * Policy</a>.
  64  * <p>
  65  * <strong>Warning:</strong>
  66  * Serialized objects of this class will not be compatible with
  67  * future Swing releases. The current serialization support is
  68  * appropriate for short term storage or RMI between applications running
  69  * the same version of Swing.  As of 1.4, support for long term storage
  70  * of all JavaBeans<sup><font size="-2">TM</font></sup>
  71  * has been added to the <code>java.beans</code> package.
  72  * Please see {@link java.beans.XMLEncoder}.
  73  * <p>
  74  * <strong>Warning:</strong>
  75  * By default, pressing the Tab key does not transfer focus from a <code>
  76  * JMenuBar</code> which is added to a container together with other Swing
  77  * components, because the <code>focusTraversalKeysEnabled</code> property
  78  * of <code>JMenuBar</code> is set to <code>false</code>. To resolve this,
  79  * you should call the <code>JMenuBar.setFocusTraversalKeysEnabled(true)</code>
  80  * method.
  81  * @beaninfo
  82  *   attribute: isContainer true
  83  * description: A container for holding and displaying menus.
  84  *
  85  * @author Georges Saab
  86  * @author David Karlton
  87  * @author Arnaud Weber
  88  * @see JMenu
  89  * @see JPopupMenu
  90  * @see JMenuItem
  91  */
  92 @SuppressWarnings("serial")
  93 public class JMenuBar extends JComponent implements Accessible,MenuElement
  94 {
  95     /**
  96      * @see #getUIClassID
  97      * @see #readObject
  98      */
  99     private static final String uiClassID = "MenuBarUI";
 100 
 101     /*
 102      * Model for the selected subcontrol.
 103      */
 104     private transient SingleSelectionModel selectionModel;
 105 
 106     private boolean paintBorder           = true;
 107     private Insets     margin             = null;
 108 
 109     /* diagnostic aids -- should be false for production builds. */
 110     private static final boolean TRACE =   false; // trace creates and disposes
 111     private static final boolean VERBOSE = false; // show reuse hits/misses
 112     private static final boolean DEBUG =   false;  // show bad params, misc.
 113 
 114     /**
 115      * Creates a new menu bar.
 116      */
 117     public JMenuBar() {
 118         super();
 119         setFocusTraversalKeysEnabled(false);
 120         setSelectionModel(new DefaultSingleSelectionModel());
 121         updateUI();
 122     }
 123 
 124     /**
 125      * Returns the menubar's current UI.
 126      * @see #setUI
 127      */
 128     public MenuBarUI getUI() {
 129         return (MenuBarUI)ui;
 130     }
 131 
 132     /**
 133      * Sets the L&F object that renders this component.
 134      *
 135      * @param ui the new MenuBarUI L&F object
 136      * @see UIDefaults#getUI
 137      * @beaninfo
 138      *        bound: true
 139      *       hidden: true
 140      *    attribute: visualUpdate true
 141      *  description: The UI object that implements the Component's LookAndFeel.
 142      */
 143     public void setUI(MenuBarUI ui) {
 144         super.setUI(ui);
 145     }
 146 
 147     /**
 148      * Resets the UI property with a value from the current look and feel.
 149      *
 150      * @see JComponent#updateUI
 151      */
 152     public void updateUI() {
 153         setUI((MenuBarUI)UIManager.getUI(this));
 154     }
 155 
 156 
 157     /**
 158      * Returns the name of the L&F class that renders this component.
 159      *
 160      * @return the string "MenuBarUI"
 161      * @see JComponent#getUIClassID
 162      * @see UIDefaults#getUI
 163      */
 164     public String getUIClassID() {
 165         return uiClassID;
 166     }
 167 
 168 
 169     /**
 170      * Returns the model object that handles single selections.
 171      *
 172      * @return the <code>SingleSelectionModel</code> property
 173      * @see SingleSelectionModel
 174      */
 175     public SingleSelectionModel getSelectionModel() {
 176         return selectionModel;
 177     }
 178 
 179     /**
 180      * Sets the model object to handle single selections.
 181      *
 182      * @param model the <code>SingleSelectionModel</code> to use
 183      * @see SingleSelectionModel
 184      * @beaninfo
 185      *       bound: true
 186      * description: The selection model, recording which child is selected.
 187      */
 188     public void setSelectionModel(SingleSelectionModel model) {
 189         SingleSelectionModel oldValue = selectionModel;
 190         this.selectionModel = model;
 191         firePropertyChange("selectionModel", oldValue, selectionModel);
 192     }
 193 
 194 
 195     /**
 196      * Appends the specified menu to the end of the menu bar.
 197      *
 198      * @param c the <code>JMenu</code> component to add
 199      * @return the menu component
 200      */
 201     public JMenu add(JMenu c) {
 202         super.add(c);
 203         return c;
 204     }
 205 
 206     /**
 207      * Returns the menu at the specified position in the menu bar.
 208      *
 209      * @param index  an integer giving the position in the menu bar, where
 210      *               0 is the first position
 211      * @return the <code>JMenu</code> at that position, or <code>null</code> if
 212      *          if there is no <code>JMenu</code> at that position (ie. if
 213      *          it is a <code>JMenuItem</code>)
 214      */
 215     public JMenu getMenu(int index) {
 216         Component c = getComponentAtIndex(index);
 217         if (c instanceof JMenu)
 218             return (JMenu) c;
 219         return null;
 220     }
 221 
 222     /**
 223      * Returns the number of items in the menu bar.
 224      *
 225      * @return the number of items in the menu bar
 226      */
 227     public int getMenuCount() {
 228         return getComponentCount();
 229     }
 230 
 231     /**
 232      * Sets the help menu that appears when the user selects the
 233      * "help" option in the menu bar. This method is not yet implemented
 234      * and will throw an exception.
 235      *
 236      * @param menu the JMenu that delivers help to the user
 237      */
 238     public void setHelpMenu(JMenu menu) {
 239         throw new Error("setHelpMenu() not yet implemented.");
 240     }
 241 
 242     /**
 243      * Gets the help menu for the menu bar.  This method is not yet
 244      * implemented and will throw an exception.
 245      *
 246      * @return the <code>JMenu</code> that delivers help to the user
 247      */
 248     @Transient
 249     public JMenu getHelpMenu() {
 250         throw new Error("getHelpMenu() not yet implemented.");
 251     }
 252 
 253     /**
 254      * Returns the component at the specified index.
 255      *
 256      * @param i an integer specifying the position, where 0 is first
 257      * @return the <code>Component</code> at the position,
 258      *          or <code>null</code> for an invalid index
 259      * @deprecated replaced by <code>getComponent(int i)</code>
 260      */
 261     @Deprecated
 262     public Component getComponentAtIndex(int i) {
 263         if(i < 0 || i >= getComponentCount()) {
 264             return null;
 265         }
 266         return getComponent(i);
 267     }
 268 
 269     /**
 270      * Returns the index of the specified component.
 271      *
 272      * @param c  the <code>Component</code> to find
 273      * @return an integer giving the component's position, where 0 is first;
 274      *          or -1 if it can't be found
 275      */
 276     public int getComponentIndex(Component c) {
 277         int ncomponents = this.getComponentCount();
 278         Component[] component = this.getComponents();
 279         for (int i = 0 ; i < ncomponents ; i++) {
 280             Component comp = component[i];
 281             if (comp == c)
 282                 return i;
 283         }
 284         return -1;
 285     }
 286 
 287     /**
 288      * Sets the currently selected component, producing a
 289      * a change to the selection model.
 290      *
 291      * @param sel the <code>Component</code> to select
 292      */
 293     public void setSelected(Component sel) {
 294         SingleSelectionModel model = getSelectionModel();
 295         int index = getComponentIndex(sel);
 296         model.setSelectedIndex(index);
 297     }
 298 
 299     /**
 300      * Returns true if the menu bar currently has a component selected.
 301      *
 302      * @return true if a selection has been made, else false
 303      */
 304     public boolean isSelected() {
 305         return selectionModel.isSelected();
 306     }
 307 
 308     /**
 309      * Returns true if the menu bars border should be painted.
 310      *
 311      * @return  true if the border should be painted, else false
 312      */
 313     public boolean isBorderPainted() {
 314         return paintBorder;
 315     }
 316 
 317     /**
 318      * Sets whether the border should be painted.
 319      *
 320      * @param b if true and border property is not <code>null</code>,
 321      *          the border is painted.
 322      * @see #isBorderPainted
 323      * @beaninfo
 324      *        bound: true
 325      *    attribute: visualUpdate true
 326      *  description: Whether the border should be painted.
 327      */
 328     public void setBorderPainted(boolean b) {
 329         boolean oldValue = paintBorder;
 330         paintBorder = b;
 331         firePropertyChange("borderPainted", oldValue, paintBorder);
 332         if (b != oldValue) {
 333             revalidate();
 334             repaint();
 335         }
 336     }
 337 
 338     /**
 339      * Paints the menubar's border if <code>BorderPainted</code>
 340      * property is true.
 341      *
 342      * @param g the <code>Graphics</code> context to use for painting
 343      * @see JComponent#paint
 344      * @see JComponent#setBorder
 345      */
 346     protected void paintBorder(Graphics g) {
 347         if (isBorderPainted()) {
 348             super.paintBorder(g);
 349         }
 350     }
 351 
 352     /**
 353      * Sets the margin between the menubar's border and
 354      * its menus. Setting to <code>null</code> will cause the menubar to
 355      * use the default margins.
 356      *
 357      * @param m an Insets object containing the margin values
 358      * @see Insets
 359      * @beaninfo
 360      *        bound: true
 361      *    attribute: visualUpdate true
 362      *  description: The space between the menubar's border and its contents
 363      */
 364     public void setMargin(Insets m) {
 365         Insets old = margin;
 366         this.margin = m;
 367         firePropertyChange("margin", old, m);
 368         if (old == null || !old.equals(m)) {
 369             revalidate();
 370             repaint();
 371         }
 372     }
 373 
 374     /**
 375      * Returns the margin between the menubar's border and
 376      * its menus.  If there is no previous margin, it will create
 377      * a default margin with zero size.
 378      *
 379      * @return an <code>Insets</code> object containing the margin values
 380      * @see Insets
 381      */
 382     public Insets getMargin() {
 383         if(margin == null) {
 384             return new Insets(0,0,0,0);
 385         } else {
 386             return margin;
 387         }
 388     }
 389 
 390 
 391     /**
 392      * Implemented to be a <code>MenuElement</code> -- does nothing.
 393      *
 394      * @see #getSubElements
 395      */
 396     public void processMouseEvent(MouseEvent event,MenuElement path[],MenuSelectionManager manager) {
 397     }
 398 
 399     /**
 400      * Implemented to be a <code>MenuElement</code> -- does nothing.
 401      *
 402      * @see #getSubElements
 403      */
 404     public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
 405     }
 406 
 407     /**
 408      * Implemented to be a <code>MenuElement</code> -- does nothing.
 409      *
 410      * @see #getSubElements
 411      */
 412     public void menuSelectionChanged(boolean isIncluded) {
 413     }
 414 
 415     /**
 416      * Implemented to be a <code>MenuElement</code> -- returns the
 417      * menus in this menu bar.
 418      * This is the reason for implementing the <code>MenuElement</code>
 419      * interface -- so that the menu bar can be treated the same as
 420      * other menu elements.
 421      * @return an array of menu items in the menu bar.
 422      */
 423     public MenuElement[] getSubElements() {
 424         MenuElement result[];
 425         Vector<MenuElement> tmp = new Vector<MenuElement>();
 426         int c = getComponentCount();
 427         int i;
 428         Component m;
 429 
 430         for(i=0 ; i < c ; i++) {
 431             m = getComponent(i);
 432             if(m instanceof MenuElement)
 433                 tmp.addElement((MenuElement) m);
 434         }
 435 
 436         result = new MenuElement[tmp.size()];
 437         for(i=0,c=tmp.size() ; i < c ; i++)
 438             result[i] = tmp.elementAt(i);
 439         return result;
 440     }
 441 
 442     /**
 443      * Implemented to be a <code>MenuElement</code>. Returns this object.
 444      *
 445      * @return the current <code>Component</code> (this)
 446      * @see #getSubElements
 447      */
 448     public Component getComponent() {
 449         return this;
 450     }
 451 
 452 
 453     /**
 454      * Returns a string representation of this <code>JMenuBar</code>.
 455      * This method
 456      * is intended to be used only for debugging purposes, and the
 457      * content and format of the returned string may vary between
 458      * implementations. The returned string may be empty but may not
 459      * be <code>null</code>.
 460      *
 461      * @return  a string representation of this <code>JMenuBar</code>
 462      */
 463     protected String paramString() {
 464         String paintBorderString = (paintBorder ?
 465                                     "true" : "false");
 466         String marginString = (margin != null ?
 467                                margin.toString() : "");
 468 
 469         return super.paramString() +
 470         ",margin=" + marginString +
 471         ",paintBorder=" + paintBorderString;
 472     }
 473 
 474 /////////////////
 475 // Accessibility support
 476 ////////////////
 477 
 478     /**
 479      * Gets the AccessibleContext associated with this JMenuBar.
 480      * For JMenuBars, the AccessibleContext takes the form of an
 481      * AccessibleJMenuBar.
 482      * A new AccessibleJMenuBar instance is created if necessary.
 483      *
 484      * @return an AccessibleJMenuBar that serves as the
 485      *         AccessibleContext of this JMenuBar
 486      */
 487     public AccessibleContext getAccessibleContext() {
 488         if (accessibleContext == null) {
 489             accessibleContext = new AccessibleJMenuBar();
 490         }
 491         return accessibleContext;
 492     }
 493 
 494     /**
 495      * This class implements accessibility support for the
 496      * <code>JMenuBar</code> class.  It provides an implementation of the
 497      * Java Accessibility API appropriate to menu bar user-interface
 498      * elements.
 499      * <p>
 500      * <strong>Warning:</strong>
 501      * Serialized objects of this class will not be compatible with
 502      * future Swing releases. The current serialization support is
 503      * appropriate for short term storage or RMI between applications running
 504      * the same version of Swing.  As of 1.4, support for long term storage
 505      * of all JavaBeans<sup><font size="-2">TM</font></sup>
 506      * has been added to the <code>java.beans</code> package.
 507      * Please see {@link java.beans.XMLEncoder}.
 508      */
 509     @SuppressWarnings("serial")
 510     protected class AccessibleJMenuBar extends AccessibleJComponent
 511         implements AccessibleSelection {
 512 
 513         /**
 514          * Get the accessible state set of this object.
 515          *
 516          * @return an instance of AccessibleState containing the current state
 517          *         of the object
 518          */
 519         public AccessibleStateSet getAccessibleStateSet() {
 520             AccessibleStateSet states = super.getAccessibleStateSet();
 521             return states;
 522         }
 523 
 524         /**
 525          * Get the role of this object.
 526          *
 527          * @return an instance of AccessibleRole describing the role of the
 528          * object
 529          */
 530         public AccessibleRole getAccessibleRole() {
 531             return AccessibleRole.MENU_BAR;
 532         }
 533 
 534         /**
 535          * Get the AccessibleSelection associated with this object.  In the
 536          * implementation of the Java Accessibility API for this class,
 537          * return this object, which is responsible for implementing the
 538          * AccessibleSelection interface on behalf of itself.
 539          *
 540          * @return this object
 541          */
 542         public AccessibleSelection getAccessibleSelection() {
 543             return this;
 544         }
 545 
 546         /**
 547          * Returns 1 if a menu is currently selected in this menu bar.
 548          *
 549          * @return 1 if a menu is currently selected, else 0
 550          */
 551          public int getAccessibleSelectionCount() {
 552             if (isSelected()) {
 553                 return 1;
 554             } else {
 555                 return 0;
 556             }
 557          }
 558 
 559         /**
 560          * Returns the currently selected menu if one is selected,
 561          * otherwise null.
 562          */
 563          public Accessible getAccessibleSelection(int i) {
 564             if (isSelected()) {
 565                 if (i != 0) {   // single selection model for JMenuBar
 566                     return null;
 567                 }
 568                 int j = getSelectionModel().getSelectedIndex();
 569                 if (getComponentAtIndex(j) instanceof Accessible) {
 570                     return (Accessible) getComponentAtIndex(j);
 571                 }
 572             }
 573             return null;
 574          }
 575 
 576         /**
 577          * Returns true if the current child of this object is selected.
 578          *
 579          * @param i the zero-based index of the child in this Accessible
 580          * object.
 581          * @see AccessibleContext#getAccessibleChild
 582          */
 583         public boolean isAccessibleChildSelected(int i) {
 584             return (i == getSelectionModel().getSelectedIndex());
 585         }
 586 
 587         /**
 588          * Selects the nth menu in the menu bar, forcing it to
 589          * pop up.  If another menu is popped up, this will force
 590          * it to close.  If the nth menu is already selected, this
 591          * method has no effect.
 592          *
 593          * @param i the zero-based index of selectable items
 594          * @see #getAccessibleStateSet
 595          */
 596         public void addAccessibleSelection(int i) {
 597             // first close up any open menu
 598             int j = getSelectionModel().getSelectedIndex();
 599             if (i == j) {
 600                 return;
 601             }
 602             if (j >= 0 && j < getMenuCount()) {
 603                 JMenu menu = getMenu(j);
 604                 if (menu != null) {
 605                     MenuSelectionManager.defaultManager().setSelectedPath(null);
 606 //                  menu.setPopupMenuVisible(false);
 607                 }
 608             }
 609             // now popup the new menu
 610             getSelectionModel().setSelectedIndex(i);
 611             JMenu menu = getMenu(i);
 612             if (menu != null) {
 613                 MenuElement me[] = new MenuElement[3];
 614                 me[0] = JMenuBar.this;
 615                 me[1] = menu;
 616                 me[2] = menu.getPopupMenu();
 617                 MenuSelectionManager.defaultManager().setSelectedPath(me);
 618 //              menu.setPopupMenuVisible(true);
 619             }
 620         }
 621 
 622         /**
 623          * Removes the nth selected item in the object from the object's
 624          * selection.  If the nth item isn't currently selected, this
 625          * method has no effect.  Otherwise, it closes the popup menu.
 626          *
 627          * @param i the zero-based index of selectable items
 628          */
 629         public void removeAccessibleSelection(int i) {
 630             if (i >= 0 && i < getMenuCount()) {
 631                 JMenu menu = getMenu(i);
 632                 if (menu != null) {
 633                     MenuSelectionManager.defaultManager().setSelectedPath(null);
 634 //                  menu.setPopupMenuVisible(false);
 635                 }
 636                 getSelectionModel().setSelectedIndex(-1);
 637             }
 638         }
 639 
 640         /**
 641          * Clears the selection in the object, so that nothing in the
 642          * object is selected.  This will close any open menu.
 643          */
 644         public void clearAccessibleSelection() {
 645             int i = getSelectionModel().getSelectedIndex();
 646             if (i >= 0 && i < getMenuCount()) {
 647                 JMenu menu = getMenu(i);
 648                 if (menu != null) {
 649                     MenuSelectionManager.defaultManager().setSelectedPath(null);
 650 //                  menu.setPopupMenuVisible(false);
 651                 }
 652             }
 653             getSelectionModel().setSelectedIndex(-1);
 654         }
 655 
 656         /**
 657          * Normally causes every selected item in the object to be selected
 658          * if the object supports multiple selections.  This method
 659          * makes no sense in a menu bar, and so does nothing.
 660          */
 661         public void selectAllAccessibleSelection() {
 662         }
 663     } // internal class AccessibleJMenuBar
 664 
 665 
 666     /**
 667      * Subclassed to check all the child menus.
 668      * @since 1.3
 669      */
 670     protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
 671                                         int condition, boolean pressed) {
 672         // See if we have a local binding.
 673         boolean retValue = super.processKeyBinding(ks, e, condition, pressed);
 674         if (!retValue) {
 675             MenuElement[] subElements = getSubElements();
 676             for (MenuElement subElement : subElements) {
 677                 if (processBindingForKeyStrokeRecursive(
 678                         subElement, ks, e, condition, pressed)) {
 679                     return true;
 680                 }
 681             }
 682         }
 683         return retValue;
 684     }
 685 
 686     static boolean processBindingForKeyStrokeRecursive(MenuElement elem,
 687                                                        KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
 688         if (elem == null) {
 689             return false;
 690         }
 691 
 692         Component c = elem.getComponent();
 693 
 694         if ( !(c.isVisible() || (c instanceof JPopupMenu)) || !c.isEnabled() ) {
 695             return false;
 696         }
 697 
 698         if (c != null && c instanceof JComponent &&
 699             ((JComponent)c).processKeyBinding(ks, e, condition, pressed)) {
 700 
 701             return true;
 702         }
 703 
 704         MenuElement[] subElements = elem.getSubElements();
 705         for (MenuElement subElement : subElements) {
 706             if (processBindingForKeyStrokeRecursive(subElement, ks, e, condition, pressed)) {
 707                 return true;
 708                 // We don't, pass along to children JMenu's
 709             }
 710         }
 711         return false;
 712     }
 713 
 714     /**
 715      * Overrides <code>JComponent.addNotify</code> to register this
 716      * menu bar with the current keyboard manager.
 717      */
 718     public void addNotify() {
 719         super.addNotify();
 720         KeyboardManager.getCurrentManager().registerMenuBar(this);
 721     }
 722 
 723     /**
 724      * Overrides <code>JComponent.removeNotify</code> to unregister this
 725      * menu bar with the current keyboard manager.
 726      */
 727     public void removeNotify() {
 728         super.removeNotify();
 729         KeyboardManager.getCurrentManager().unregisterMenuBar(this);
 730     }
 731 
 732 
 733     private void writeObject(ObjectOutputStream s) throws IOException {
 734         s.defaultWriteObject();
 735         if (getUIClassID().equals(uiClassID)) {
 736             byte count = JComponent.getWriteObjCounter(this);
 737             JComponent.setWriteObjCounter(this, --count);
 738             if (count == 0 && ui != null) {
 739                 ui.installUI(this);
 740             }
 741         }
 742 
 743         Object[] kvData = new Object[4];
 744         int n = 0;
 745 
 746         if (selectionModel instanceof Serializable) {
 747             kvData[n++] = "selectionModel";
 748             kvData[n++] = selectionModel;
 749         }
 750 
 751         s.writeObject(kvData);
 752     }
 753 
 754 
 755     /**
 756      * See JComponent.readObject() for information about serialization
 757      * in Swing.
 758      */
 759     private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException
 760     {
 761         s.defaultReadObject();
 762         Object[] kvData = (Object[])(s.readObject());
 763 
 764         for(int i = 0; i < kvData.length; i += 2) {
 765             if (kvData[i] == null) {
 766                 break;
 767             }
 768             else if (kvData[i].equals("selectionModel")) {
 769                 selectionModel = (SingleSelectionModel)kvData[i + 1];
 770             }
 771         }
 772 
 773     }
 774 }