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