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.ObjectProperty;
  29 import javafx.beans.property.ObjectPropertyBase;
  30 import javafx.collections.ListChangeListener;
  31 import javafx.collections.ObservableList;
  32 
  33 /**
  34  * An abstract class that extends {@link SelectionModel} to add API to support
  35  * multiple selection.
  36  *
  37  * @see SelectionModel
  38  * @see SelectionMode
  39  * @param <T> The type of the item contained in the control that can be selected.
  40  * @since JavaFX 2.0
  41  */
  42 public abstract class MultipleSelectionModel<T> extends SelectionModel<T> {
  43 
  44     /***************************************************************************
  45      *                                                                         *
  46      * Properties                                                              *
  47      *                                                                         *
  48      **************************************************************************/
  49 
  50     /**
  51      * <p>Specifies the selection mode to use in this selection model. The
  52      * selection mode specifies how many items in the underlying data model can
  53      * be selected at any one time.
  54      *
  55      * <p>By default, the selection mode is <code>SelectionMode.SINGLE</code>.
  56      */
  57     private ObjectProperty<SelectionMode> selectionMode;
  58     public final void setSelectionMode(SelectionMode value) {
  59         selectionModeProperty().set(value);
  60     }
  61 
  62     public final SelectionMode getSelectionMode() {
  63         return selectionMode == null ? SelectionMode.SINGLE : selectionMode.get();
  64     }
  65 
  66     public final ObjectProperty<SelectionMode> selectionModeProperty() {
  67         if (selectionMode == null) {
  68             selectionMode = new ObjectPropertyBase<SelectionMode>(SelectionMode.SINGLE) {
  69                 @Override protected void invalidated() {
  70                     if (getSelectionMode() == SelectionMode.SINGLE) {
  71                         // we need to pick out just the last selected item, as we've gone
  72                         // to single selection
  73                         if (! isEmpty()) {
  74                             int lastIndex = getSelectedIndex();
  75                             clearSelection();
  76                             select(lastIndex);
  77                         }
  78                     }
  79                 }
  80 
  81                 @Override
  82                 public Object getBean() {
  83                     return MultipleSelectionModel.this;
  84                 }
  85 
  86                 @Override
  87                 public String getName() {
  88                     return "selectionMode";
  89                 }
  90             };
  91         }
  92         return selectionMode;
  93     }
  94 
  95 
  96 
  97     /***************************************************************************
  98      *                                                                         *
  99      * Constructor                                                             *
 100      *                                                                         *
 101      **************************************************************************/
 102 
 103     /**
 104      * Creates a default MultipleSelectionModel instance.
 105      */
 106     public MultipleSelectionModel() { }
 107 
 108 
 109 
 110     /***************************************************************************
 111      *                                                                         *
 112      * Public API                                                              *
 113      *                                                                         *
 114      **************************************************************************/
 115 
 116     /**
 117      * <p>Returns a <b>read-only</b> ObservableList of all selected indices. The
 118      * ObservableList will be updated  by the selection model to always reflect
 119      * changes in selection. This can be observed by adding a
 120      * {@link ListChangeListener} to the returned ObservableList.
 121      * @return the list of selected indices
 122      */
 123     public abstract ObservableList<Integer> getSelectedIndices();
 124 
 125     /**
 126      * <p>Returns a <b>read-only</b> ObservableList of all selected items. The
 127      * ObservableList will be updated further by the selection model to always reflect
 128      * changes in selection. This can be observed by adding a
 129      * {@link ListChangeListener} to the returned ObservableList.
 130      * @return the list of selected items
 131      */
 132     public abstract ObservableList<T> getSelectedItems();
 133 
 134     /**
 135      * <p>This method allows for one or more selections to be set at the same time.
 136      * It will ignore any value that is not within the valid range (i.e. greater
 137      * than or equal to zero, and less than the total number of items in the
 138      * underlying data model). Any duplication of indices will be ignored.
 139      *
 140      * <p>If there is already one or more indices selected in this model, calling
 141      * this method will <b>not</b> clear these selections - to do so it is
 142      * necessary to first call clearSelection.
 143      *
 144      * <p>The last valid value given will become the selected index / selected
 145      * item.
 146      * @param index the selected index
 147      * @param indices the selected indices
 148      */
 149     public abstract void selectIndices(int index, int... indices);
 150 
 151     /**
 152      * <p>Selects all indices from the given start index to the item before the
 153      * given end index. This means that the selection is inclusive of the start
 154      * index, and exclusive of the end index. This method will work regardless
 155      * of whether start &lt; end or start &gt; end: the only constant is that the
 156      * index before the given end index will become the selected index.
 157      *
 158      * <p>If there is already one or more indices selected in this model, calling
 159      * this method will <b>not</b> clear these selections - to do so it is
 160      * necessary to first call clearSelection.
 161      *
 162      * @param start The first index to select - this index will be selected.
 163      * @param end The last index of the selection - this index will not be selected.
 164      */
 165     public void selectRange(final int start, final int end) {
 166         if (start == end) return;
 167 
 168         final boolean asc = start < end;
 169         final int low = asc ? start : end;      // Math.min(start, end);
 170         final int high = asc ? end : start;     //Math.max(start, end);
 171         final int arrayLength = high - low - 1;
 172 
 173         int[] indices = new int[arrayLength];
 174 
 175         int startValue = asc ? low : high;
 176         int firstVal = asc ? startValue++ : startValue--;
 177         for (int i = 0; i < arrayLength; i++) {
 178             indices[i] = asc ? startValue++ : startValue--;
 179         }
 180         selectIndices(firstVal, indices);
 181     }
 182 
 183     /**
 184      * <p>Convenience method to select all available indices.</p>
 185      */
 186     public abstract void selectAll();
 187 
 188     /**
 189      * <p>This method will attempt to select the first index in the control. If
 190      * clearSelection is not called first, this method
 191      * will have the result of selecting the first index, whilst retaining
 192      * the selection of any other currently selected indices.</p>
 193      *
 194      * <p>If the first index is already selected, calling this method will have
 195      * no result, and no selection event will take place.</p>
 196      */
 197     @Override public abstract void selectFirst();
 198 
 199     /**
 200      * <p>This method will attempt to select the last index in the control. If
 201      * clearSelection is not called first, this method
 202      * will have the result of selecting the last index, whilst retaining
 203      * the selection of any other currently selected indices.</p>
 204      *
 205      * <p>If the last index is already selected, calling this method will have
 206      * no result, and no selection event will take place.</p>
 207      */
 208     @Override public abstract void selectLast();
 209 }