1 /* 2 * Copyright (c) 2010, 2014, 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 33 /** 34 * The abstract base class for FocusModel implementations. 35 * @since JavaFX 2.0 36 */ 37 public abstract class FocusModel<T> { 38 39 /*********************************************************************** 40 * * 41 * Constructors * 42 * * 43 **********************************************************************/ 44 45 /** 46 * Creates a default FocusModel instance. 47 */ 48 public FocusModel() { 49 focusedIndexProperty().addListener(valueModel -> { 50 // we used to lazily retrieve the focused item, but now we just 51 // do it when the focused index changes. 52 setFocusedItem(getModelItem(getFocusedIndex())); 53 }); 54 } 55 56 57 58 /*************************************************************************** 59 * * 60 * Focus Properties * 61 * * 62 **************************************************************************/ 63 64 /** 65 * The index of the current item in the FocusModel which has the focus. It 66 * is possible that this will be -1, but only if the control is empty. 67 * If the control is not itself focused, this property will still 68 * reference the row index that would receive the keyboard focus if the control 69 * itself were focused. 70 */ 71 private ReadOnlyIntegerWrapper focusedIndex = new ReadOnlyIntegerWrapper(this, "focusedIndex", -1); 72 public final ReadOnlyIntegerProperty focusedIndexProperty() { return focusedIndex.getReadOnlyProperty(); } 73 public final int getFocusedIndex() { return focusedIndex.get(); } 74 final void setFocusedIndex(int value) { focusedIndex.set(value); } 75 76 77 78 /** 79 * The current item in the FocusModel which has the focus. It 80 * is possible that this will be null, but only if the control is empty. 81 * If the control is not itself focused, this property will still 82 * reference the item that would receive the keyboard focus if the control 83 * itself were focused. 84 */ 85 private ReadOnlyObjectWrapper<T> focusedItem = new ReadOnlyObjectWrapper<T>(this, "focusedItem"); 86 public final ReadOnlyObjectProperty<T> focusedItemProperty() { return focusedItem.getReadOnlyProperty(); } 87 public final T getFocusedItem() { return focusedItemProperty().get(); } 88 final void setFocusedItem(T value) { focusedItem.set(value); } 89 90 91 92 /*********************************************************************** 93 * * 94 * Public Focus API * 95 * * 96 **********************************************************************/ 97 98 99 /** 100 * Returns the number of items in the data model that underpins the control. 101 * An example would be that a ListView focus model would likely return 102 * <code>listView.getItems().size()</code>. The valid range of focusable 103 * indices is between 0 and whatever is returned by this method. 104 * @return the number of items in the data model that underpins the control 105 */ 106 protected abstract int getItemCount(); 107 108 /** 109 * Returns the item at the given index. An example using ListView would be 110 * <code>listView.getItems().get(index)</code>. 111 * 112 * @param index The index of the item that is requested from the underlying 113 * data model. 114 * @return Returns null if the index is out of bounds, or an element of type 115 * T that is related to the given index. 116 */ 117 protected abstract T getModelItem(int index); 118 119 /** 120 * <p>Convenience method to inform if the given index is currently focused 121 * in this SelectionModel. Is functionally equivalent to calling 122 * <pre><code>getFocusedIndex() == index</code></pre>. 123 * 124 * @param index The index to check as to whether it is currently focused 125 * or not. 126 * @return True if the given index is focused, false otherwise. 127 */ 128 public boolean isFocused(int index) { 129 if (index < 0 || index >= getItemCount()) return false; 130 131 return getFocusedIndex() == index; 132 } 133 134 /** 135 * Causes the item at the given index to receive the focus. This does not 136 * cause the current selection to change. Updates the focusedItem and 137 * focusedIndex properties such that <code>focusedIndex = -1</code> unless 138 * <code>0 <= index < model size</code>. 139 * 140 * @param index The index of the item to get focus. 141 */ 142 public void focus(int index) { 143 if (index < 0 || index >= getItemCount()) { 144 setFocusedIndex(-1); 145 } else { 146 int oldFocusIndex = getFocusedIndex(); 147 setFocusedIndex(index); 148 149 if (oldFocusIndex == index) { 150 // manually update the focus item to ensure consistency 151 setFocusedItem(getModelItem(index)); 152 } 153 } 154 } 155 156 /** 157 * Attempts to give focus to the row previous to the currently focused row. 158 * If the current focus owner is the first row, or is -1 (representing that 159 * there is no current focus owner), calling this method will have no result. 160 */ 161 public void focusPrevious() { 162 if (getFocusedIndex() == -1) { 163 focus(0); 164 } else if (getFocusedIndex() > 0) { 165 focus(getFocusedIndex() - 1); 166 } 167 } 168 169 /** 170 * Attempts to give focus to the row after to the currently focused row. 171 * If the current focus owner is the last row, calling this method will have 172 * no result. 173 */ 174 public void focusNext() { 175 if (getFocusedIndex() == -1) { 176 focus(0); 177 } else if (getFocusedIndex() != getItemCount() -1) { 178 focus(getFocusedIndex() + 1); 179 } 180 } 181 }