1 /*
   2  * Copyright (c) 1997, 2018, 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 package javax.swing;
  26 
  27 import java.awt.Component;
  28 import java.awt.Graphics;
  29 import java.awt.Insets;
  30 import java.awt.Toolkit;
  31 import java.awt.event.*;
  32 import java.beans.JavaBean;
  33 import java.beans.BeanProperty;
  34 import java.beans.Transient;
  35 import java.util.Vector;
  36 
  37 import java.io.Serializable;
  38 import java.io.ObjectOutputStream;
  39 import java.io.ObjectInputStream;
  40 import java.io.IOException;
  41 
  42 import javax.swing.plaf.*;
  43 import javax.accessibility.*;
  44 
  45 import sun.awt.SunToolkit;
  46 
  47 /**
  48  * An implementation of a menu bar. You add <code>JMenu</code> objects to the
  49  * menu bar to construct a menu. When the user selects a <code>JMenu</code>
  50  * object, its associated <code>JPopupMenu</code> is displayed, allowing the
  51  * user to select one of the <code>JMenuItems</code> on it.
  52  * <p>
  53  * For information and examples of using menu bars see
  54  * <a
  55  href="https://docs.oracle.com/javase/tutorial/uiswing/components/menu.html">How to Use Menus</a>,
  56  * a section in <em>The Java Tutorial.</em>
  57  * <p>
  58  * <strong>Warning:</strong> Swing is not thread safe. For more
  59  * information see <a
  60  * href="package-summary.html#threading">Swing's Threading
  61  * Policy</a>.
  62  * <p>
  63  * <strong>Warning:</strong>
  64  * Serialized objects of this class will not be compatible with
  65  * future Swing releases. The current serialization support is
  66  * appropriate for short term storage or RMI between applications running
  67  * the same version of Swing.  As of 1.4, support for long term storage
  68  * of all JavaBeans
  69  * has been added to the <code>java.beans</code> package.
  70  * Please see {@link java.beans.XMLEncoder}.
  71  * <p>
  72  * <strong>Warning:</strong>
  73  * By default, pressing the Tab key does not transfer focus from a <code>
  74  * JMenuBar</code> which is added to a container together with other Swing
  75  * components, because the <code>focusTraversalKeysEnabled</code> property
  76  * of <code>JMenuBar</code> is set to <code>false</code>. To resolve this,
  77  * you should call the <code>JMenuBar.setFocusTraversalKeysEnabled(true)</code>
  78  * method.
  79  *
  80  * @author Georges Saab
  81  * @author David Karlton
  82  * @author Arnaud Weber
  83  * @see JMenu
  84  * @see JPopupMenu
  85  * @see JMenuItem
  86  * @since 1.2
  87  */
  88 @JavaBean(defaultProperty = "UI", description = "A container for holding and displaying menus.")
  89 @SwingContainer
  90 @SuppressWarnings("serial")
  91 public class JMenuBar extends JComponent implements Accessible,MenuElement
  92 {
  93     /**
  94      * @see #getUIClassID
  95      * @see #readObject
  96      */
  97     private static final String uiClassID = "MenuBarUI";
  98 
  99     /*
 100      * Model for the selected subcontrol.
 101      */
 102     private transient SingleSelectionModel selectionModel;
 103 
 104     private boolean paintBorder           = true;
 105     private Insets     margin             = null;
 106 
 107     /* diagnostic aids -- should be false for production builds. */
 108     private static final boolean TRACE =   false; // trace creates and disposes
 109     private static final boolean VERBOSE = false; // show reuse hits/misses
 110     private static final boolean DEBUG =   false;  // show bad params, misc.
 111 
 112     /**
 113      * Creates a new menu bar.
 114      */
 115     public JMenuBar() {
 116         super();
 117         setFocusTraversalKeysEnabled(false);
 118         setSelectionModel(new DefaultSingleSelectionModel());
 119         updateUI();
 120     }
 121 
 122     /**
 123      * Returns the menubar's current UI.
 124      *
 125      * @return a {@code MenuBarUI} which is the menubar's current L&amp;F object
 126      * @see #setUI
 127      */
 128     public MenuBarUI getUI() {
 129         return (MenuBarUI)ui;
 130     }
 131 
 132     /**
 133      * Sets the L&amp;F object that renders this component.
 134      *
 135      * @param ui the new MenuBarUI L&amp;F object
 136      * @see UIDefaults#getUI
 137      */
 138     @BeanProperty(hidden = true, visualUpdate = true, description
 139             = "The UI object that implements the Component's LookAndFeel.")
 140     public void setUI(MenuBarUI ui) {
 141         super.setUI(ui);
 142     }
 143 
 144     /**
 145      * Resets the UI property with a value from the current look and feel.
 146      *
 147      * @see JComponent#updateUI
 148      */
 149     public void updateUI() {
 150         Toolkit tk = Toolkit.getDefaultToolkit();
 151         if (tk instanceof SunToolkit) {
 152             ((SunToolkit)tk).updateScreenMenuBarUI();
 153         }
 154         setUI((MenuBarUI)UIManager.getUI(this));
 155     }
 156 
 157 
 158     /**
 159      * Returns the name of the L&amp;F class that renders this component.
 160      *
 161      * @return the string "MenuBarUI"
 162      * @see JComponent#getUIClassID
 163      * @see UIDefaults#getUI
 164      */
 165     @BeanProperty(bound = false)
 166     public String getUIClassID() {
 167         return uiClassID;
 168     }
 169 
 170 
 171     /**
 172      * Returns the model object that handles single selections.
 173      *
 174      * @return the <code>SingleSelectionModel</code> property
 175      * @see SingleSelectionModel
 176      */
 177     public SingleSelectionModel getSelectionModel() {
 178         return selectionModel;
 179     }
 180 
 181     /**
 182      * Sets the model object to handle single selections.
 183      *
 184      * @param model the <code>SingleSelectionModel</code> to use
 185      * @see SingleSelectionModel
 186      */
 187     @BeanProperty(description = "The selection model, recording which child is selected.")
 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     @BeanProperty(bound = false)
 228     public int getMenuCount() {
 229         return getComponentCount();
 230     }
 231 
 232     /**
 233      * Sets the help menu that appears when the user selects the
 234      * "help" option in the menu bar. This method is not yet implemented
 235      * and will throw an exception.
 236      *
 237      * @param menu the JMenu that delivers help to the user
 238      */
 239     public void setHelpMenu(JMenu menu) {
 240         throw new Error("setHelpMenu() not yet implemented.");
 241     }
 242 
 243     /**
 244      * Gets the help menu for the menu bar.  This method is not yet
 245      * implemented and will throw an exception.
 246      *
 247      * @return the <code>JMenu</code> that delivers help to the user
 248      */
 249     @Transient
 250     public JMenu getHelpMenu() {
 251         throw new Error("getHelpMenu() not yet implemented.");
 252     }
 253 
 254     /**
 255      * Returns the component at the specified index.
 256      *
 257      * @param i an integer specifying the position, where 0 is first
 258      * @return the <code>Component</code> at the position,
 259      *          or <code>null</code> for an invalid index
 260      * @deprecated replaced by <code>getComponent(int i)</code>
 261      */
 262     @Deprecated
 263     public Component getComponentAtIndex(int i) {
 264         if(i < 0 || i >= getComponentCount()) {
 265             return null;
 266         }
 267         return getComponent(i);
 268     }
 269 
 270     /**
 271      * Returns the index of the specified component.
 272      *
 273      * @param c  the <code>Component</code> to find
 274      * @return an integer giving the component's position, where 0 is first;
 275      *          or -1 if it can't be found
 276      */
 277     public int getComponentIndex(Component c) {
 278         int ncomponents = this.getComponentCount();
 279         Component[] component = this.getComponents();
 280         for (int i = 0 ; i < ncomponents ; i++) {
 281             Component comp = component[i];
 282             if (comp == c)
 283                 return i;
 284         }
 285         return -1;
 286     }
 287 
 288     /**
 289      * Sets the currently selected component, producing a
 290      * a change to the selection model.
 291      *
 292      * @param sel the <code>Component</code> to select
 293      */
 294     public void setSelected(Component sel) {
 295         SingleSelectionModel model = getSelectionModel();
 296         int index = getComponentIndex(sel);
 297         model.setSelectedIndex(index);
 298     }
 299 
 300     /**
 301      * Returns true if the menu bar currently has a component selected.
 302      *
 303      * @return true if a selection has been made, else false
 304      */
 305     @BeanProperty(bound = false)
 306     public boolean isSelected() {
 307         return selectionModel.isSelected();
 308     }
 309 
 310     /**
 311      * Returns true if the menu bars border should be painted.
 312      *
 313      * @return  true if the border should be painted, else false
 314      */
 315     public boolean isBorderPainted() {
 316         return paintBorder;
 317     }
 318 
 319     /**
 320      * Sets whether the border should be painted.
 321      *
 322      * @param b if true and border property is not <code>null</code>,
 323      *          the border is painted.
 324      * @see #isBorderPainted
 325      */
 326     @BeanProperty(visualUpdate = true, description
 327             = "Whether the border should be painted.")
 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      */
 360     @BeanProperty(visualUpdate = true, description
 361             = "The space between the menubar's border and its contents")
 362     public void setMargin(Insets m) {
 363         Insets old = margin;
 364         this.margin = m;
 365         firePropertyChange("margin", old, m);
 366         if (old == null || !old.equals(m)) {
 367             revalidate();
 368             repaint();
 369         }
 370     }
 371 
 372     /**
 373      * Returns the margin between the menubar's border and
 374      * its menus.  If there is no previous margin, it will create
 375      * a default margin with zero size.
 376      *
 377      * @return an <code>Insets</code> object containing the margin values
 378      * @see Insets
 379      */
 380     public Insets getMargin() {
 381         if(margin == null) {
 382             return new Insets(0,0,0,0);
 383         } else {
 384             return margin;
 385         }
 386     }
 387 
 388 
 389     /**
 390      * Implemented to be a <code>MenuElement</code> -- does nothing.
 391      *
 392      * @see #getSubElements
 393      */
 394     public void processMouseEvent(MouseEvent event,MenuElement[] path,MenuSelectionManager manager) {
 395     }
 396 
 397     /**
 398      * Implemented to be a <code>MenuElement</code> -- does nothing.
 399      *
 400      * @see #getSubElements
 401      */
 402     public void processKeyEvent(KeyEvent e,MenuElement[] path,MenuSelectionManager manager) {
 403     }
 404 
 405     /**
 406      * Implemented to be a <code>MenuElement</code> -- does nothing.
 407      *
 408      * @see #getSubElements
 409      */
 410     public void menuSelectionChanged(boolean isIncluded) {
 411     }
 412 
 413     /**
 414      * Implemented to be a <code>MenuElement</code> -- returns the
 415      * menus in this menu bar.
 416      * This is the reason for implementing the <code>MenuElement</code>
 417      * interface -- so that the menu bar can be treated the same as
 418      * other menu elements.
 419      * @return an array of menu items in the menu bar.
 420      */
 421     @BeanProperty(bound = false)
 422     public MenuElement[] getSubElements() {
 423         MenuElement[] result;
 424         Vector<MenuElement> tmp = new Vector<MenuElement>();
 425         int c = getComponentCount();
 426         int i;
 427         Component m;
 428 
 429         for(i=0 ; i < c ; i++) {
 430             m = getComponent(i);
 431             if(m instanceof MenuElement)
 432                 tmp.addElement((MenuElement) m);
 433         }
 434 
 435         result = new MenuElement[tmp.size()];
 436         for(i=0,c=tmp.size() ; i < c ; i++)
 437             result[i] = tmp.elementAt(i);
 438         return result;
 439     }
 440 
 441     /**
 442      * Implemented to be a <code>MenuElement</code>. Returns this object.
 443      *
 444      * @return the current <code>Component</code> (this)
 445      * @see #getSubElements
 446      */
 447     public Component getComponent() {
 448         return this;
 449     }
 450 
 451 
 452     /**
 453      * Returns a string representation of this <code>JMenuBar</code>.
 454      * This method
 455      * is intended to be used only for debugging purposes, and the
 456      * content and format of the returned string may vary between
 457      * implementations. The returned string may be empty but may not
 458      * be <code>null</code>.
 459      *
 460      * @return  a string representation of this <code>JMenuBar</code>
 461      */
 462     protected String paramString() {
 463         String paintBorderString = (paintBorder ?
 464                                     "true" : "false");
 465         String marginString = (margin != null ?
 466                                margin.toString() : "");
 467 
 468         return super.paramString() +
 469         ",margin=" + marginString +
 470         ",paintBorder=" + paintBorderString;
 471     }
 472 
 473 /////////////////
 474 // Accessibility support
 475 ////////////////
 476 
 477     /**
 478      * Gets the AccessibleContext associated with this JMenuBar.
 479      * For JMenuBars, the AccessibleContext takes the form of an
 480      * AccessibleJMenuBar.
 481      * A new AccessibleJMenuBar instance is created if necessary.
 482      *
 483      * @return an AccessibleJMenuBar that serves as the
 484      *         AccessibleContext of this JMenuBar
 485      */
 486     @BeanProperty(bound = false)
 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
 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 }