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 com.sun.javafx.scene.control.behavior; 27 28 import javafx.scene.control.ChoiceBox; 29 import javafx.scene.control.SelectionModel; 30 import javafx.scene.input.MouseButton; 31 import javafx.scene.input.MouseEvent; 32 import java.util.ArrayList; 33 import java.util.List; 34 import com.sun.javafx.scene.control.skin.Utils; 35 import static javafx.scene.input.KeyCode.CANCEL; 36 import static javafx.scene.input.KeyCode.DOWN; 37 import static javafx.scene.input.KeyCode.ENTER; 38 import static javafx.scene.input.KeyCode.ESCAPE; 39 import static javafx.scene.input.KeyCode.SPACE; 40 import static javafx.scene.input.KeyEvent.KEY_PRESSED; 41 import static javafx.scene.input.KeyEvent.KEY_RELEASED; 42 43 /** 44 * ChoiceBoxBehavior - default implementation 45 * 46 * @profile common 47 */ 48 public class ChoiceBoxBehavior<T> extends BehaviorBase<ChoiceBox<T>> { 49 /** 50 * The key bindings for the ChoiceBox. It seems this should really be the 51 * same as with the ButtonBehavior super class, but it doesn't handle ENTER 52 * events on desktop, whereas this does. It may be a proper analysis of the 53 * interaction logic would allow us to share bindings, but for now, we simply 54 * build it up specially here. 55 */ 56 protected static final List<KeyBinding> CHOICE_BUTTON_BINDINGS = new ArrayList<KeyBinding>(); 57 static { 58 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(SPACE, KEY_PRESSED, "Press")); 59 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(SPACE, KEY_RELEASED, "Release")); 60 61 if (Utils.isTwoLevelFocus()) { 62 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(ENTER, KEY_PRESSED, "Press")); 63 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(ENTER, KEY_RELEASED, "Release")); 64 } 65 66 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(ESCAPE, KEY_RELEASED, "Cancel")); 67 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(DOWN, KEY_RELEASED, "Down")); 68 CHOICE_BUTTON_BINDINGS.add(new KeyBinding(CANCEL, KEY_RELEASED, "Cancel")); 69 70 } 71 72 private TwoLevelFocusComboBehavior tlFocus; 73 74 /************************************************************************** 75 * Setup KeyBindings * 76 *************************************************************************/ 77 @Override protected void callAction(String name) { 78 if (name.equals("Cancel")) cancel(); 79 else if (name.equals("Press")) keyPressed(); 80 else if (name.equals("Release")) keyReleased(); 81 else if (name.equals("Down")) showPopup(); 82 else super.callAction(name); 83 } 84 85 public ChoiceBoxBehavior(ChoiceBox<T> control) { 86 super(control, CHOICE_BUTTON_BINDINGS); 87 // Only add this if we're on an embedded platform that supports 5-button navigation 88 if (Utils.isTwoLevelFocus()) { 89 tlFocus = new TwoLevelFocusComboBehavior(control); // needs to be last. 90 } 91 } 92 93 @Override public void dispose() { 94 if (tlFocus != null) tlFocus.dispose(); 95 super.dispose(); 96 } 97 98 public void select(int index) { 99 SelectionModel<T> sm = getControl().getSelectionModel(); 100 if (sm == null) return; 101 102 sm.select(index); 103 } 104 105 public void close() { 106 getControl().hide(); 107 } 108 109 public void showPopup() { 110 getControl().show(); 111 } 112 113 /** 114 * Invoked when a mouse press has occurred over the box. In addition to 115 * potentially arming the Button, this will transfer focus to the box 116 */ 117 @Override public void mousePressed(MouseEvent e) { 118 ChoiceBox<T> choiceButton = getControl(); 119 super.mousePressed(e); 120 if (choiceButton.isFocusTraversable()) choiceButton.requestFocus(); 121 } 122 123 /** 124 * Invoked when a mouse release has occurred. We determine whether this 125 * was done in a manner that would fire the box's action. This happens 126 * only if the box was armed by a corresponding mouse press. 127 */ 128 @Override public void mouseReleased(MouseEvent e) { 129 ChoiceBox<T> choiceButton = getControl(); 130 super.mouseReleased(e); 131 if (choiceButton.isShowing() || !choiceButton.contains(e.getX(), e.getY())) { 132 choiceButton.hide(); // hide if already showing 133 } 134 else if (e.getButton() == MouseButton.PRIMARY) { 135 choiceButton.show(); 136 } 137 } 138 139 /** 140 * This function is invoked when an appropriate keystroke occurs which 141 * causes this box to be armed if it is not already armed by a mouse 142 * press. 143 */ 144 private void keyPressed() { 145 ChoiceBox<T> choiceButton = getControl(); 146 if (!choiceButton.isShowing()) { 147 choiceButton.show(); 148 } 149 } 150 151 /** 152 * Invoked when a valid keystroke release occurs which causes the box 153 * to fire if it was armed by a keyPress. 154 */ 155 private void keyReleased() { 156 } 157 158 // no-op 159 /** 160 * Invoked when "escape" key is released 161 */ 162 public void cancel() { 163 ChoiceBox<T> choiceButton = getControl(); 164 choiceButton.hide(); 165 } 166 167 }