001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui.cursortool;
033:
034: import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
035: import com.vividsolutions.jump.workbench.ui.WorkbenchFrame;
036: import com.vividsolutions.jump.workbench.ui.zoom.PanTool;
037: import com.vividsolutions.jump.workbench.ui.zoom.ZoomTool;
038:
039: import java.awt.Cursor;
040: import java.awt.event.*;
041: import java.awt.event.KeyEvent;
042: import java.awt.event.KeyListener;
043: import java.awt.event.MouseEvent;
044: import java.awt.event.WindowAdapter;
045:
046: import java.util.HashMap;
047:
048: import javax.swing.SwingUtilities;
049:
050: /**
051: * Delegates to different CursorTools depending on whether various modifier
052: * keys are pressed (Ctrl, Shift, Alt). The term "quasimode" refers to a mode that
053: * is only in existence as long as a key is held down -- the mode vanishes as soon
054: * as the key is released. For more information, see the book "Humane Interfaces"
055: * by Jef Raskin.
056: */
057: public class QuasimodeTool extends DelegatingTool {
058:
059: private boolean altKeyDown = false;
060: private boolean mouseDown = false;
061:
062: //Sometimes when I try to use the Alt quasimode, the cursor becomes the default
063: //cursor (arrow) and stays that way. This seems to have been fixed in JDK 1.4,
064: //in which the default cursor stays only for a split second. [Jon Aquino]
065:
066: public QuasimodeTool(CursorTool defaultTool) {
067: super (defaultTool);
068: add(new ModifierKeySpec(false, false, false), defaultTool);
069: cursor = defaultTool.getCursor();
070: }
071:
072: private CursorTool getDefaultTool() {
073: return (CursorTool) keySpecToToolMap.get(new ModifierKeySpec(
074: false, false, false));
075: }
076:
077: public Cursor getCursor() {
078: return cursor;
079: }
080:
081: private Cursor cursor;
082:
083: private CursorTool getTool(KeyEvent e) {
084: CursorTool tool = (CursorTool) keySpecToToolMap
085: .get(new ModifierKeySpec(e.isControlDown(), e
086: .isShiftDown(), e.isAltDown() || e.isMetaDown()));
087: return tool != null ? tool : getDefaultTool();
088: }
089:
090: private KeyListener keyListener = new KeyListener() {
091: public void keyTyped(KeyEvent e) {
092: }
093:
094: public void keyPressed(KeyEvent e) {
095: keyStateChanged(e);
096: }
097:
098: public void keyReleased(KeyEvent e) {
099: keyStateChanged(e);
100: }
101:
102: private void keyStateChanged(KeyEvent e) {
103: altKeyDown = e.isAltDown();
104: setTool(e);
105: }
106: };
107: /*
108: * [sstein: 17.Mar2007] added the Listener, to be able to remove the listener
109: * if the cursortool is deactivated. Otherwise the listener still exists and
110: * the mousepointer is "flickering" through all previously used mouse-tools
111: * The modifications (see also #activate and #deactivate) are proposed by Bob and Larry
112: */
113: private WindowAdapter windowListener = new WindowAdapter() {
114: public void windowActivated(WindowEvent e) {
115: super .windowActivated(e);
116: setTool(new KeyEvent(panel, KeyEvent.KEY_PRESSED, 0, 0,
117: KeyEvent.VK_UNDEFINED, KeyEvent.CHAR_UNDEFINED));
118: }
119: };
120:
121: private void setTool(KeyEvent e) {
122: if (!mouseDown) {
123: cursor = getTool(e).getCursor();
124: panel.setCursor(cursor);
125: currentKeyEvent = e;
126: setDelegate(getTool(e));
127: }
128:
129: // if the following code is used then the tool gets set during the mouse pressed event
130: // if (getDelegate().isGestureInProgress()
131: // && getDelegate() != getTool(e)
132: // && getDelegate() != getDefaultTool())
133: // {
134: // setDelegate(getDefaultTool());
135: // }
136: }
137:
138: private KeyEvent currentKeyEvent = null;
139:
140: private LayerViewPanel panel;
141: private WorkbenchFrame frame;
142:
143: public void activate(final LayerViewPanel panel) {
144: super .activate(panel);
145: this .panel = panel;
146: //Cache WorkbenchFrame because in JDK 1.3 when I minimize an internal
147: //frame, SwingUtilities#windowForComponent returns null for that frame.
148: //A Swing bug. [Jon Aquino]
149: frame = AbstractCursorTool.workbenchFrame(panel);
150: if (frame != null) {
151: frame.addEasyKeyListener(keyListener);
152: //Workaround for the following:
153: // * Use WorkbenchFrame#addKeyboardShortcut for a plug-in that
154: // pops up a dialog (or could pop up an error dialog). Assign it to Ctrl-A,
155: // for example.
156: // * Press Ctrl-A. The Ctrl quasimode happens. But also the dialog pops up.
157: // * Release Ctrl. Close the dialog. Note that the cursor shows we're still
158: // in the Ctrl quasimode! This is because the dialog consumed the
159: // key-up event.
160: //So we're working around this by clearing the quasimode when the
161: //WorkbenchFrame is activated (e.g. when a dialog is closed). [Jon Aquino]
162:
163: //-- [sstein : ] deactivated and repalced by line above see comment
164: // on windowListener above
165: frame.addWindowListener(windowListener);
166: /*frame.addWindowListener(new WindowAdapter() {
167: public void windowActivated(WindowEvent e) {
168: super.windowActivated(e);
169: setTool(new KeyEvent(panel, KeyEvent.KEY_PRESSED,
170: 0, 0, KeyEvent.VK_UNDEFINED, KeyEvent.CHAR_UNDEFINED));
171: }
172: });*/
173: }
174: }
175:
176: public void mousePressed(MouseEvent e) {
177: // setDelegate(currentKeyEvent != null ? getTool(currentKeyEvent) : getDefaultTool());
178: super .mousePressed(e);
179: mouseDown = true;
180: }
181:
182: public void mouseReleased(MouseEvent e) {
183: super .mouseReleased(e);
184: mouseDown = false;
185: }
186:
187: public void deactivate() {
188: if (!altKeyDown) {
189: super .deactivate();
190: if (frame != null) {
191: frame.removeEasyKeyListener(keyListener);
192: frame.removeWindowListener(windowListener);
193: }
194: }
195: }
196:
197: private HashMap keySpecToToolMap = new HashMap();
198:
199: public QuasimodeTool add(ModifierKeySpec keySpec, CursorTool tool) {
200: if (keySpecToToolMap.containsKey(keySpec)) {
201: return this ;
202: }
203: keySpecToToolMap.put(keySpec, tool != null ? (tool
204: .isRightMouseButtonUsed() ? tool : new LeftClickFilter(
205: tool)) : null);
206: return this ;
207: }
208:
209: public static class ModifierKeySpec {
210: public ModifierKeySpec(boolean needsControl,
211: boolean needsShift, boolean needsAltOrMeta) {
212: this .needsControl = needsControl;
213: this .needsShift = needsShift;
214: this .needsAltOrMeta = needsAltOrMeta;
215: }
216:
217: private boolean needsShift, needsAltOrMeta, needsControl;
218:
219: public int hashCode() {
220: //Map will be small anyway. [Jon Aquino]
221: return 0;
222: }
223:
224: public boolean equals(Object obj) {
225: if (!(obj instanceof ModifierKeySpec)) {
226: return false;
227: }
228: ModifierKeySpec other = (ModifierKeySpec) obj;
229: return needsControl == other.needsControl
230: && needsShift == other.needsShift
231: && needsAltOrMeta == other.needsAltOrMeta;
232: }
233: }
234:
235: public static QuasimodeTool addStandardQuasimodes(CursorTool tool) {
236: QuasimodeTool quasimodeTool = tool instanceof QuasimodeTool ? (QuasimodeTool) tool
237: : new QuasimodeTool(tool);
238: quasimodeTool.add(new ModifierKeySpec(false, false, true),
239: new ZoomTool());
240: quasimodeTool.add(new ModifierKeySpec(false, true, true),
241: new PanTool());
242: SelectFeaturesTool selectFeaturesTool = new SelectFeaturesTool() {
243: protected boolean selectedLayersOnly() {
244: return false;
245: }
246: };
247: quasimodeTool.add(new ModifierKeySpec(true, false, false),
248: selectFeaturesTool);
249: quasimodeTool.add(new ModifierKeySpec(true, true, false),
250: selectFeaturesTool);
251: quasimodeTool.add(new ModifierKeySpec(true, false, true),
252: new FeatureInfoTool());
253: return quasimodeTool;
254: }
255:
256: }
|