/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* Copyright (C) 2003 Vivid Solutions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.vividsolutions.jump.workbench.ui.cursortool;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.WorkbenchFrame;
import com.vividsolutions.jump.workbench.ui.zoom.PanTool;
import com.vividsolutions.jump.workbench.ui.zoom.ZoomTool;
import java.awt.Cursor;
import java.awt.event.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.util.HashMap;
import javax.swing.SwingUtilities;
/**
* Delegates to different CursorTools depending on whether various modifier
* keys are pressed (Ctrl, Shift, Alt). The term "quasimode" refers to a mode that
* is only in existence as long as a key is held down -- the mode vanishes as soon
* as the key is released. For more information, see the book "Humane Interfaces"
* by Jef Raskin.
*/
public class QuasimodeTool extends DelegatingTool {
private boolean altKeyDown = false;
private boolean mouseDown = false;
//Sometimes when I try to use the Alt quasimode, the cursor becomes the default
//cursor (arrow) and stays that way. This seems to have been fixed in JDK 1.4,
//in which the default cursor stays only for a split second. [Jon Aquino]
public QuasimodeTool(CursorTool defaultTool) {
super(defaultTool);
add(new ModifierKeySpec(false, false, false), defaultTool);
cursor = defaultTool.getCursor();
}
private CursorTool getDefaultTool() {
return (CursorTool) keySpecToToolMap.get(new ModifierKeySpec(false, false, false));
}
public Cursor getCursor() {
return cursor;
}
private Cursor cursor;
private CursorTool getTool(KeyEvent e) {
CursorTool tool =
(CursorTool) keySpecToToolMap.get(
new ModifierKeySpec(
e.isControlDown(),
e.isShiftDown(),
e.isAltDown() || e.isMetaDown()));
return tool != null ? tool : getDefaultTool();
}
private KeyListener keyListener = new KeyListener() {
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
keyStateChanged(e);
}
public void keyReleased(KeyEvent e) {
keyStateChanged(e);
}
private void keyStateChanged(KeyEvent e) {
altKeyDown = e.isAltDown();
setTool(e);
}
};
/*
* [sstein: 17.Mar2007] added the Listener, to be able to remove the listener
* if the cursortool is deactivated. Otherwise the listener still exists and
* the mousepointer is "flickering" through all previously used mouse-tools
* The modifications (see also #activate and #deactivate) are proposed by Bob and Larry
*/
private WindowAdapter windowListener = new WindowAdapter()
{
public void windowActivated(WindowEvent e) {
super.windowActivated(e);
setTool(new KeyEvent(panel, KeyEvent.KEY_PRESSED,
0, 0, KeyEvent.VK_UNDEFINED, KeyEvent.CHAR_UNDEFINED));
}
};
private void setTool(KeyEvent e) {
if (!mouseDown)
{
cursor = getTool(e).getCursor();
panel.setCursor(cursor);
currentKeyEvent = e;
setDelegate(getTool(e));
}
// if the following code is used then the tool gets set during the mouse pressed event
// if (getDelegate().isGestureInProgress()
// && getDelegate() != getTool(e)
// && getDelegate() != getDefaultTool())
// {
// setDelegate(getDefaultTool());
// }
}
private KeyEvent currentKeyEvent = null;
private LayerViewPanel panel;
private WorkbenchFrame frame;
public void activate(final LayerViewPanel panel) {
super.activate(panel);
this.panel = panel;
//Cache WorkbenchFrame because in JDK 1.3 when I minimize an internal
//frame, SwingUtilities#windowForComponent returns null for that frame.
//A Swing bug. [Jon Aquino]
frame = AbstractCursorTool.workbenchFrame(panel);
if (frame != null) {
frame.addEasyKeyListener(keyListener);
//Workaround for the following:
// * Use WorkbenchFrame#addKeyboardShortcut for a plug-in that
// pops up a dialog (or could pop up an error dialog). Assign it to Ctrl-A,
// for example.
// * Press Ctrl-A. The Ctrl quasimode happens. But also the dialog pops up.
// * Release Ctrl. Close the dialog. Note that the cursor shows we're still
// in the Ctrl quasimode! This is because the dialog consumed the
// key-up event.
//So we're working around this by clearing the quasimode when the
//WorkbenchFrame is activated (e.g. when a dialog is closed). [Jon Aquino]
//-- [sstein : ] deactivated and repalced by line above see comment
// on windowListener above
frame.addWindowListener(windowListener);
//Need to do the following so that the delegate gets set to the LeftClickFilter
//This fixes a problem where the selection was being lost on a right click
//when using the selector tool on the tool bar.
//The following code is executed during windowActivated event in WindowAdapter windowListener above.
//This is why the problem fixed itself when returning to the window after bringing
//another application forward.
//This event was done when clicking the selection tool on the toolbox so
//that is why it was always working.
setTool(new KeyEvent(panel, KeyEvent.KEY_PRESSED,
0, 0, KeyEvent.VK_UNDEFINED, KeyEvent.CHAR_UNDEFINED));
/*frame.addWindowListener(new WindowAdapter() {
public void windowActivated(WindowEvent e) {
super.windowActivated(e);
setTool(new KeyEvent(panel, KeyEvent.KEY_PRESSED,
0, 0, KeyEvent.VK_UNDEFINED, KeyEvent.CHAR_UNDEFINED));
}
});*/
}
}
public void mousePressed(MouseEvent e) {
// setDelegate(currentKeyEvent != null ? getTool(currentKeyEvent) : getDefaultTool());
super.mousePressed(e);
mouseDown = true;
}
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
mouseDown = false;
}
public void deactivate() {
if (!altKeyDown)
{
super.deactivate();
if (frame != null) {
frame.removeEasyKeyListener(keyListener);
frame.removeWindowListener(windowListener);
}
}
}
private HashMap keySpecToToolMap = new HashMap();
public QuasimodeTool add(ModifierKeySpec keySpec, CursorTool tool) {
if (keySpecToToolMap.containsKey(keySpec)) {
return this;
}
keySpecToToolMap.put(
keySpec,
tool != null
? (tool.isRightMouseButtonUsed() ? tool : new LeftClickFilter(tool))
: null);
return this;
}
public static class ModifierKeySpec {
public ModifierKeySpec(boolean needsControl, boolean needsShift, boolean needsAltOrMeta) {
this.needsControl = needsControl;
this.needsShift = needsShift;
this.needsAltOrMeta = needsAltOrMeta;
}
private boolean needsShift, needsAltOrMeta, needsControl;
public int hashCode() {
//Map will be small anyway. [Jon Aquino]
return 0;
}
public boolean equals(Object obj) {
if (!(obj instanceof ModifierKeySpec)) {
return false;
}
ModifierKeySpec other = (ModifierKeySpec) obj;
return needsControl == other.needsControl
&& needsShift == other.needsShift
&& needsAltOrMeta == other.needsAltOrMeta;
}
}
public static QuasimodeTool addStandardQuasimodes(CursorTool tool) {
QuasimodeTool quasimodeTool =
tool instanceof QuasimodeTool ? (QuasimodeTool) tool : new QuasimodeTool(tool);
quasimodeTool.add(new ModifierKeySpec(false, false, true), new ZoomTool());
quasimodeTool.add(new ModifierKeySpec(false, true, true), new PanTool());
SelectFeaturesTool selectFeaturesTool = new SelectFeaturesTool() {
protected boolean selectedLayersOnly() {
return false;
}
};
quasimodeTool.add(new ModifierKeySpec(true, false, false), selectFeaturesTool);
quasimodeTool.add(new ModifierKeySpec(true, true, false), selectFeaturesTool);
quasimodeTool.add(new ModifierKeySpec(true, false, true), new FeatureInfoTool());
return quasimodeTool;
}
}