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 com.sun.javafx.util.Utils; 29 import static com.sun.javafx.scene.control.behavior.OptionalBoolean.ANY; 30 import static com.sun.javafx.scene.control.behavior.OptionalBoolean.FALSE; 31 import static com.sun.javafx.scene.control.behavior.OptionalBoolean.TRUE; 32 import com.sun.javafx.tk.Toolkit; 33 import javafx.event.EventType; 34 import javafx.scene.control.Control; 35 import javafx.scene.input.KeyCode; 36 import javafx.scene.input.KeyEvent; 37 38 /** 39 * KeyBindings are used to describe which action should occur based on some 40 * KeyEvent state and Control state. These bindings are used to populate the 41 * keyBindings variable on BehaviorBase. The KeyBinding can be subclassed to 42 * add additional matching criteria. A match in a subclass should always have 43 * a specificity that is 1 greater than its superclass in the case of a match, 44 * or 0 in the case where there is no match. 45 * 46 * Note that this API is, at present, quite odd in that you use a constructor 47 * and then use shift(), ctrl(), alt(), or meta() separately. It gave me an 48 * object-literal like approach but isn't ideal. We will want some builder 49 * approach here (similar as in other places). 50 */ 51 public class KeyBinding { 52 private KeyCode code; 53 private EventType<KeyEvent> eventType = KeyEvent.KEY_PRESSED; 54 private String action; 55 private OptionalBoolean shift = FALSE; 56 private OptionalBoolean ctrl = FALSE; 57 private OptionalBoolean alt = FALSE; 58 private OptionalBoolean meta = FALSE; 59 60 public KeyBinding(KeyCode code, String action) { 61 this.code = code; 62 this.action = action; 63 } 64 65 public KeyBinding(KeyCode code, EventType<KeyEvent> type, String action) { 66 this.code = code; 67 this.eventType = type; 68 this.action = action; 69 } 70 71 public KeyBinding shift() { 72 return shift(TRUE); 73 } 74 75 public KeyBinding shift(OptionalBoolean value) { 76 shift = value; 77 return this; 78 } 79 80 public KeyBinding ctrl() { 81 return ctrl(TRUE); 82 } 83 84 public KeyBinding ctrl(OptionalBoolean value) { 85 ctrl = value; 86 return this; 87 } 88 89 public KeyBinding alt() { 90 return alt(TRUE); 91 } 92 93 public KeyBinding alt(OptionalBoolean value) { 94 alt = value; 95 return this; 96 } 97 98 public KeyBinding meta() { 99 return meta(TRUE); 100 } 101 102 public KeyBinding meta(OptionalBoolean value) { 103 meta = value; 104 return this; 105 } 106 107 public KeyBinding shortcut() { 108 if (Toolkit.getToolkit().getClass().getName().endsWith("StubToolkit")) { 109 // FIXME: We've hit the terrible StubToolkit (which only appears 110 // during testing). We will dumb down what we do here 111 if (Utils.isMac()) { 112 return meta(); 113 } else { 114 return ctrl(); 115 } 116 } else { 117 switch (Toolkit.getToolkit().getPlatformShortcutKey()) { 118 case SHIFT: 119 return shift(); 120 121 case CONTROL: 122 return ctrl(); 123 124 case ALT: 125 return alt(); 126 127 case META: 128 return meta(); 129 130 default: 131 return this; 132 } 133 } 134 } 135 136 public final KeyCode getCode() { return code; } 137 public final EventType<KeyEvent> getType() { return eventType; } 138 public final String getAction() { return action; } 139 public final OptionalBoolean getShift() { return shift; } 140 public final OptionalBoolean getCtrl() { return ctrl; } 141 public final OptionalBoolean getAlt() { return alt; } 142 public final OptionalBoolean getMeta() { return meta; } 143 144 public int getSpecificity(Control control, KeyEvent event) { 145 int s = 0; 146 if (code != null && code != event.getCode()) return 0; else s = 1; 147 if (!shift.equals(event.isShiftDown())) return 0; else if (shift != ANY) s++; 148 if (!ctrl.equals(event.isControlDown())) return 0; else if (ctrl != ANY) s++; 149 if (!alt.equals(event.isAltDown())) return 0; else if (alt != ANY) s++; 150 if (!meta.equals(event.isMetaDown())) return 0; else if (meta != ANY) s++; 151 if (eventType != null && eventType != event.getEventType()) return 0; else s++; 152 // We can now trivially accept it 153 return s; 154 } 155 156 @Override public String toString() { 157 return "KeyBinding [code=" + code + ", shift=" + shift + 158 ", ctrl=" + ctrl + ", alt=" + alt + 159 ", meta=" + meta + ", type=" + eventType + 160 ", action=" + action + "]"; 161 } 162 }