1 /*
   2  * Copyright (c) 2010, 2016, 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 javafx.scene.control;
  27 
  28 import javafx.beans.property.ObjectProperty;
  29 import javafx.beans.property.ObjectPropertyBase;
  30 import javafx.beans.property.SimpleObjectProperty;
  31 import javafx.beans.value.ChangeListener;
  32 import javafx.collections.FXCollections;
  33 import javafx.collections.ListChangeListener;
  34 import javafx.collections.ObservableList;
  35 import javafx.beans.property.ReadOnlyBooleanProperty;
  36 import javafx.beans.property.ReadOnlyBooleanWrapper;
  37 import javafx.event.ActionEvent;
  38 import javafx.event.Event;
  39 import javafx.event.EventHandler;
  40 import javafx.event.EventType;
  41 import javafx.scene.AccessibleAction;
  42 import javafx.scene.AccessibleAttribute;
  43 import javafx.scene.AccessibleRole;
  44 import javafx.util.StringConverter;
  45 import javafx.css.PseudoClass;
  46 
  47 import javafx.scene.control.skin.ChoiceBoxSkin;
  48 
  49 import javafx.beans.DefaultProperty;
  50 
  51 /**
  52  * The ChoiceBox is used for presenting the user with a relatively small set of
  53  * predefined choices from which they may choose. The ChoiceBox, when "showing",
  54  * will display to the user these choices and allow them to pick exactly one
  55  * choice. When not showing, the current choice is displayed.
  56  * <p>
  57  * By default, the ChoiceBox has no item selected unless otherwise specified.
  58  * Although the ChoiceBox will only allow a user to select from the predefined
  59  * list, it is possible for the developer to specify the selected item to be
  60  * something other than what is available in the predefined list. This is
  61  * required for several important use cases.
  62  * <p>
  63  * It means configuration of the ChoiceBox is order independent. You
  64  * may either specify the items and then the selected item, or you may
  65  * specify the selected item and then the items. Either way will function
  66  * correctly.
  67  * <p>
  68  * ChoiceBox item selection is handled by
  69  * {@link javafx.scene.control.SelectionModel SelectionModel}
  70  * As with ListView and ComboBox, it is possible to modify the
  71  * {@link javafx.scene.control.SelectionModel SelectionModel} that is used,
  72  * although this is likely to be rarely changed. ChoiceBox supports only a
  73  * single selection model, hence the default used is a {@link SingleSelectionModel}.
  74  *
  75  * <pre>
  76  * import javafx.scene.control.ChoiceBox;
  77  *
  78  * ChoiceBox cb = new ChoiceBox();
  79  * cb.getItems().addAll("item1", "item2", "item3");
  80  * </pre>
  81  * @since JavaFX 2.0
  82  */
  83 @DefaultProperty("items")
  84 public class ChoiceBox<T> extends Control {
  85 
  86     /***************************************************************************
  87      *                                                                         *
  88      * Static properties and methods                                           *
  89      *                                                                         *
  90      **************************************************************************/
  91 
  92     /**
  93      * Called prior to the ChoiceBox showing its popup after the user
  94      * has clicked or otherwise interacted with the ChoiceBox.
  95      * @since JavaFX 8u60
  96      */
  97     public static final EventType<Event> ON_SHOWING =
  98             new EventType<Event>(Event.ANY, "CHOICE_BOX_ON_SHOWING");
  99 
 100     /**
 101      * Called after the ChoiceBox has shown its popup.
 102      * @since JavaFX 8u60
 103      */
 104     public static final EventType<Event> ON_SHOWN =
 105             new EventType<Event>(Event.ANY, "CHOICE_BOX_ON_SHOWN");
 106 
 107     /**
 108      * Called when the ChoiceBox popup <b>will</b> be hidden.
 109      * @since JavaFX 8u60
 110      */
 111     public static final EventType<Event> ON_HIDING =
 112             new EventType<Event>(Event.ANY, "CHOICE_BOX_ON_HIDING");
 113 
 114     /**
 115      * Called when the ChoiceBox popup has been hidden.
 116      * @since JavaFX 8u60
 117      */
 118     public static final EventType<Event> ON_HIDDEN =
 119             new EventType<Event>(Event.ANY, "CHOICE_BOX_ON_HIDDEN");
 120 
 121 
 122 
 123     /***************************************************************************
 124      *                                                                         *
 125      * Constructors                                                            *
 126      *                                                                         *
 127      **************************************************************************/
 128 
 129     /**
 130      * Create a new ChoiceBox which has an empty list of items.
 131      */
 132     public ChoiceBox() {
 133         this(FXCollections.<T>observableArrayList());
 134     }
 135 
 136     /**
 137      * Create a new ChoiceBox with the given set of items. Since it is observable,
 138      * the content of this list may change over time and the ChoiceBox will
 139      * be updated accordingly.
 140      * @param items
 141      */
 142     public ChoiceBox(ObservableList<T> items) {
 143         getStyleClass().setAll("choice-box");
 144         setAccessibleRole(AccessibleRole.COMBO_BOX);
 145         setItems(items);
 146         setSelectionModel(new ChoiceBoxSelectionModel<T>(this));
 147 
 148         // listen to the value property, if the value is
 149         // set to something that exists in the items list, update the
 150         // selection model to indicate that this is the selected item
 151         valueProperty().addListener((ov, t, t1) -> {
 152             if (getItems() == null) return;
 153             int index = getItems().indexOf(t1);
 154             if (index > -1) {
 155                 getSelectionModel().select(index);
 156             }
 157         });
 158     }
 159 
 160     /***************************************************************************
 161      *                                                                         *
 162      * Properties                                                              *
 163      *                                                                         *
 164      **************************************************************************/
 165 
 166     /**
 167      * The selection model for the ChoiceBox. Only a single choice can be made,
 168      * hence, the ChoiceBox supports only a SingleSelectionModel. Generally, the
 169      * main interaction with the selection model is to explicitly set which item
 170      * in the items list should be selected, or to listen to changes in the
 171      * selection to know which item has been chosen.
 172      */
 173     private ObjectProperty<SingleSelectionModel<T>> selectionModel =
 174             new SimpleObjectProperty<SingleSelectionModel<T>>(this, "selectionModel") {
 175          private SelectionModel<T> oldSM = null;
 176         @Override protected void invalidated() {
 177             if (oldSM != null) {
 178                 oldSM.selectedItemProperty().removeListener(selectedItemListener);
 179             }
 180             SelectionModel<T> sm = get();
 181             oldSM = sm;
 182             if (sm != null) {
 183                 sm.selectedItemProperty().addListener(selectedItemListener);
 184                 if (sm.getSelectedItem() != null && ! valueProperty().isBound()) {
 185                     ChoiceBox.this.setValue(sm.getSelectedItem());
 186                 }
 187             }
 188         }
 189     };
 190 
 191     private ChangeListener<T> selectedItemListener = (ov, t, t1) -> {
 192         if (! valueProperty().isBound()) {
 193             setValue(t1);
 194         }
 195     };
 196 
 197 
 198     public final void setSelectionModel(SingleSelectionModel<T> value) { selectionModel.set(value); }
 199     public final SingleSelectionModel<T> getSelectionModel() { return selectionModel.get(); }
 200     public final ObjectProperty<SingleSelectionModel<T>> selectionModelProperty() { return selectionModel; }
 201 
 202 
 203     /**
 204      * Indicates whether the drop down is displaying the list of choices to the
 205      * user. This is a readonly property which should be manipulated by means of
 206      * the #show and #hide methods.
 207      */
 208     private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper() {
 209         @Override protected void invalidated() {
 210             pseudoClassStateChanged(SHOWING_PSEUDOCLASS_STATE, get());
 211             notifyAccessibleAttributeChanged(AccessibleAttribute.EXPANDED);
 212         }
 213 
 214         @Override
 215         public Object getBean() {
 216             return ChoiceBox.this;
 217         }
 218 
 219         @Override
 220         public String getName() {
 221             return "showing";
 222         }
 223     };
 224     public final boolean isShowing() { return showing.get(); }
 225     public final ReadOnlyBooleanProperty showingProperty() { return showing.getReadOnlyProperty(); }
 226     private void setShowing(boolean value) {
 227         // these events will not fire if the showing property is bound
 228         Event.fireEvent(this, value ? new Event(ComboBoxBase.ON_SHOWING) :
 229                 new Event(ComboBoxBase.ON_HIDING));
 230         showing.set(value);
 231         Event.fireEvent(this, value ? new Event(ComboBoxBase.ON_SHOWN) :
 232                 new Event(ComboBoxBase.ON_HIDDEN));
 233     }
 234 
 235     /**
 236      * The items to display in the choice box. The selected item (as indicated in the
 237      * selection model) must always be one of these items.
 238      */
 239     private ObjectProperty<ObservableList<T>> items = new ObjectPropertyBase<ObservableList<T>>() {
 240         ObservableList<T> old;
 241         @Override protected void invalidated() {
 242             final ObservableList<T> newItems = get();
 243             if (old != newItems) {
 244                 // Add and remove listeners
 245                 if (old != null) old.removeListener(itemsListener);
 246                 if (newItems != null) newItems.addListener(itemsListener);
 247                 // Clear the selection model
 248                 final SingleSelectionModel<T> sm = getSelectionModel();
 249                 if (sm != null) {
 250                     if (newItems != null && newItems.isEmpty()) {
 251                         // RT-29433 - clear selection.
 252                         sm.clearSelection();
 253                     } else if (sm.getSelectedIndex() == -1 && sm.getSelectedItem() != null) {
 254                         int newIndex = getItems().indexOf(sm.getSelectedItem());
 255                         if (newIndex != -1) {
 256                             sm.setSelectedIndex(newIndex);
 257                         }
 258                     } else sm.clearSelection();
 259                 }
 260 //                if (sm != null) sm.setSelectedIndex(-1);
 261                 // Save off the old items
 262                 old = newItems;
 263             }
 264         }
 265 
 266         @Override
 267         public Object getBean() {
 268             return ChoiceBox.this;
 269         }
 270 
 271         @Override
 272         public String getName() {
 273             return "items";
 274         }
 275     };
 276     public final void setItems(ObservableList<T> value) { items.set(value); }
 277     public final ObservableList<T> getItems() { return items.get(); }
 278     public final ObjectProperty<ObservableList<T>> itemsProperty() { return items; }
 279 
 280     private final ListChangeListener<T> itemsListener = c -> {
 281         final SingleSelectionModel<T> sm = getSelectionModel();
 282         if (sm!= null) {
 283             if (getItems() == null || getItems().isEmpty()) {
 284                 sm.clearSelection();
 285             } else {
 286                 int newIndex = getItems().indexOf(sm.getSelectedItem());
 287                 sm.setSelectedIndex(newIndex);
 288             }
 289         }
 290         if (sm != null) {
 291 
 292             // Look for the selected item as having been removed. If it has been,
 293             // then we need to clear the selection in the selection model.
 294             final T selectedItem = sm.getSelectedItem();
 295             while (c.next()) {
 296                 if (selectedItem != null && c.getRemoved().contains(selectedItem)) {
 297                     sm.clearSelection();
 298                     break;
 299                     }
 300             }
 301         }
 302     };
 303 
 304     /**
 305      * Allows a way to specify how to represent objects in the items list. When
 306      * a StringConverter is set, the object toString method is not called and
 307      * instead its toString(object T) is called, passing the objects in the items list.
 308      * This is useful when using domain objects in a ChoiceBox as this property
 309      * allows for customization of the representation. Also, any of the pre-built
 310      * Converters available in the {@link javafx.util.converter} package can be set.
 311      * @since JavaFX 2.1
 312      */
 313     public ObjectProperty<StringConverter<T>> converterProperty() { return converter; }
 314     private ObjectProperty<StringConverter<T>> converter =
 315             new SimpleObjectProperty<StringConverter<T>>(this, "converter", null);
 316     public final void setConverter(StringConverter<T> value) { converterProperty().set(value); }
 317     public final StringConverter<T> getConverter() {return converterProperty().get(); }
 318 
 319     /**
 320      * The value of this ChoiceBox is defined as the selected item in the ChoiceBox
 321      * selection model. The valueProperty is synchronized with the selectedItem.
 322      * This property allows for bi-directional binding of external properties to the
 323      * ChoiceBox and updates the selection model accordingly.
 324      * @since JavaFX 2.1
 325      */
 326     public ObjectProperty<T> valueProperty() { return value; }
 327     private ObjectProperty<T> value = new SimpleObjectProperty<T>(this, "value") {
 328         @Override protected void invalidated() {
 329             super.invalidated();
 330             fireEvent(new ActionEvent());
 331             // Update selection
 332             final SingleSelectionModel<T> sm = getSelectionModel();
 333             if (sm != null) {
 334                 sm.select(super.getValue());
 335             }
 336             notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
 337         }
 338     };
 339     public final void setValue(T value) { valueProperty().set(value); }
 340     public final T getValue() { return valueProperty().get(); }
 341 
 342 
 343     // --- On Action
 344     /**
 345      * The ChoiceBox action, which is invoked whenever the ChoiceBox
 346      * {@link #valueProperty() value} property is changed. This
 347      * may be due to the value property being programmatically changed or when the
 348      * user selects an item in a popup menu.
 349      *
 350      * @since JavaFX 8u60
 351      */
 352     public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { return onAction; }
 353     public final void setOnAction(EventHandler<ActionEvent> value) { onActionProperty().set(value); }
 354     public final EventHandler<ActionEvent> getOnAction() { return onActionProperty().get(); }
 355     private ObjectProperty<EventHandler<ActionEvent>> onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
 356         @Override protected void invalidated() {
 357             setEventHandler(ActionEvent.ACTION, get());
 358         }
 359 
 360         @Override
 361         public Object getBean() {
 362             return ChoiceBox.this;
 363         }
 364 
 365         @Override
 366         public String getName() {
 367             return "onAction";
 368         }
 369     };
 370 
 371 
 372     // --- On Showing
 373     /**
 374      * Called just prior to the {@code ChoiceBox} popup being shown.
 375      * @since JavaFX 8u60
 376      */
 377     public final ObjectProperty<EventHandler<Event>> onShowingProperty() { return onShowing; }
 378     public final void setOnShowing(EventHandler<Event> value) { onShowingProperty().set(value); }
 379     public final EventHandler<Event> getOnShowing() { return onShowingProperty().get(); }
 380     private ObjectProperty<EventHandler<Event>> onShowing = new ObjectPropertyBase<EventHandler<Event>>() {
 381         @Override protected void invalidated() {
 382             setEventHandler(ON_SHOWING, get());
 383         }
 384 
 385         @Override public Object getBean() {
 386             return ChoiceBox.this;
 387         }
 388 
 389         @Override public String getName() {
 390             return "onShowing";
 391         }
 392     };
 393 
 394 
 395     // -- On Shown
 396     /**
 397      * Called just after the {@link ChoiceBox} popup is shown.
 398      * @since JavaFX 8u60
 399      */
 400     public final ObjectProperty<EventHandler<Event>> onShownProperty() { return onShown; }
 401     public final void setOnShown(EventHandler<Event> value) { onShownProperty().set(value); }
 402     public final EventHandler<Event> getOnShown() { return onShownProperty().get(); }
 403     private ObjectProperty<EventHandler<Event>> onShown = new ObjectPropertyBase<EventHandler<Event>>() {
 404         @Override protected void invalidated() {
 405             setEventHandler(ON_SHOWN, get());
 406         }
 407 
 408         @Override public Object getBean() {
 409             return ChoiceBox.this;
 410         }
 411 
 412         @Override public String getName() {
 413             return "onShown";
 414         }
 415     };
 416 
 417 
 418     // --- On Hiding
 419     /**
 420      * Called just prior to the {@link ChoiceBox} popup being hidden.
 421      * @since JavaFX 8u60
 422      */
 423     public final ObjectProperty<EventHandler<Event>> onHidingProperty() { return onHiding; }
 424     public final void setOnHiding(EventHandler<Event> value) { onHidingProperty().set(value); }
 425     public final EventHandler<Event> getOnHiding() { return onHidingProperty().get(); }
 426     private ObjectProperty<EventHandler<Event>> onHiding = new ObjectPropertyBase<EventHandler<Event>>() {
 427         @Override protected void invalidated() {
 428             setEventHandler(ON_HIDING, get());
 429         }
 430 
 431         @Override public Object getBean() {
 432             return ChoiceBox.this;
 433         }
 434 
 435         @Override public String getName() {
 436             return "onHiding";
 437         }
 438     };
 439 
 440 
 441     // --- On Hidden
 442     /**
 443      * Called just after the {@link ChoiceBox} popup has been hidden.
 444      * @since JavaFX 8u60
 445      */
 446     public final ObjectProperty<EventHandler<Event>> onHiddenProperty() { return onHidden; }
 447     public final void setOnHidden(EventHandler<Event> value) { onHiddenProperty().set(value); }
 448     public final EventHandler<Event> getOnHidden() { return onHiddenProperty().get(); }
 449     private ObjectProperty<EventHandler<Event>> onHidden = new ObjectPropertyBase<EventHandler<Event>>() {
 450         @Override protected void invalidated() {
 451             setEventHandler(ON_HIDDEN, get());
 452         }
 453 
 454         @Override public Object getBean() {
 455             return ChoiceBox.this;
 456         }
 457 
 458         @Override public String getName() {
 459             return "onHidden";
 460         }
 461     };
 462 
 463     /***************************************************************************
 464      *                                                                         *
 465      * Methods                                                                 *
 466      *                                                                         *
 467      **************************************************************************/
 468 
 469     /**
 470      * Opens the list of choices.
 471      */
 472     public void show() {
 473         if (!isDisabled()) setShowing(true);
 474     }
 475 
 476     /**
 477      * Closes the list of choices.
 478      */
 479     public void hide() {
 480         setShowing(false);
 481     }
 482 
 483     /** {@inheritDoc} */
 484     @Override protected Skin<?> createDefaultSkin() {
 485         return new ChoiceBoxSkin<T>(this);
 486     }
 487 
 488     /***************************************************************************
 489      *                                                                         *
 490      * Stylesheet Handling                                                     *
 491      *                                                                         *
 492      **************************************************************************/
 493 
 494     private static final PseudoClass SHOWING_PSEUDOCLASS_STATE =
 495             PseudoClass.getPseudoClass("showing");
 496 
 497     // package for testing
 498     static class ChoiceBoxSelectionModel<T> extends SingleSelectionModel<T> {
 499         private final ChoiceBox<T> choiceBox;
 500 
 501         public ChoiceBoxSelectionModel(final ChoiceBox<T> cb) {
 502             if (cb == null) {
 503                 throw new NullPointerException("ChoiceBox can not be null");
 504             }
 505             this.choiceBox = cb;
 506 
 507             /*
 508              * The following two listeners are used in conjunction with
 509              * SelectionModel.select(T obj) to allow for a developer to select
 510              * an item that is not actually in the data model. When this occurs,
 511              * we actively try to find an index that matches this object, going
 512              * so far as to actually watch for all changes to the items list,
 513              * rechecking each time.
 514              */
 515 
 516             // watching for changes to the items list content
 517             final ListChangeListener<T> itemsContentObserver = c -> {
 518                 if (choiceBox.getItems() == null || choiceBox.getItems().isEmpty()) {
 519                     setSelectedIndex(-1);
 520                 } else if (getSelectedIndex() == -1 && getSelectedItem() != null) {
 521                     int newIndex = choiceBox.getItems().indexOf(getSelectedItem());
 522                     if (newIndex != -1) {
 523                         setSelectedIndex(newIndex);
 524                     }
 525                 }
 526             };
 527             if (this.choiceBox.getItems() != null) {
 528                 this.choiceBox.getItems().addListener(itemsContentObserver);
 529             }
 530 
 531             // watching for changes to the items list
 532             ChangeListener<ObservableList<T>> itemsObserver = (valueModel, oldList, newList) -> {
 533                 if (oldList != null) {
 534                     oldList.removeListener(itemsContentObserver);
 535                 }
 536                 if (newList != null) {
 537                     newList.addListener(itemsContentObserver);
 538                 }
 539                 setSelectedIndex(-1);
 540                 if (getSelectedItem() != null) {
 541                     int newIndex = choiceBox.getItems().indexOf(getSelectedItem());
 542                     if (newIndex != -1) {
 543                         setSelectedIndex(newIndex);
 544                     }
 545                 }
 546             };
 547             this.choiceBox.itemsProperty().addListener(itemsObserver);
 548         }
 549 
 550         // API Implementation
 551         @Override protected T getModelItem(int index) {
 552             final ObservableList<T> items = choiceBox.getItems();
 553             if (items == null) return null;
 554             if (index < 0 || index >= items.size()) return null;
 555             return items.get(index);
 556         }
 557 
 558         @Override protected int getItemCount() {
 559             final ObservableList<T> items = choiceBox.getItems();
 560             return items == null ? 0 : items.size();
 561         }
 562 
 563         /**
 564          * Selects the given row. Since the SingleSelectionModel can only support having
 565          * a single row selected at a time, this also causes any previously selected
 566          * row to be unselected.
 567          * This method is overridden here so that we can move past a Separator
 568          * in a ChoiceBox and select the next valid menuitem.
 569          */
 570         @Override public void select(int index) {
 571             // this does not sound right, we should let the superclass handle it.
 572             super.select(index);
 573 
 574             if (choiceBox.isShowing()) {
 575                 choiceBox.hide();
 576             }
 577         }
 578 
 579         /** {@inheritDoc} */
 580         @Override public void selectPrevious() {
 581             // overridden to properly handle Separators
 582             int index = getSelectedIndex() - 1;
 583             while (index >= 0) {
 584                 final T value = getModelItem(index);
 585                 if (value instanceof Separator) {
 586                     index--;
 587                 } else {
 588                     select(index);
 589                     break;
 590                 }
 591             }
 592         }
 593 
 594         /** {@inheritDoc} */
 595         @Override public void selectNext() {
 596             // overridden to properly handle Separators
 597             int index = getSelectedIndex() + 1;
 598             while (index < getItemCount()) {
 599                 final T value = getModelItem(index);
 600                 if (value instanceof Separator) {
 601                     index++;
 602                 } else {
 603                     select(index);
 604                     break;
 605                 }
 606             }
 607         }
 608     }
 609 
 610     /***************************************************************************
 611      *                                                                         *
 612      * Accessibility handling                                                  *
 613      *                                                                         *
 614      **************************************************************************/
 615 
 616     @Override
 617     public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
 618         switch(attribute) {
 619             case TEXT:
 620                 String accText = getAccessibleText();
 621                 if (accText != null && !accText.isEmpty()) return accText;
 622 
 623                 //let the skin first.
 624                 Object title = super.queryAccessibleAttribute(attribute, parameters);
 625                 if (title != null) return title;
 626                 StringConverter<T> converter = getConverter();
 627                 if (converter == null) {
 628                     return getValue() != null ? getValue().toString() : "";
 629                 }
 630                 return converter.toString(getValue());
 631             case EXPANDED: return isShowing();
 632             default: return super.queryAccessibleAttribute(attribute, parameters);
 633         }
 634     }
 635 
 636     @Override
 637     public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
 638         switch (action) {
 639             case COLLAPSE: hide(); break;
 640             case EXPAND: show(); break;
 641             default: super.executeAccessibleAction(action); break;
 642         }
 643     }
 644 
 645 }