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      */
 122     public abstract ObservableList<Integer> getSelectedIndices();
 123 
 124     /**
 125      * <p>Returns a <b>read-only</b> ObservableList of all selected items. The
 126      * ObservableList will be updated further by the selection model to always reflect
 127      * changes in selection. This can be observed by adding a
 128      * {@link ListChangeListener} to the returned ObservableList.
 129      */
 130     public abstract ObservableList<T> getSelectedItems();
 131 
 132     /**
 133      * <p>This method allows for one or more selections to be set at the same time.
 134      * It will ignore any value that is not within the valid range (i.e. greater
 135      * than or equal to zero, and less than the total number of items in the
 136      * underlying data model). Any duplication of indices will be ignored.
 137      *
 138      * <p>If there is already one or more indices selected in this model, calling
 139      * this method will <b>not</b> clear these selections - to do so it is
 140      * necessary to first call clearSelection.
 141      *
 142      * <p>The last valid value given will become the selected index / selected
 143      * item.
 144      */
 145     public abstract void selectIndices(int index, int... indices);
 146 
 147     /**
 148      * <p>Selects all indices from the given start index to the item before the
 149      * given end index. This means that the selection is inclusive of the start
 150      * index, and exclusive of the end index. This method will work regardless
 151      * of whether start < end or start > end: the only constant is that the
 152      * index before the given end index will become the selected index.
 153      *
 154      * <p>If there is already one or more indices selected in this model, calling
 155      * this method will <b>not</b> clear these selections - to do so it is
 156      * necessary to first call clearSelection.
 157      *
 158      * @param start The first index to select - this index will be selected.
 159      * @param end The last index of the selection - this index will not be selected.
 160      */
 161     public void selectRange(final int start, final int end) {
 162         if (start == end) return;
 163 
 164         final boolean asc = start < end;
 165         final int low = asc ? start : end;      // Math.min(start, end);
 166         final int high = asc ? end : start;     //Math.max(start, end);
 167         final int arrayLength = high - low - 1;
 168 
 169         int[] indices = new int[arrayLength];
 170 
 171         int startValue = asc ? low : high;
 172         int firstVal = asc ? startValue++ : startValue--;
 173         for (int i = 0; i < arrayLength; i++) {
 174             indices[i] = asc ? startValue++ : startValue--;
 175         }
 176         selectIndices(firstVal, indices);
 177     }
 178 
 179     /**
 180      * <p>Convenience method to select all available indices.</p>
 181      */
 182     public abstract void selectAll();
 183 
 184     /**
 185      * <p>This method will attempt to select the first index in the control. If
 186      * clearSelection is not called first, this method
 187      * will have the result of selecting the first index, whilst retaining
 188      * the selection of any other currently selected indices.</p>
 189      *
 190      * <p>If the first index is already selected, calling this method will have
 191      * no result, and no selection event will take place.</p>
 192      */
 193     @Override public abstract void selectFirst();
 194 
 195     /**
 196      * <p>This method will attempt to select the last index in the control. If
 197      * clearSelection is not called first, this method
 198      * will have the result of selecting the last index, whilst retaining
 199      * the selection of any other currently selected indices.</p>
 200      *
 201      * <p>If the last index is already selected, calling this method will have
 202      * no result, and no selection event will take place.</p>
 203      */
 204     @Override public abstract void selectLast();
 205 }