1 /* 2 * Copyright (c) 2011, 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 /** 29 * A SelectionModel which enforces the requirement that only a single index 30 * be selected at any given time. This class exists for controls that allow for 31 * pluggable selection models, but which do not allow for multiple selection. 32 * A good example is the {@link ChoiceBox} control. Conversely, most other 33 * controls ({@link ListView}, {@link TreeView}, {@link TableView}, etc) 34 * require {@link MultipleSelectionModel} implementations (although 35 * MultipleSelectionModel does still allow for single selection to be set via the 36 * {@link MultipleSelectionModel#selectionModeProperty() selectionMode} 37 * property). 38 * 39 * @see SelectionModel 40 * @see MultipleSelectionModel 41 * @see SelectionMode 42 * @param <T> The type of the item contained in the control that can be selected. 43 * @since JavaFX 2.0 44 */ 45 public abstract class SingleSelectionModel<T> extends SelectionModel<T> { 46 47 /*************************************************************************** 48 * * 49 * Constructor * 50 * * 51 **************************************************************************/ 52 53 /** 54 * Creates a default SingleSelectionModel instance. 55 */ 56 public SingleSelectionModel() { } 57 58 59 60 /*************************************************************************** 61 * * 62 * Selection API * 63 * * 64 **************************************************************************/ 65 66 /** {@inheritDoc} */ 67 @Override public void clearSelection() { 68 updateSelectedIndex(-1); 69 } 70 71 /** 72 * Clears the selection of the given index, if it is currently selected. 73 */ 74 @Override public void clearSelection(int index) { 75 if (getSelectedIndex() == index) { 76 clearSelection(); 77 } 78 } 79 80 /** {@inheritDoc} */ 81 @Override public boolean isEmpty() { 82 return getItemCount() == 0 || getSelectedIndex() == -1; 83 } 84 85 /** 86 * <p>This method will return true if the given index is the currently 87 * selected index in this SingleSelectionModel.</code>. 88 * 89 * @param index The index to check as to whether it is currently selected 90 * or not. 91 * @return True if the given index is selected, false otherwise. 92 */ 93 @Override public boolean isSelected(int index) { 94 return getSelectedIndex() == index; 95 } 96 97 /** 98 * In the SingleSelectionModel, this method is functionally equivalent to 99 * calling <code>select(index)</code>, as only one selection is allowed at 100 * a time. 101 */ 102 @Override public void clearAndSelect(int index) { 103 select(index); 104 } 105 106 /** 107 * Selects the index for the first instance of given object in the underlying 108 * data model. Since the SingleSelectionModel can 109 * only support having a single index selected at a time, this also causes 110 * any previously selected index to be unselected. 111 */ 112 @Override public void select(T obj) { 113 if (obj == null) { 114 setSelectedIndex(-1); 115 setSelectedItem(null); 116 return; 117 } 118 119 final int itemCount = getItemCount(); 120 121 for (int i = 0; i < itemCount; i++) { 122 final T value = getModelItem(i); 123 if (value != null && value.equals(obj)) { 124 select(i); 125 return; 126 } 127 } 128 129 // if we are here, we did not find the item in the entire data model. 130 // Even still, we allow for this item to be set to the give object. 131 // We expect that in concrete subclasses of this class we observe the 132 // data model such that we check to see if the given item exists in it, 133 // whilst SelectedIndex == -1 && SelectedItem != null. 134 setSelectedItem(obj); 135 } 136 137 /** 138 * Selects the given index. Since the SingleSelectionModel can only support having 139 * a single index selected at a time, this also causes any previously selected 140 * index to be unselected. 141 */ 142 @Override public void select(int index) { 143 if (index == -1) { 144 clearSelection(); 145 return; 146 } 147 final int itemCount = getItemCount(); 148 if (itemCount == 0 || index < 0 || index >= itemCount) return; 149 updateSelectedIndex(index); 150 } 151 152 /** 153 * Selects the previous index. Since the SingleSelectionModel can only support having 154 * a single index selected at a time, this also causes any previously selected 155 * index to be unselected. 156 */ 157 @Override public void selectPrevious() { 158 if (getSelectedIndex() == 0) return; 159 select(getSelectedIndex() - 1); 160 } 161 162 /** 163 * Selects the next index. Since the SingleSelectionModel can only support having 164 * a single index selected at a time, this also causes any previously selected 165 * index to be unselected. 166 */ 167 @Override public void selectNext() { 168 select(getSelectedIndex() + 1); 169 } 170 171 /** 172 * Selects the first index. Since the SingleSelectionModel can only support having 173 * a single index selected at a time, this also causes any previously selected 174 * index to be unselected. 175 */ 176 @Override public void selectFirst() { 177 if (getItemCount() > 0) { 178 select(0); 179 } 180 } 181 182 /** 183 * Selects the last index. Since the SingleSelectionModel can only support having 184 * a single index selected at a time, this also causes any previously selected 185 * index to be unselected. 186 */ 187 @Override public void selectLast() { 188 int numItems = getItemCount(); 189 if (numItems > 0 && getSelectedIndex() < numItems - 1) { 190 select(numItems - 1); 191 } 192 } 193 194 /** 195 * Gets the data model item associated with a specific index. 196 * @param index The position of the item in the underlying data model. 197 * @return The item that exists at the given index. 198 */ 199 protected abstract T getModelItem(int index); 200 201 /** 202 * Gets the number of items available for the selection model. If the number 203 * of items can change dynamically, it is the responsibility of the 204 * concrete SingleSelectionModel implementation to ensure that items are 205 * selected or unselected as appropriate as the items change. 206 * @return A number greater than or equal to 0. 207 */ 208 protected abstract int getItemCount(); 209 210 // Private Implementation 211 private void updateSelectedIndex(int newIndex) { 212 int currentIndex = getSelectedIndex(); 213 T currentItem = getSelectedItem(); 214 215 setSelectedIndex(newIndex); 216 217 if (currentIndex == -1 && currentItem != null && newIndex == -1) { 218 // no-op: the current selection isn't in the underlying data model - 219 // we should keep the selected item as the new index is -1 220 } else { 221 // we don't use newIndex here, to prevent RT-32139 (which has a unit 222 // test developed to prevent regressions in the future) 223 setSelectedItem(getModelItem(getSelectedIndex())); 224 } 225 } 226 }