/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.googlecode.gwtquake.client; import com.google.gwt.core.client.Duration; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style.Cursor; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.googlecode.gwtquake.shared.client.Key; import com.googlecode.gwtquake.shared.client.Keys; import com.googlecode.gwtquake.shared.common.Globals; import com.googlecode.gwtquake.shared.sys.IN; import com.googlecode.gwtquake.shared.sys.KBD; import com.googlecode.gwtquake.shared.sys.Timer; public class GwtKBD extends KBD { /** * Show a brighter cursor for the outer quarters of the screen, * to serve as a reminder to recenter. */ private static final boolean RECENTER_REMINDER = true; /** * Mouse position sets a rotation speed instead. */ private static final boolean AUTOROTATE = false; /** * Use the left mouse key to drag the view, instead of capturing * automatically. */ private static final boolean LEFT_MOUSE_DRAG = false; private static final double SCALE = 10.0; private static final double AUTOROTATE_SCALE = SCALE; private static final int MAX_CLICK_TIME = 333; private static double normalX; private static int lastCmx; private static int lastCmy; private static boolean captureMove; public static void Frame(int ms) { if (AUTOROTATE && captureMove) { mx += (int) ms * normalX * AUTOROTATE_SCALE; } } public double mouseDownTime; public double mouseUpTime; private boolean hasMeta(NativeEvent nevt) { return nevt.getAltKey() || nevt.getMetaKey() || nevt.getCtrlKey(); } private class Handler implements NativePreviewHandler { public void onPreviewNativeEvent(NativePreviewEvent event) { NativeEvent nevt = event.getNativeEvent(); if ("keydown".equals(nevt.getType())) { Do_Key_Event(XLateKey(nevt.getKeyCode()), true); if (!hasMeta(nevt)) { nevt.preventDefault(); } } else if ("keyup".equals(nevt.getType())) { Do_Key_Event(XLateKey(nevt.getKeyCode()), false); if (!hasMeta(nevt)) { nevt.preventDefault(); } } else if (!IN.mouse_active) { stopCapturingMouse(); } else { if ("mousemove".equals(nevt.getType())) { int cmx = nevt.getClientX(); int cmy = nevt.getClientY(); double cx = Globals.viddef.width / 2; normalX = (cmx - cx) / cx; if (captureMove) { if (RECENTER_REMINDER) { if (Math.abs(normalX) > 0.5) { GwtQuake.canvas.getStyle().setCursor(Cursor.MOVE); } else { GwtQuake.canvas.getStyle().setCursor(Cursor.CROSSHAIR); } } if (!AUTOROTATE) { mx += (cmx - lastCmx) * SCALE; my += (cmy - lastCmy) * SCALE; lastCmx = cmx; lastCmy = cmy; } } nevt.preventDefault(); } else if("mousedown".equals(nevt.getType())) { boolean ignoreClick = false; if (nevt.getButton() == NativeEvent.BUTTON_RIGHT) { stopCapturingMouse(); } else if (nevt.getButton() == NativeEvent.BUTTON_LEFT) { mouseDownTime = Duration.currentTimeMillis(); ignoreClick = startCapturingMouse(nevt); } int button = translateMouseButton(nevt); if (!ignoreClick && (!LEFT_MOUSE_DRAG || mouseDownTime - mouseUpTime < MAX_CLICK_TIME)) { Do_Key_Event(button, true); } } else if("mouseup".equals(nevt.getType())) { int button = translateMouseButton(nevt); if (LEFT_MOUSE_DRAG && nevt.getButton() == NativeEvent.BUTTON_LEFT) { stopCapturingMouse(); if (Duration.currentTimeMillis() - mouseDownTime < MAX_CLICK_TIME) { mouseUpTime = Duration.currentTimeMillis(); Do_Key_Event(button, true); } } else { startCapturingMouse(nevt); } Do_Key_Event(button, false); } else if("mousewheel".equals(nevt.getType())) { Do_Key_Event(nevt.getMouseWheelVelocityY() < 0 ? Keys.K_MWHEELUP : Keys.K_MWHEELDOWN, true); Do_Key_Event(nevt.getMouseWheelVelocityY() < 0 ? Keys.K_MWHEELUP : Keys.K_MWHEELDOWN, false); nevt.preventDefault(); nevt.stopPropagation(); } else if("contextmenu".equals(nevt.getType())) { // try to stop that pesky menu on right button, for some reason, intercepting it on mousedown/up doesn't work nevt.preventDefault(); nevt.stopPropagation(); } } } } private void stopCapturingMouse() { captureMove = false; GwtQuake.canvas.getStyle().setCursor(Cursor.DEFAULT); } private boolean startCapturingMouse(NativeEvent nevt) { if (captureMove) { return false; } captureMove = true; GwtQuake.canvas.getStyle().setProperty("cursor", "none"); lastCmx = nevt.getClientX(); lastCmy = nevt.getClientY(); return true; } private static int translateMouseButton(NativeEvent evt) { switch(evt.getButton()) { case NativeEvent.BUTTON_LEFT: return Keys.K_MOUSE1; case NativeEvent.BUTTON_RIGHT: return Keys.K_MOUSE2; default: return Keys.K_MOUSE3; } } @Override public void Close() { } @Override public void Do_Key_Event(int key, boolean down) { Key.Event(key, down, Timer.Milliseconds()); } @Override public void Init() { Event.addNativePreviewHandler(new Handler()); } @Override public void Update() { } @Override public void installGrabs() { } @Override public void uninstallGrabs() { } private int XLateKey(int key) { switch(key) { case KeyCodes.KEY_PAGEUP: key = Keys.K_PGUP; break; case KeyCodes.KEY_PAGEDOWN: key = Keys.K_PGDN; break; case KeyCodes.KEY_HOME: key = Keys.K_HOME; break; case KeyCodes.KEY_END: key = Keys.K_END; break; case KeyCodes.KEY_LEFT: key = Keys.K_LEFTARROW; break; case KeyCodes.KEY_RIGHT: key = Keys.K_RIGHTARROW; break; case KeyCodes.KEY_DOWN: key = Keys.K_DOWNARROW; break; case KeyCodes.KEY_UP: key = Keys.K_UPARROW; break; case KeyCodes.KEY_ESCAPE: key = Keys.K_ESCAPE; break; case KeyCodes.KEY_ENTER: key = Keys.K_ENTER; break; case KeyCodes.KEY_TAB: key = Keys.K_TAB; break; case KeyCodes.KEY_BACKSPACE: key = Keys.K_BACKSPACE; break; case KeyCodes.KEY_DELETE: key = Keys.K_DEL; break; case KeyCodes.KEY_SHIFT: key = Keys.K_SHIFT; break; case KeyCodes.KEY_CTRL: key = Keys.K_CTRL; break; // Safari on MAC (TODO(jgw): other browsers may need tweaking): case 112: key = Keys.K_F1;break; case 113: key = Keys.K_F2;break; case 114: key = Keys.K_F3;break; case 115: key = Keys.K_F4;break; case 116: key = Keys.K_F5;break; case 117: key = Keys.K_F6;break; case 118: key = Keys.K_F7;break; case 119: key = Keys.K_F8;break; case 120: key = Keys.K_F9;break; case 121: key = Keys.K_F10;break; case 122: key = Keys.K_F11;break; case 123: key = Keys.K_F12;break; case 186: key = ';'; break; case 187: key = '='; break; case 188: key = ','; break; case 189: key = '-'; break; case 190: key = '.'; break; case 191: key = '/'; break; case 192: key = '`'; break; case 222: key = '\''; break; case 219: key = '['; break; case 220: key = '\\'; break; case 221: key = ']'; break; default: if (key < '0' || key > '9') { if (key >= 'A' && key <= 'Z') { key = Character.toLowerCase((char) key); } } // TODO(jgw): We probably need keycodes for these. // case KeyCodes.KEY_PAUSE: key = Key.K_PAUSE; break; // case KeyCodes.KEY_MENU: key = Key.K_ALT; break; // case KeyCodes.KEY_INSERT: key = Key.K_INS; break; } return key; } }