Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.control;
027
028/**
029 * A SelectionModel which enforces the requirement that only a single index
030 * be selected at any given time. This class exists for controls that allow for
031 * pluggable selection models, but which do not allow for multiple selection.
032 * A good example is the {@link ChoiceBox} control. Conversely, most other 
033 * controls ({@link ListView}, {@link TreeView}, {@link TableView}, etc) 
034 * require {@link MultipleSelectionModel} implementations (although 
035 * MultipleSelectionModel does still allow for single selection to be set via the 
036 * {@link MultipleSelectionModel#selectionModeProperty() selectionMode}
037 * property).
038 * 
039 * @see SelectionModel
040 * @see MultipleSelectionModel
041 * @see SelectionMode
042 * @param <T> The type of the item contained in the control that can be selected.
043 */
044public abstract class SingleSelectionModel<T> extends SelectionModel<T> {
045    
046    /***************************************************************************
047     *                                                                         *
048     * Constructor                                                             *
049     *                                                                         *
050     **************************************************************************/  
051    
052    /**
053     * Creates a default SingleSelectionModel instance.
054     */
055    public SingleSelectionModel() { }
056
057    
058
059    /***************************************************************************
060     *                                                                         *
061     * Selection API                                                           *
062     *                                                                         *
063     **************************************************************************/
064    
065    /** {@inheritDoc} */
066    @Override public void clearSelection() {
067        updateSelectedIndex(-1);
068    }
069
070    /** 
071     * Clears the selection of the given index, if it is currently selected.
072     */
073    @Override public void clearSelection(int index) {
074        if (getSelectedIndex() == index) {
075            clearSelection();
076        }
077    }
078
079    /** {@inheritDoc} */
080    @Override public boolean isEmpty() {
081        return getItemCount() == 0 || getSelectedIndex() == -1;
082    }
083
084    /**
085     * <p>This method will return true if the given index is the currently 
086     * selected index in this SingleSelectionModel.</code>.
087     *
088     * @param index The index to check as to whether it is currently selected
089     *      or not.
090     * @return True if the given index is selected, false otherwise.
091     */
092    @Override public boolean isSelected(int index) {
093        return getSelectedIndex() == index;
094    }
095
096    /** 
097     * In the SingleSelectionModel, this method is functionally equivalent to 
098     * calling <code>select(index)</code>, as only one selection is allowed at
099     * a time.
100     */
101    @Override public void clearAndSelect(int index) {
102        select(index);
103    }
104
105    /**
106     * Selects the index for the first instance of given object in the underlying
107     * data model. Since the SingleSelectionModel can
108     * only support having a single index selected at a time, this also causes
109     * any previously selected index to be unselected.
110     */
111    @Override public void select(T obj) {
112        if (obj == null) {
113            setSelectedIndex(-1);
114            setSelectedItem(null);
115            return;
116        }
117        
118        final int itemCount = getItemCount();
119
120        for (int i = 0; i < itemCount; i++) {
121            final T value = getModelItem(i);
122            if (value != null && value.equals(obj)) {
123                select(i);
124                return;
125            }
126        }
127
128        // if we are here, we did not find the item in the entire data model.
129        // Even still, we allow for this item to be set to the give object.
130        // We expect that in concrete subclasses of this class we observe the 
131        // data model such that we check to see if the given item exists in it,
132        // whilst SelectedIndex == -1 && SelectedItem != null.
133        setSelectedItem(obj);
134    }
135
136    /**
137     * Selects the given index. Since the SingleSelectionModel can only support having
138     * a single index selected at a time, this also causes any previously selected
139     * index to be unselected.
140     */
141    @Override public void select(int index) {
142        if (index == -1) {
143            clearSelection();
144            return;
145        }
146        final int itemCount = getItemCount();
147        if (itemCount == 0 || index < 0 || index >= itemCount) return;
148        updateSelectedIndex(index);
149    }
150
151    /**
152     * Selects the previous index. Since the SingleSelectionModel can only support having
153     * a single index selected at a time, this also causes any previously selected
154     * index to be unselected.
155     */
156    @Override public void selectPrevious() {
157        if (getSelectedIndex() == 0) return;
158        select(getSelectedIndex() - 1);
159    }
160
161    /**
162     * Selects the next index. Since the SingleSelectionModel can only support having
163     * a single index selected at a time, this also causes any previously selected
164     * index to be unselected.
165     */
166    @Override public void selectNext() {
167        select(getSelectedIndex() + 1);
168    }
169    
170    /**
171     * Selects the first index. Since the SingleSelectionModel can only support having
172     * a single index selected at a time, this also causes any previously selected
173     * index to be unselected.
174     */
175    @Override public void selectFirst() {
176        if (getItemCount() > 0) {
177            select(0);
178        }
179    }
180
181    /**
182     * Selects the last index. Since the SingleSelectionModel can only support having
183     * a single index selected at a time, this also causes any previously selected
184     * index to be unselected.
185     */
186    @Override public void selectLast() {
187        int numItems = getItemCount();
188        if (numItems > 0 && getSelectedIndex() < numItems - 1) {
189            select(numItems - 1);
190        }
191    }
192    
193    /**
194     * Gets the data model item associated with a specific index.
195     * @param index The position of the item in the underlying data model.
196     * @return The item that exists at the given index.
197     */
198    protected abstract T getModelItem(int index);
199    
200    /**
201     * Gets the number of items available for the selection model. If the number
202     * of items can change dynamically, it is the responsibility of the
203     * concrete SingleSelectionModel implementation to ensure that items are
204     * selected or unselected as appropriate as the items change.
205     * @return A number greater than or equal to 0.
206     */
207    protected abstract int getItemCount();
208
209    // Private Implementation
210    private void updateSelectedIndex(int newIndex) {
211        int currentIndex = getSelectedIndex();
212        T currentItem = getSelectedItem();
213        
214        setSelectedIndex(newIndex);
215        
216        if (currentIndex == -1 && currentItem != null && newIndex == -1) {
217            // no-op: the current selection isn't in the underlying data model - 
218            // we should keep the selected item as the new index is -1
219        } else {
220            setSelectedItem(getModelItem(newIndex));
221        }
222    }    
223}