/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
/*
* @(#)SelectionTool.java 2.0 2009-04-25
*
* Copyright (c) 1996-2009 by the original authors of JHotDraw
* and all its contributors.
* All rights reserved.
*
* The copyright of this software is owned by the authors and
* contributors of the JHotDraw project ("the copyright holders").
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* the copyright holders. For details see accompanying license terms.
*/
package org.jhotdraw.draw;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.util.HashSet;
/**
* Tool to select and manipulate figures.
* <p>
* A selection tool is in one of three states: 1) area
* selection, 2) figure dragging, 3) handle manipulation. The different
* states are handled by different tracker objects: the
* <code>DefaultSelectAreaTracker</code>, the <code>DefaultDragTracker</code> and the
* <code>DefaultHandleTracker</code>.
* <p>
* A Figure can be selected by clicking at it. Holding the alt key or the
* ctrl key down, selects the Figure behind it.
* <p>
* Design pattern:<br>
* Name: Chain of Responsibility.<br>
* Role: Handler.<br>
* Partners: {@link DefaultSelectAreaTracker} as Handler, {@link DefaultDragTracker} as
* Handler, {@link DefaultHandleTracker} as Handler.
* <p>
* Design pattern:<br>
* Name: State.<br>
* Role: Context.<br>
* Partners: {@link DefaultSelectAreaTracker} as State, {@link DefaultDragTracker} as
* State, {@link DefaultHandleTracker} as State.
*
* @see DefaultSelectAreaTracker
* @see DefaultDragTracker
* @see DefaultHandleTracker
*
* @author Werner Randelshofer
* @version 2.0 2009-04-25 Trackers can now be replaced without having to
* subclass SelectionTool.
* <br>1.1 2007-11-05 Added property selectBehindEnabled.
* <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1.
*/
public class SelectionTool extends AbstractTool
implements ToolListener {
/**
* The tracker encapsulates the current state of the SelectionTool.
*/
private Tool tracker;
/**
* The tracker encapsulates the current state of the SelectionTool.
*/
private HandleTracker handleTracker;
/**
* The tracker encapsulates the current state of the SelectionTool.
*/
private SelectAreaTracker selectAreaTracker;
/**
* The tracker encapsulates the current state of the SelectionTool.
*/
private DragTracker dragTracker;
/**
* Constant for the name of the selectBehindEnabled property.
*/
public final static String SELECT_BEHIND_ENABLED_PROPERTY = "selectBehindEnabled";
/**
* Represents the state of the selectBehindEnabled property.
* By default, this property is set to true.
*/
private boolean isSelectBehindEnabled = true;
/** Creates a new instance. */
public SelectionTool() {
tracker = getSelectAreaTracker();
tracker.addToolListener(this);
}
/**
* Sets the selectBehindEnabled property.
* This is a bound property.
*
* @param newValue The new value.
*/
public void setSelectBehindEnabled(boolean newValue) {
boolean oldValue = isSelectBehindEnabled;
isSelectBehindEnabled = newValue;
firePropertyChange(SELECT_BEHIND_ENABLED_PROPERTY, oldValue, newValue);
}
/**
* Returns the value of the selectBehindEnabled property.
* This is a bound property.
*
* @return The property value.
*/
public boolean isSelectBehindEnabled() {
return isSelectBehindEnabled;
}
@Override
public void activate(DrawingEditor editor) {
super.activate(editor);
tracker.activate(editor);
}
public void deactivate(DrawingEditor editor) {
super.deactivate(editor);
tracker.deactivate(editor);
}
public void keyPressed(KeyEvent e) {
if (getView() != null && getView().isEnabled()) {
tracker.keyPressed(e);
}
}
public void keyReleased(KeyEvent evt) {
if (getView() != null && getView().isEnabled()) {
tracker.keyReleased(evt);
}
}
public void keyTyped(KeyEvent evt) {
if (getView() != null && getView().isEnabled()) {
tracker.keyTyped(evt);
}
}
public void mouseClicked(MouseEvent evt) {
if (getView() != null && getView().isEnabled()) {
tracker.mouseClicked(evt);
}
}
public void mouseDragged(MouseEvent evt) {
if (getView() != null && getView().isEnabled()) {
tracker.mouseDragged(evt);
}
}
public void mouseEntered(MouseEvent evt) {
super.mouseEntered(evt);
tracker.mouseEntered(evt);
}
public void mouseExited(MouseEvent evt) {
super.mouseExited(evt);
tracker.mouseExited(evt);
}
@Override
public void mouseMoved(MouseEvent evt) {
tracker.mouseMoved(evt);
}
@Override
public void mouseReleased(MouseEvent evt) {
if (getView() != null && getView().isEnabled()) {
tracker.mouseReleased(evt);
}
}
@Override
public void draw(Graphics2D g) {
tracker.draw(g);
}
@Override
public void mousePressed(MouseEvent evt) {
if (getView() != null && getView().isEnabled()) {
super.mousePressed(evt);
DrawingView view = getView();
Handle handle = view.findHandle(anchor);
Tool newTracker = null;
if (handle != null) {
newTracker = getHandleTracker(handle);
} else {
Figure figure;
Drawing drawing = view.getDrawing();
Point2D.Double p = view.viewToDrawing(anchor);
if (isSelectBehindEnabled() &&
(evt.getModifiersEx() &
(InputEvent.ALT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK)) != 0) {
// Select a figure behind the current selection
figure = view.findFigure(anchor);
while (figure != null && !figure.isSelectable()) {
figure = drawing.findFigureBehind(p, figure);
}
HashSet<Figure> ignoredFigures = new HashSet<Figure>(view.getSelectedFigures());
ignoredFigures.add(figure);
Figure figureBehind = view.getDrawing().findFigureBehind(
view.viewToDrawing(anchor), ignoredFigures);
if (figureBehind != null) {
figure = figureBehind;
}
} else {
// Note: The search sequence used here, must be
// consistent with the search sequence used by the
// DefaultHandleTracker, the DefaultSelectAreaTracker and DelegationSelectionTool.
// If possible, continue to work with the current selection
figure = null;
if (isSelectBehindEnabled()) {
for (Figure f : view.getSelectedFigures()) {
if (f.contains(p)) {
figure = f;
break;
}
}
}
// If the point is not contained in the current selection,
// search for a figure in the drawing.
if (figure == null) {
figure = view.findFigure(anchor);
while (figure != null && !figure.isSelectable()) {
figure = drawing.findFigureBehind(p, figure);
}
}
}
if (figure != null && figure.isSelectable()) {
newTracker = getDragTracker(figure);
} else {
if (!evt.isShiftDown()) {
view.clearSelection();
view.setHandleDetailLevel(0);
}
newTracker = getSelectAreaTracker();
}
}
if (newTracker != null) {
setTracker(newTracker);
}
tracker.mousePressed(evt);
}
}
protected void setTracker(Tool newTracker) {
if (tracker != null) {
tracker.deactivate(getEditor());
tracker.removeToolListener(this);
}
tracker = newTracker;
if (tracker != null) {
tracker.activate(getEditor());
tracker.addToolListener(this);
}
}
/**
* Method to get a {@code HandleTracker} which handles user interaction
* for the specified handle.
*/
protected HandleTracker getHandleTracker(Handle handle) {
if (handleTracker == null) {
handleTracker = new DefaultHandleTracker();
}
handleTracker.setHandles(handle, getView().getCompatibleHandles(handle));
return handleTracker;
}
/**
* Method to get a {@code DragTracker} which handles user interaction
* for dragging the specified figure.
*/
protected DragTracker getDragTracker(Figure f) {
if (dragTracker == null) {
dragTracker = new DefaultDragTracker();
}
dragTracker.setDraggedFigure(f);
return dragTracker;
}
/**
* Method to get a {@code SelectAreaTracker} which handles user interaction
* for selecting an area on the drawing.
*/
protected SelectAreaTracker getSelectAreaTracker() {
if (selectAreaTracker == null) {
selectAreaTracker = new DefaultSelectAreaTracker();
}
return selectAreaTracker;
}
/**
* Method to set a {@code HandleTracker}. If you specify null, the
* {@code SelectionTool} uses the {@code DefaultHandleTracker}.
*/
public void setHandleTracker(HandleTracker newValue) {
handleTracker = newValue;
}
/**
* Method to set a {@code SelectAreaTracker}. If you specify null, the
* {@code SelectionTool} uses the {@code DefaultSelectAreaTracker}.
*/
public void setSelectAreaTracker(SelectAreaTracker newValue) {
selectAreaTracker = newValue;
}
/**
* Method to set a {@code DragTracker}. If you specify null, the
* {@code SelectionTool} uses the {@code DefaultDragTracker}.
*/
public void setDragTracker(DragTracker newValue) {
dragTracker = newValue;
}
public void toolStarted(ToolEvent event) {
}
public void toolDone(ToolEvent event) {
// Empty
Tool newTracker = getSelectAreaTracker();
if (newTracker != null) {
if (tracker != null) {
tracker.deactivate(getEditor());
tracker.removeToolListener(this);
}
tracker = newTracker;
tracker.activate(getEditor());
tracker.addToolListener(this);
}
fireToolDone();
}
/**
* Sent when an area of the drawing view needs to be repainted.
*/
public void areaInvalidated(ToolEvent e) {
fireAreaInvalidated(e.getInvalidatedArea());
}
/**
* Returns true, if this tool lets the user interact with handles.
* <p>
* Handles may draw differently, if interaction is not possible.
*
* @return True, if this tool supports interaction with the handles.
*/
public boolean supportsHandleInteraction() {
return true;
}
}