1 /* 2 * Copyright (c) 2011, 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 com.sun.javafx.scene.control.behavior; 27 28 import com.sun.javafx.scene.control.inputmap.InputMap; 29 import javafx.event.Event; 30 import javafx.scene.control.SelectionModel; 31 import javafx.scene.control.Tab; 32 import javafx.scene.control.TabPane; 33 import javafx.scene.input.*; 34 import com.sun.javafx.scene.control.inputmap.KeyBinding; 35 36 import java.util.List; 37 38 import static javafx.scene.input.KeyCode.*; 39 import static com.sun.javafx.scene.control.inputmap.InputMap.KeyMapping; 40 import static com.sun.javafx.scene.control.inputmap.InputMap.MouseMapping; 41 42 public class TabPaneBehavior extends BehaviorBase<TabPane> { 43 44 private final InputMap<TabPane> tabPaneInputMap; 45 46 public TabPaneBehavior(TabPane tabPane) { 47 super(tabPane); 48 49 // create a map for TabPane-specific mappings (this reuses the default 50 // InputMap installed on the control, if it is non-null, allowing us to pick up any user-specified mappings) 51 tabPaneInputMap = createInputMap(); 52 53 // TabPane-specific mappings for key and mouse input 54 addDefaultMapping(tabPaneInputMap, 55 new KeyMapping(UP, e -> selectPreviousTab()), 56 new KeyMapping(DOWN, e -> selectNextTab()), 57 new KeyMapping(LEFT, e -> rtl(tabPane, this::selectNextTab, this::selectPreviousTab)), 58 new KeyMapping(RIGHT, e -> rtl(tabPane, this::selectPreviousTab, this::selectNextTab)), 59 new KeyMapping(HOME, e -> { 60 if (getNode().isFocused()) { 61 moveSelection(0, 1); 62 } 63 }), 64 new KeyMapping(END, e -> { 65 if (getNode().isFocused()) { 66 moveSelection(getNode().getTabs().size() - 1, -1); 67 } 68 }), 69 new KeyMapping(new KeyBinding(PAGE_UP).ctrl(), e -> selectPreviousTab()), 70 new KeyMapping(new KeyBinding(PAGE_DOWN).ctrl(), e -> selectNextTab()), 71 new KeyMapping(new KeyBinding(TAB).ctrl(), e -> selectNextTab()), 72 new KeyMapping(new KeyBinding(TAB).ctrl().shift(), e -> selectPreviousTab()), 73 new MouseMapping(MouseEvent.MOUSE_PRESSED, e -> getNode().requestFocus()) 74 ); 75 } 76 77 @Override public InputMap<TabPane> getInputMap() { 78 return tabPaneInputMap; 79 } 80 81 public void selectTab(Tab tab) { 82 getNode().getSelectionModel().select(tab); 83 } 84 85 public boolean canCloseTab(Tab tab) { 86 Event event = new Event(tab,tab,Tab.TAB_CLOSE_REQUEST_EVENT); 87 Event.fireEvent(tab, event); 88 return ! event.isConsumed(); 89 } 90 91 public void closeTab(Tab tab) { 92 TabPane tabPane = getNode(); 93 // only switch to another tab if the selected tab is the one we're closing 94 int index = tabPane.getTabs().indexOf(tab); 95 if (index != -1) { 96 tabPane.getTabs().remove(index); 97 } 98 if (tab.getOnClosed() != null) { 99 Event.fireEvent(tab, new Event(Tab.CLOSED_EVENT)); 100 } 101 } 102 103 // Find a tab after the currently selected that is not disabled. Loop around 104 // if no tabs are found after currently selected tab. 105 public void selectNextTab() { 106 moveSelection(1); 107 } 108 109 // Find a tab before the currently selected that is not disabled. 110 public void selectPreviousTab() { 111 moveSelection(-1); 112 } 113 114 private void moveSelection(int delta) { 115 moveSelection(getNode().getSelectionModel().getSelectedIndex(), delta); 116 } 117 118 private void moveSelection(int startIndex, int delta) { 119 final TabPane tabPane = getNode(); 120 int tabIndex = findValidTab(startIndex, delta); 121 if (tabIndex > -1) { 122 final SelectionModel<Tab> selectionModel = tabPane.getSelectionModel(); 123 selectionModel.select(tabIndex); 124 } 125 tabPane.requestFocus(); 126 } 127 128 private int findValidTab(int startIndex, int delta) { 129 final TabPane tabPane = getNode(); 130 final List<Tab> tabs = tabPane.getTabs(); 131 final int max = tabs.size(); 132 133 int index = startIndex; 134 do { 135 index = nextIndex(index + delta, max); 136 Tab tab = tabs.get(index); 137 if (tab != null && !tab.isDisable()) { 138 return index; 139 } 140 } while (index != startIndex); 141 142 return -1; 143 } 144 145 private int nextIndex(int value, int max) { 146 final int min = 0; 147 int r = value % max; 148 if (r > min && max < min) { 149 r = r + max - min; 150 } else if (r < min && max > min) { 151 r = r + max - min; 152 } 153 return r; 154 } 155 }