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