// Copyright 2007 Google Inc. All Rights Reserved. // // 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.google.scrollview.events; import com.google.scrollview.ScrollView; import com.google.scrollview.events.SVEvent; import com.google.scrollview.events.SVEventType; import com.google.scrollview.ui.SVWindow; import org.piccolo2d.PCamera; import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; import org.piccolo2d.nodes.PPath; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.Timer; /** * The ScrollViewEventHandler takes care of any events which might happen on the * canvas and converts them to an according SVEvent, which is (using the * processEvent method) then added to a message queue. All events from the * message queue get sent gradually * * @author wanke@google.com */ public class SVEventHandler extends PBasicInputEventHandler implements ActionListener, KeyListener, WindowListener { /** Necessary to wait for a defined period of time (for SVET_HOVER). */ public Timer timer; /** The window which the event corresponds to. */ private SVWindow svWindow; /** These are used to determine a selection size (for SVET_SELECTION). */ private int lastX = 0; private int lastY = 0; /** * These are used in case we want to transmit our position, but do not get it * because it was no MouseEvent, in particular SVET_HOVER and SVET_INPUT. */ private int lastXMove = 0; private int lastYMove = 0; /** For Drawing a rubber-band rectangle for selection */ private int startX = 0; private int startY = 0; private float rubberBandTransparency = 0.5f; private PNode selection = null; /** The string entered since the last enter. Since the client * end eats all newlines, we can't use the newline * character, so use ! for now, as it cannot be entered * directly anyway and therefore can never show up for real. */ private String keyStr = "!"; /** Setup the timer. */ public SVEventHandler(SVWindow wdw) { timer = new Timer(1000, this); svWindow = wdw; } /** * Store the newest x,y values, add the message to the queue and restart the * timer. */ private void processEvent(SVEvent e) { lastXMove = e.x; lastYMove = e.y; ScrollView.addMessage(e); timer.restart(); } /** Show the associated popup menu at (x,y) (relative position of the window). */ private void showPopup(PInputEvent e) { double x = e.getCanvasPosition().getX(); double y = e.getCanvasPosition().getY(); if (svWindow.svPuMenu != null) { svWindow.svPuMenu.show(svWindow, (int) x, (int) y); } } /** The mouse is clicked - create an SVET_CLICK event. */ @Override public void mouseClicked(PInputEvent e) { if (e.isPopupTrigger()) { showPopup(e); } else { processEvent(new SVEvent(SVEventType.SVET_CLICK, svWindow, (int) e .getPosition().getX(), (int) e.getPosition().getY(), 0, 0, null)); } } /** * The mouse key is pressed (and keeps getting pressed). * Depending on the OS, show a popup menu (if the button pressed is associated * with popup menus, like the RMB under windows&linux) or otherwise save the * position (in case it is a selection). */ @Override public void mousePressed(PInputEvent e) { if (e.isPopupTrigger()) { showPopup(e); } else { lastX = (int) e.getPosition().getX(); lastY = (int) e.getPosition().getY(); timer.restart(); } } /** The mouse is getting dragged - create an SVET_MOUSE event. */ @Override public void mouseDragged(PInputEvent e) { processEvent(new SVEvent(SVEventType.SVET_MOUSE, svWindow, (int) e .getPosition().getX(), (int) e.getPosition().getY(), (int) e .getPosition().getX() - lastX, (int) e.getPosition().getY() - lastY, null)); // Paint a selection rectangle. if (selection == null) { startX = (int) e.getPosition().getX(); startY = (int) e.getPosition().getY(); selection = PPath.createRectangle(startX, startY, 1, 1); selection.setTransparency(rubberBandTransparency); svWindow.canvas.getLayer().addChild(selection); } else { int right = Math.max(startX, (int) e.getPosition().getX()); int left = Math.min(startX, (int) e.getPosition().getX()); int bottom = Math.max(startY, (int) e.getPosition().getY()); int top = Math.min(startY, (int) e.getPosition().getY()); svWindow.canvas.getLayer().removeChild(selection); selection = PPath.createRectangle(left, top, right - left, bottom - top); selection.setPaint(Color.YELLOW); selection.setTransparency(rubberBandTransparency); svWindow.canvas.getLayer().addChild(selection); } } /** * The mouse was released. * Depending on the OS, show a popup menu (if the button pressed is associated * with popup menus, like the RMB under windows&linux) or otherwise create an * SVET_SELECTION event. */ @Override public void mouseReleased(PInputEvent e) { if (e.isPopupTrigger()) { showPopup(e); } else { processEvent(new SVEvent(SVEventType.SVET_SELECTION, svWindow, (int) e .getPosition().getX(), (int) e.getPosition().getY(), (int) e .getPosition().getX() - lastX, (int) e.getPosition().getY() - lastY, null)); } if (selection != null) { svWindow.canvas.getLayer().removeChild(selection); selection = null; } } /** * The mouse wheel is used to zoom in and out of the viewport and center on * the (x,y) position the mouse is currently on. */ @Override public void mouseWheelRotated(PInputEvent e) { PCamera lc = svWindow.canvas.getCamera(); double sf = SVWindow.SCALING_FACTOR; if (e.getWheelRotation() < 0) { sf = 1 / sf; } lc.scaleViewAboutPoint(lc.getScale() / sf, e.getPosition().getX(), e .getPosition().getY()); } /** * The mouse was moved - create an SVET_MOTION event. NOTE: This obviously * creates a lot of traffic and, depending on the type of application, could * quite possibly be disabled. */ @Override public void mouseMoved(PInputEvent e) { processEvent(new SVEvent(SVEventType.SVET_MOTION, svWindow, (int) e .getPosition().getX(), (int) e.getPosition().getY(), 0, 0, null)); } /** * The mouse entered the window. * Start the timer, which will then emit SVET_HOVER events every X ms. */ @Override public void mouseEntered(PInputEvent e) { timer.restart(); } /** * The mouse exited the window * Stop the timer, so no more SVET_HOVER events will emit. */ @Override public void mouseExited(PInputEvent e) { timer.stop(); } /** * The only associated object with this is the timer, so we use it to send a * SVET_HOVER event. */ public void actionPerformed(ActionEvent e) { processEvent(new SVEvent(SVEventType.SVET_HOVER, svWindow, lastXMove, lastYMove, 0, 0, null)); } /** * A key was pressed - create an SVET_INPUT event. * * NOTE: Might be useful to specify hotkeys. * * Implementation note: The keyListener provided by Piccolo seems to be * broken, so we use the AWT listener directly. * There are never any keyTyped events received either so we are * stuck with physical keys, which is very ugly. */ public void keyPressed(KeyEvent e) { char keyCh = e.getKeyChar(); if (keyCh == '\r' || keyCh == '\n' || keyCh == '\0' || keyCh == '?') { processEvent(new SVEvent(SVEventType.SVET_INPUT, svWindow, lastXMove, lastYMove, 0, 0, keyStr)); // Send newline characters as '!' as '!' can never be a keypressed // and the client eats all newline characters. keyStr = "!"; } else { processEvent(new SVEvent(SVEventType.SVET_INPUT, svWindow, lastXMove, lastYMove, 0, 0, String.valueOf(keyCh))); keyStr += keyCh; } } /** * A window is closed (by the 'x') - create an SVET_DESTROY event. If it was * the last open Window, also send an SVET_EXIT event (but do not exit unless * the client says so). */ public void windowClosing(WindowEvent e) { processEvent(new SVEvent(SVEventType.SVET_DESTROY, svWindow, lastXMove, lastYMove, 0, 0, null)); e.getWindow().dispose(); SVWindow.nrWindows--; if (SVWindow.nrWindows == 0) { processEvent(new SVEvent(SVEventType.SVET_EXIT, svWindow, lastXMove, lastYMove, 0, 0, null)); } } /** These are all events we do not care about and throw away */ public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } }