1 // Copyright 2007 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); You may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by 6 // applicable law or agreed to in writing, software distributed under the 7 // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 8 // OF ANY KIND, either express or implied. See the License for the specific 9 // language governing permissions and limitations under the License. 10 11 package com.google.scrollview.events; 12 13 import com.google.scrollview.ScrollView; 14 import com.google.scrollview.ui.SVWindow; 15 import com.google.scrollview.events.SVEvent; 16 import com.google.scrollview.events.SVEventType; 17 18 import java.awt.Color; 19 import java.awt.event.ActionEvent; 20 import java.awt.event.ActionListener; 21 import java.awt.event.KeyEvent; 22 import java.awt.event.KeyListener; 23 import java.awt.event.WindowEvent; 24 import java.awt.event.WindowListener; 25 26 import javax.swing.Timer; 27 28 import edu.umd.cs.piccolo.PCamera; 29 import edu.umd.cs.piccolo.PNode; 30 import edu.umd.cs.piccolo.event.PBasicInputEventHandler; 31 import edu.umd.cs.piccolo.event.PInputEvent; 32 import edu.umd.cs.piccolo.nodes.PPath; 33 34 /** 35 * The ScrollViewEventHandler takes care of any events which might happen on the 36 * canvas and converts them to an according SVEvent, which is (using the 37 * processEvent method) then added to a message queue. All events from the 38 * message queue get sent gradually 39 * 40 * @author wanke@google.com 41 */ 42 public class SVEventHandler extends PBasicInputEventHandler implements 43 ActionListener, KeyListener, WindowListener { 44 45 /** Necessary to wait for a defined period of time (for SVET_HOVER). */ 46 public Timer timer; 47 48 /** The window which the event corresponds to. */ 49 private SVWindow svWindow; 50 51 /** These are used to determine a selection size (for SVET_SELECTION). */ 52 private int lastX = 0; 53 private int lastY = 0; 54 55 /** 56 * These are used in case we want to transmit our position, but do not get it 57 * because it was no MouseEvent, in particular SVET_HOVER and SVET_INPUT. 58 */ 59 private int lastXMove = 0; 60 private int lastYMove = 0; 61 62 /** For Drawing a rubber-band rectangle for selection */ 63 private int startX = 0; 64 private int startY = 0; 65 private float rubberBandTransparency = 0.5f; 66 private PNode selection = null; 67 68 /** The string entered since the last enter. Since the client 69 * end eats all newlines, we can't use the newline 70 * character, so use ! for now, as it cannot be entered 71 * directly anyway and therefore can never show up for real. */ 72 private String keyStr = "!"; 73 74 /** Setup the timer. */ SVEventHandler(SVWindow wdw)75 public SVEventHandler(SVWindow wdw) { 76 timer = new Timer(1000, this); 77 svWindow = wdw; 78 } 79 80 /** 81 * Store the newest x,y values, add the message to the queue and restart the 82 * timer. 83 */ processEvent(SVEvent e)84 private void processEvent(SVEvent e) { 85 lastXMove = e.x; 86 lastYMove = e.y; 87 ScrollView.addMessage(e); 88 timer.restart(); 89 } 90 91 /** Show the associated popup menu at (x,y) (relative position of the window). */ showPopup(PInputEvent e)92 private void showPopup(PInputEvent e) { 93 double x = e.getCanvasPosition().getX(); 94 double y = e.getCanvasPosition().getY(); 95 96 if (svWindow.svPuMenu != null) { 97 svWindow.svPuMenu.show(svWindow, (int) x, (int) y); 98 } 99 } 100 101 102 /** The mouse is clicked - create an SVET_CLICK event. */ 103 @Override mouseClicked(PInputEvent e)104 public void mouseClicked(PInputEvent e) { 105 if (e.isPopupTrigger()) { 106 showPopup(e); 107 } else { 108 processEvent(new SVEvent(SVEventType.SVET_CLICK, svWindow, (int) e 109 .getPosition().getX(), (int) e.getPosition().getY(), 0, 0, null)); 110 } 111 } 112 113 /** 114 * The mouse key is pressed (and keeps getting pressed). 115 * Depending on the OS, show a popup menu (if the button pressed is associated 116 * with popup menus, like the RMB under windows&linux) or otherwise save the 117 * position (in case it is a selection). 118 */ 119 @Override mousePressed(PInputEvent e)120 public void mousePressed(PInputEvent e) { 121 if (e.isPopupTrigger()) { 122 showPopup(e); 123 } else { 124 lastX = (int) e.getPosition().getX(); 125 lastY = (int) e.getPosition().getY(); 126 timer.restart(); 127 } 128 } 129 130 /** The mouse is getting dragged - create an SVET_MOUSE event. */ 131 @Override mouseDragged(PInputEvent e)132 public void mouseDragged(PInputEvent e) { 133 processEvent(new SVEvent(SVEventType.SVET_MOUSE, svWindow, (int) e 134 .getPosition().getX(), (int) e.getPosition().getY(), (int) e 135 .getPosition().getX() 136 - lastX, (int) e.getPosition().getY() - lastY, null)); 137 138 // Paint a selection rectangle. 139 if (selection == null) { 140 startX = (int) e.getPosition().getX(); 141 startY = (int) e.getPosition().getY(); 142 selection = PPath.createRectangle(startX, startY, 1, 1); 143 selection.setTransparency(rubberBandTransparency); 144 svWindow.canvas.getLayer().addChild(selection); 145 } else { 146 int right = Math.max(startX, (int) e.getPosition().getX()); 147 int left = Math.min(startX, (int) e.getPosition().getX()); 148 int bottom = Math.max(startY, (int) e.getPosition().getY()); 149 int top = Math.min(startY, (int) e.getPosition().getY()); 150 svWindow.canvas.getLayer().removeChild(selection); 151 selection = PPath.createRectangle(left, top, right - left, bottom - top); 152 selection.setPaint(Color.YELLOW); 153 selection.setTransparency(rubberBandTransparency); 154 svWindow.canvas.getLayer().addChild(selection); 155 } 156 } 157 158 /** 159 * The mouse was released. 160 * Depending on the OS, show a popup menu (if the button pressed is associated 161 * with popup menus, like the RMB under windows&linux) or otherwise create an 162 * SVET_SELECTION event. 163 */ 164 @Override mouseReleased(PInputEvent e)165 public void mouseReleased(PInputEvent e) { 166 if (e.isPopupTrigger()) { 167 showPopup(e); 168 } else { 169 processEvent(new SVEvent(SVEventType.SVET_SELECTION, svWindow, (int) e 170 .getPosition().getX(), (int) e.getPosition().getY(), (int) e 171 .getPosition().getX() 172 - lastX, (int) e.getPosition().getY() - lastY, null)); 173 } 174 if (selection != null) { 175 svWindow.canvas.getLayer().removeChild(selection); 176 } 177 } 178 179 /** 180 * The mouse wheel is used to zoom in and out of the viewport and center on 181 * the (x,y) position the mouse is currently on. 182 */ 183 @Override mouseWheelRotated(PInputEvent e)184 public void mouseWheelRotated(PInputEvent e) { 185 PCamera lc = svWindow.canvas.getCamera(); 186 double sf = SVWindow.SCALING_FACTOR; 187 188 if (e.getWheelRotation() < 0) { 189 sf = 1 / sf; 190 } 191 lc.scaleViewAboutPoint(lc.getScale() / sf, e.getPosition().getX(), e 192 .getPosition().getY()); 193 } 194 195 /** 196 * The mouse was moved - create an SVET_MOTION event. NOTE: This obviously 197 * creates a lot of traffic and, depending on the type of application, could 198 * quite possibly be disabled. 199 */ 200 @Override mouseMoved(PInputEvent e)201 public void mouseMoved(PInputEvent e) { 202 processEvent(new SVEvent(SVEventType.SVET_MOTION, svWindow, (int) e 203 .getPosition().getX(), (int) e.getPosition().getY(), 0, 0, null)); 204 } 205 206 /** 207 * The mouse entered the window. 208 * Start the timer, which will then emit SVET_HOVER events every X ms. */ 209 @Override mouseEntered(PInputEvent e)210 public void mouseEntered(PInputEvent e) { 211 timer.restart(); 212 } 213 214 /** 215 * The mouse exited the window 216 * Stop the timer, so no more SVET_HOVER events will emit. */ 217 @Override mouseExited(PInputEvent e)218 public void mouseExited(PInputEvent e) { 219 timer.stop(); 220 } 221 222 /** 223 * The only associated object with this is the timer, so we use it to send a 224 * SVET_HOVER event. 225 */ actionPerformed(ActionEvent e)226 public void actionPerformed(ActionEvent e) { 227 processEvent(new SVEvent(SVEventType.SVET_HOVER, svWindow, lastXMove, 228 lastYMove, 0, 0, null)); 229 } 230 231 /** 232 * A key was pressed - create an SVET_INPUT event. 233 * 234 * NOTE: Might be useful to specify hotkeys. 235 * 236 * Implementation note: The keyListener provided by Piccolo seems to be 237 * broken, so we use the AWT listener directly. 238 * There are never any keyTyped events received either so we are 239 * stuck with physical keys, which is very ugly. 240 */ keyPressed(KeyEvent e)241 public void keyPressed(KeyEvent e) { 242 char keyCh = e.getKeyChar(); 243 if (keyCh == '\r' || keyCh == '\n' || keyCh == '\0' || keyCh == '?') { 244 processEvent(new SVEvent(SVEventType.SVET_INPUT, svWindow, lastXMove, 245 lastYMove, 0, 0, keyStr)); 246 // Send newline characters as '!' as '!' can never be a keypressed 247 // and the client eats all newline characters. 248 keyStr = "!"; 249 } else { 250 processEvent(new SVEvent(SVEventType.SVET_INPUT, svWindow, lastXMove, 251 lastYMove, 0, 0, String.valueOf(keyCh))); 252 keyStr += keyCh; 253 } 254 } 255 256 /** 257 * A window is closed (by the 'x') - create an SVET_DESTROY event. If it was 258 * the last open Window, also send an SVET_EXIT event (but do not exit unless 259 * the client says so). 260 */ windowClosing(WindowEvent e)261 public void windowClosing(WindowEvent e) { 262 processEvent(new SVEvent(SVEventType.SVET_DESTROY, svWindow, lastXMove, 263 lastYMove, 0, 0, null)); 264 e.getWindow().dispose(); 265 SVWindow.nrWindows--; 266 if (SVWindow.nrWindows == 0) { 267 processEvent(new SVEvent(SVEventType.SVET_EXIT, svWindow, lastXMove, 268 lastYMove, 0, 0, null)); 269 } 270 } 271 272 /** These are all events we do not care about and throw away */ keyReleased(KeyEvent e)273 public void keyReleased(KeyEvent e) { 274 } 275 keyTyped(KeyEvent e)276 public void keyTyped(KeyEvent e) { 277 } 278 windowActivated(WindowEvent e)279 public void windowActivated(WindowEvent e) { 280 } 281 windowClosed(WindowEvent e)282 public void windowClosed(WindowEvent e) { 283 } 284 windowDeactivated(WindowEvent e)285 public void windowDeactivated(WindowEvent e) { 286 } 287 windowDeiconified(WindowEvent e)288 public void windowDeiconified(WindowEvent e) { 289 } 290 windowIconified(WindowEvent e)291 public void windowIconified(WindowEvent e) { 292 } 293 windowOpened(WindowEvent e)294 public void windowOpened(WindowEvent e) { 295 } 296 } 297