/******************************************************************************* * 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 ******************************************************************************/ /* * @(#)DelegationSelectionTool.java 2.1.1 2008-05-15 * * Copyright (c) 1996-2008 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.geom.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import org.jhotdraw.app.action.Actions; /** * A SelectionTool, which recognizes double clicks and popup menu triggers. * If a double click or popup trigger is encountered a hook method is called, * which handles the event. This methods can be overriden in subclasse to * provide customized behaviour. * <p> * By default, this Tool delegates mouse events to a specific Tool if * the figure which has been double clicked, provides a specialized tool. * * @author Werner Randelshofer * @version 2.1.1 2008-05-15 Only forward double clicks of left mouse button. * <br>2.1 2007-04-14 Added support for multi-clicks. * <br>2.0 2006-01-18 Changed to support double precision coordinates. * Popup timer added. Support for radio button menu items added. * <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1. */ public class DelegationSelectionTool extends SelectionTool { /** * Set this to true to turn on debugging output on System.out. */ private final static boolean DEBUG = false; /** * A set of actions which is applied to the drawing. */ private Collection<Action> drawingActions; /** * A set of actions which is applied to a selection of figures. */ private Collection<Action> selectionActions; /** * We use this timer, to show a popup menu, when the user presses the * mouse key for a second without moving the mouse. */ private javax.swing.Timer popupTimer; /** * When the popup menu is visible, we do not track mouse movements. */ private JPopupMenu popupMenu; /** * We store the last mouse click here, to support multi-click behavior, * that is, a behavior that is invoked, when the user clicks multiple on * the same spot, but in a longer interval than needed for a double click. */ private MouseEvent lastClickEvent; /** * This variable is set to true, if a mouse pressed event is a popup trigger. */ private boolean isMousePressedPopupTrigger; /** Creates a new instance. */ public DelegationSelectionTool() { this(new LinkedList<Action>(), new LinkedList<Action>()); } /** Creates a new instance. */ public DelegationSelectionTool(Collection<Action> drawingActions, Collection<Action> selectionActions) { this.drawingActions = drawingActions; this.selectionActions = selectionActions; } public void setDrawingActions(Collection<Action> drawingActions) { this.drawingActions = drawingActions; } public void setFigureActions(Collection<Action> selectionActions) { this.selectionActions = selectionActions; } /** * MouseListener method for mousePressed events. If the popup trigger has * been activated, then the appropriate hook method is called. */ public void mousePressed(final MouseEvent evt) { if (popupTimer != null) { popupTimer.stop(); popupTimer = null; } // XXX - When we want to support multiple views, we have to // implement this: //setView((DrawingView)e.getSource()); isMousePressedPopupTrigger = evt.isPopupTrigger(); if (isMousePressedPopupTrigger) { getView().requestFocus(); handlePopupMenu(evt); } else { super.mousePressed(evt); popupTimer = new javax.swing.Timer(1000, new ActionListener() { public void actionPerformed(ActionEvent aevt) { handlePopupMenu(evt); popupTimer = null; } }); popupTimer.setRepeats(false); popupTimer.start(); } } /** * MouseListener method for mouseReleased events. If the popup trigger has * been activated, then the appropriate hook method is called. */ public void mouseReleased(MouseEvent evt) { if (popupTimer != null) { popupTimer.stop(); popupTimer = null; } if (isMousePressedPopupTrigger) { isMousePressedPopupTrigger = false; } else { if (evt.isPopupTrigger()) { handlePopupMenu(evt); } else { super.mouseReleased(evt); } } } public void mouseDragged(MouseEvent evt) { if (popupTimer != null) { popupTimer.stop(); popupTimer = null; } if (popupMenu == null || !popupMenu.isVisible()) { super.mouseDragged(evt); } } public void mouseClicked(MouseEvent evt) { if (DEBUG) { System.out.println("DelegationSelectionTool.mouseClicked " + evt); } super.mouseClicked(evt); if (!evt.isConsumed()) { if (evt.getClickCount() == 2 && evt.getButton() == MouseEvent.BUTTON1) { handleDoubleClick(evt); } else if (evt.getClickCount() == 1 && evt.getModifiersEx() == 0 && lastClickEvent != null && lastClickEvent.getClickCount() == 1 && lastClickEvent.getModifiersEx() == 0 && lastClickEvent.getX() == evt.getX() && lastClickEvent.getY() == evt.getY()) { handleMultiClick(evt); } } lastClickEvent = evt; } /** * Hook method which can be overriden by subclasses to provide * specialised behaviour in the event of a popup trigger. */ protected void handlePopupMenu(MouseEvent evt) { Point p = new Point(evt.getX(), evt.getY()); Figure figure = getView().findFigure(p); if (figure != null || drawingActions.size() > 0) { showPopupMenu(figure, p, evt.getComponent()); } else { popupMenu = null; } } protected void showPopupMenu(Figure figure, Point p, Component c) { if (DEBUG) { System.out.println("DelegationSelectionTool.showPopupMenu " + figure); } JPopupMenu menu = new JPopupMenu(); popupMenu = menu; JMenu submenu = null; String submenuName = null; LinkedList<Action> popupActions = new LinkedList<Action>(); if (figure != null) { LinkedList<Action> figureActions = new LinkedList<Action>( figure.getActions(viewToDrawing(p))); if (popupActions.size() != 0 && figureActions.size() != 0) { popupActions.add(null); } popupActions.addAll(figureActions); if (popupActions.size() != 0 && selectionActions.size() != 0) { popupActions.add(null); } popupActions.addAll(selectionActions); } if (popupActions.size() != 0 && drawingActions.size() != 0) { popupActions.add(null); } popupActions.addAll(drawingActions); HashMap<Object, ButtonGroup> buttonGroups = new HashMap<Object, ButtonGroup>(); for (Action a : popupActions) { if (a != null && a.getValue(Actions.SUBMENU_KEY) != null) { if (submenuName == null || !submenuName.equals(a.getValue(Actions.SUBMENU_KEY))) { submenuName = (String) a.getValue(Actions.SUBMENU_KEY); submenu = new JMenu(submenuName); menu.add(submenu); } } else { submenuName = null; submenu = null; } if (a == null) { if (submenu != null) { submenu.addSeparator(); } else { menu.addSeparator(); } } else { AbstractButton button; if (a.getValue(Actions.BUTTON_GROUP_KEY) != null) { ButtonGroup bg = buttonGroups.get(a.getValue(Actions.BUTTON_GROUP_KEY)); if (bg == null) { bg = new ButtonGroup(); buttonGroups.put(a.getValue(Actions.BUTTON_GROUP_KEY), bg); } button = new JRadioButtonMenuItem(a); bg.add(button); button.setSelected(a.getValue(Actions.SELECTED_KEY) == Boolean.TRUE); } else if (a.getValue(Actions.SELECTED_KEY) != null) { button = new JCheckBoxMenuItem(a); button.setSelected(a.getValue(Actions.SELECTED_KEY) == Boolean.TRUE); } else { button = new JMenuItem(a); } if (submenu != null) { submenu.add(button); } else { menu.add(button); } } } menu.show(c, p.x, p.y); } /** * Hook method which can be overriden by subclasses to provide * specialised behaviour in the event of a double click. */ protected void handleDoubleClick(MouseEvent evt) { if (DEBUG) { System.out.println("DelegationSelectionTool.handleDoubleClick " + evt); } DrawingView v = getView(); Point pos = new Point(evt.getX(), evt.getY()); Handle handle = v.findHandle(pos); if (handle != null) { if (DEBUG) { System.out.println("DelegationSelectionTool.handleDoubleClick by handle"); } handle.trackDoubleClick(pos, evt.getModifiersEx()); } else { Point2D.Double p = viewToDrawing(pos); // Note: The search sequence used here, must be // consistent with the search sequence used by the // HandleTracker, the SelectAreaTracker and SelectionTool. // If possible, continue to work with the current selection Figure figure = null; if (isSelectBehindEnabled()) { for (Figure f : v.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 = v.findFigure(pos); } Figure outerFigure = figure; if (figure != null && figure.isSelectable()) { if (DEBUG) { System.out.println("DelegationSelectionTool.handleDoubleClick by figure"); } Tool figureTool = figure.getTool(p); if (figureTool == null) { figure = getDrawing().findFigureInside(p); if (figure != null) { figureTool = figure.getTool(p); } } if (figureTool != null) { setTracker(figureTool); figureTool.mousePressed(evt); } else { if (outerFigure.handleMouseClick(p, evt, getView())) { v.clearSelection(); v.addToSelection(outerFigure); } else { v.clearSelection(); v.addToSelection(outerFigure); v.setHandleDetailLevel(v.getHandleDetailLevel() + 1); } } } } evt.consume(); } /** * Hook method which can be overriden by subclasses to provide * specialised behaviour in the event of a multi-click. */ protected void handleMultiClick(MouseEvent evt) { if (DEBUG) { System.out.println("DelegationSelectionTool.handleMultiClick " + evt); } DrawingView v = getView(); Point pos = new Point(evt.getX(), evt.getY()); Handle handle = v.findHandle(pos); if (handle == null) { v.setHandleDetailLevel(v.getHandleDetailLevel() + 1); } } @Override public String getToolTipText(DrawingView view, MouseEvent evt) { Handle handle = view.findHandle(evt.getPoint()); if (handle != null) { return handle.getToolTipText(evt.getPoint()); } Figure figure = view.findFigure(evt.getPoint()); if (figure != null) { return figure.getToolTipText(viewToDrawing(evt.getPoint())); } return null; } }