1 /*
   2  * Copyright (c) 2010, 2013, 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.ReadOnlyIntegerProperty;
  29 import javafx.beans.property.ReadOnlyIntegerWrapper;
  30 import javafx.beans.property.ReadOnlyObjectProperty;
  31 import javafx.beans.property.ReadOnlyObjectWrapper;
  32 import javafx.collections.ListChangeListener;
  33 
  34 /**
  35  * SelectionModel is an abstract class used by UI controls to provide a
  36  * consistent API for maintaining selection.
  37  *
  38  * @param <T> The type of the item contained in the control that can be selected.
  39  * @since JavaFX 2.0
  40  */
  41 public abstract class SelectionModel<T> {
  42 
  43     /***************************************************************************
  44      *                                                                         *
  45      * Selection Properties                                                    *
  46      *                                                                         *
  47      **************************************************************************/
  48 
  49     /**
  50      * <p>Refers to the selected index property, which is used to indicate
  51      * the currently selected index value in the selection model. The selected
  52      * index is either -1,
  53      * to represent that there is no selection, or an integer value that is within
  54      * the range of the underlying data model size.
  55      *
  56      * <p>The selected index property is most commonly used when the selection
  57      * model only allows single selection, but is equally applicable when in
  58      * multiple selection mode. When in this mode, the selected index will always
  59      * represent the last selection made.
  60      *
  61      * <p>Note that in the case of multiple selection, it is possible to add
  62      * a {@link ListChangeListener} to the collection returned by
  63      * {@link MultipleSelectionModel#getSelectedIndices()} to be informed whenever
  64      * the selection changes, and this will also work in the case of single selection.
  65      * @return the selected index property
  66      */
  67     public final ReadOnlyIntegerProperty selectedIndexProperty() { return selectedIndex.getReadOnlyProperty(); }
  68     private ReadOnlyIntegerWrapper selectedIndex = new ReadOnlyIntegerWrapper(this, "selectedIndex", -1);
  69     protected final void setSelectedIndex(int value) { selectedIndex.set(value); }
  70 
  71     /**
  72      * <p>Returns the integer value indicating the currently selected index in
  73      * this model. If there are multiple items selected, this will return the
  74      * most recent selection made.
  75      *
  76      * <p>Note that the returned value is a snapshot in time - if you wish to
  77      * observe the selection model for changes to the selected index, you can
  78      * add a ChangeListener as such:
  79      *
  80      * <pre><code>
  81      * SelectionModel sm = ...;
  82      * InvalidationListener listener = ...;
  83      * sm.selectedIndexProperty().addListener(listener);
  84      * </code></pre>
  85      * @return the selected index
  86      */
  87     public final int getSelectedIndex() { return selectedIndexProperty().get(); }
  88 
  89     /**
  90      * <p>Refers to the selected item property, which is used to indicate
  91      * the currently selected item in the selection model. The selected item is
  92      * either null,
  93      * to represent that there is no selection, or an Object that is retrieved
  94      * from the underlying data model of the control the selection model is
  95      * associated with.
  96      *
  97      * <p>The selected item property is most commonly used when the selection
  98      * model is set to be single selection, but is equally applicable when in
  99      * multiple selection mode. When in this mode, the selected item will always
 100      * represent the last selection made.
 101      * @return the selected item property
 102      */
 103     public final ReadOnlyObjectProperty<T> selectedItemProperty() { return selectedItem.getReadOnlyProperty(); }
 104     private ReadOnlyObjectWrapper<T> selectedItem = new ReadOnlyObjectWrapper<T>(this, "selectedItem");
 105     protected final void setSelectedItem(T value) { selectedItem.set(value); }
 106 
 107     /**
 108      * Returns the currently selected object (which resides in the selected index
 109      * position). If there are multiple items selected, this will return the
 110      * object contained at the index returned by getSelectedIndex() (which is
 111      * always the index to the most recently selected item).
 112      *
 113      * <p>Note that the returned value is a snapshot in time - if you wish to
 114      * observe the selection model for changes to the selected item, you can
 115      * add a ChangeListener as such:
 116      *
 117      * <pre><code>
 118      * SelectionModel sm = ...;
 119      * InvalidationListener listener = ...;
 120      * sm.selectedItemProperty().addListener(listener);
 121      * </code></pre>
 122      * @return the selected item
 123      */
 124     public final T getSelectedItem() { return selectedItemProperty().get(); }
 125 
 126 
 127     /***************************************************************************
 128      *                                                                         *
 129      * Constructor                                                             *
 130      *                                                                         *
 131      **************************************************************************/
 132 
 133     /**
 134      * Creates a default SelectionModel instance.
 135      */
 136     public SelectionModel() { }
 137 
 138 
 139     /***************************************************************************
 140      *                                                                         *
 141      * Selection API                                                           *
 142      *                                                                         *
 143      **************************************************************************/
 144 
 145 
 146     /**
 147      * A method that clears any selection prior to setting the selection to the
 148      * given index. The purpose of this method is to avoid having to call
 149      * {@link #clearSelection()} first, meaning that observers that are listening to
 150      * the {@link #selectedIndexProperty() selected index} property will not
 151      * see the selected index being temporarily set to -1.
 152      *
 153      * @param index The index that should be the only selected index in this
 154      *      selection model.
 155      */
 156     public abstract void clearAndSelect(int index);
 157 
 158     /**
 159      * <p>This will select the given index in the selection model, assuming the
 160      * index is within the valid range (i.e. greater than or equal to zero, and
 161      * less than the total number of items in the underlying data model).
 162      *
 163      * <p>If there is already one or more indices selected in this model, calling
 164      * this method will <b>not</b> clear these selections - to do so it is
 165      * necessary to first call {@link #clearSelection()}.
 166      *
 167      * <p>If the index is already selected, it will not be selected again, or
 168      * unselected. However, if multiple selection is implemented, then calling
 169      * select on an already selected index will have the effect of making the index
 170      * the new selected index (as returned by {@link #getSelectedIndex()}.
 171      *
 172      * @param index The position of the item to select in the selection model.
 173      */
 174     public abstract void select(int index);
 175 
 176     /**
 177      * <p>This method will attempt to select the index that contains the given
 178      * object. It will iterate through the underlying data model until it finds
 179      * an item whose value is equal to the given object. At this point it will
 180      * stop iterating - this means that this method will not select multiple
 181      * indices.
 182      *
 183      * @param obj The object to attempt to select in the underlying data model.
 184      */
 185     public abstract void select(T obj);
 186 
 187     /**
 188      * <p>This method will clear the selection of the item in the given index.
 189      * If the given index is not selected, nothing will happen.
 190      *
 191      * @param index The selected item to deselect.
 192      */
 193     public abstract void clearSelection(int index);
 194 
 195     /**
 196      * <p>Clears the selection model of all selected indices.
 197      */
 198     public abstract void clearSelection();
 199 
 200     /**
 201      * <p>Convenience method to inform if the given index is currently selected
 202      * in this SelectionModel. Is functionally equivalent to calling
 203      * <code>getSelectedIndices().contains(index)</code>.
 204      *
 205      * @param index The index to check as to whether it is currently selected
 206      *      or not.
 207      * @return True if the given index is selected, false otherwise.
 208      */
 209     public abstract boolean isSelected(int index);
 210 
 211     /**
 212      * This method is available to test whether there are any selected
 213      * indices/items. It will return true if there are <b>no</b> selected items,
 214      * and false if there are.
 215      *
 216      * @return Will return true if there are <b>no</b> selected items, and false
 217      *          if there are.
 218      */
 219     public abstract boolean isEmpty();
 220 
 221     /**
 222      * <p>This method will attempt to select the index directly before the current
 223      * focused index. If clearSelection is not called first, this method
 224      * will have the result of selecting the previous index, whilst retaining
 225      * the selection of any other currently selected indices.</p>
 226      *
 227      * <p>Calling this method will only succeed if:</p>
 228      *
 229      * <ul>
 230      *   <li>There is currently a lead/focused index.
 231      *   <li>The lead/focus index is not the first index in the control.
 232      *   <li>The previous index is not already selected.
 233      * </ul>
 234      *
 235      * <p>If any of these conditions is false, no selection event will take
 236      * place.</p>
 237      */
 238     public abstract void selectPrevious();
 239 
 240     /**
 241      * <p>This method will attempt to select the index directly after the current
 242      * focused index. If clearSelection is not called first, this method
 243      * will have the result of selecting the next index, whilst retaining
 244      * the selection of any other currently selected indices.</p>
 245      *
 246      * <p>Calling this method will only succeed if:</p>
 247      *
 248      * <ul>
 249      *   <li>There is currently a lead/focused index.
 250      *   <li>The lead/focus index is not the last index in the control.
 251      *   <li>The next index is not already selected.
 252      * </ul>
 253      *
 254      * <p>If any of these conditions is false, no selection event will take
 255      * place.</p>
 256      */
 257     public abstract void selectNext();
 258 
 259     /**
 260      * <p>This method will attempt to select the first index in the control. If
 261      * clearSelection is not called first, this method
 262      * will have the result of selecting the first index, whilst retaining
 263      * the selection of any other currently selected indices.</p>
 264      *
 265      * <p>If the first index is already selected, calling this method will have
 266      * no result, and no selection event will take place.</p>
 267      */
 268     public abstract void selectFirst();
 269 
 270     /**
 271      * <p>This method will attempt to select the last index in the control. If
 272      * clearSelection is not called first, this method
 273      * will have the result of selecting the last index, whilst retaining
 274      * the selection of any other currently selected indices.</p>
 275      *
 276      * <p>If the last index is already selected, calling this method will have
 277      * no result, and no selection event will take place.</p>
 278      */
 279     public abstract void selectLast();
 280 }