/******************************************************************************* * 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; } }