/******************************************************************************* * Copyright (c) 2000, 2010, 2012 IBM Corporation, Gerhardt Informatics Kft. and others. * 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: * IBM Corporation - initial API and implementation * Gerhardt Informatics Kft. - GEFGWT port *******************************************************************************/ package org.eclipse.gef.tools; import java.util.List; import org.eclipse.draw2d.geometry.Point; import org.eclipse.gef.AccessibleHandleProvider; import org.eclipse.gef.DragTracker; import org.eclipse.gef.EditDomain; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.GraphicalViewer; import org.eclipse.gef.Handle; import org.eclipse.gef.KeyHandler; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.requests.LocationRequest; import org.eclipse.gef.requests.SelectionRequest; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; /** * Tool to select and manipulate figures. A selection tool is in one of three * states, e.g., background selection, figure selection, handle manipulation. * The different states are handled by different child tools. */ public class SelectionTool extends TargetingTool { private static final int FLAG_HOVER_FEEDBACK = TargetingTool.MAX_FLAG << 1; /** Max flag */ protected static final int MAX_FLAG = FLAG_HOVER_FEEDBACK; /** Traverse handle state */ protected static final int STATE_TRAVERSE_HANDLE = TargetingTool.MAX_STATE << 1; /** Max state */ protected static final int MAX_STATE = STATE_TRAVERSE_HANDLE; private int handleIndex; private DragTracker dragTracker; private LocationRequest hoverRequest; // private WeakReference cachedHandlePart; private EditPart cachedHandlePart; /** * Default constructor. */ public SelectionTool() { } private boolean acceptTraverseHandle(KeyEvent e) { return (e.character == '.' || e.character == '>') && isInState(STATE_INITIAL | STATE_ACCESSIBLE_DRAG | STATE_ACCESSIBLE_DRAG_IN_PROGRESS) && ((e.stateMask & (SWT.ALT | SWT.CONTROL)) == 0); } /** * Creates the hover request (a {@link LocationRequest}) and sets its type * to {@link RequestConstants#REQ_SELECTION_HOVER}. */ protected void createHoverRequest() { hoverRequest = new LocationRequest(); hoverRequest.setType(RequestConstants.REQ_SELECTION_HOVER); } /** * Creates a {@link SelectionRequest} for the target request. * * @see TargetingTool#createTargetRequest() */ protected Request createTargetRequest() { SelectionRequest request = new SelectionRequest(); request.setType(getCommandName()); return request; } /** * Deactivates the tool. This method is called whenever the user switches to * another tool. Use this method to do some clean-up when the tool is * switched. Sets the drag tracker to <code>null</code>. */ public void deactivate() { setDragTracker(null); // deactivates the current drag tracker super.deactivate(); } /** * Erases the hover feedback by calling * {@link EditPart#eraseTargetFeedback(Request)}. */ protected void eraseHoverFeedback() { if (getTargetEditPart() == null) return; if (getTargetHoverRequest() == null) return; getTargetEditPart().eraseTargetFeedback(getTargetHoverRequest()); } /** * @see AbstractTool#getCommandName() */ protected String getCommandName() { return REQ_SELECTION; } /** * @see AbstractTool#getDebugName() */ protected String getDebugName() { return "Selection Tool";//$NON-NLS-1$ } /** * Returns the current drag tracker. * * @return the drag tracker */ protected DragTracker getDragTracker() { return dragTracker; } private EditPart getLastHandleProvider() { return cachedHandlePart; // if (cachedHandlePart == null) // return null; // EditPart part = (EditPart) cachedHandlePart.get(); // if (cachedHandlePart.isEnqueued()) // return null; // return part; } /** * Returns a new Conditional that evaluates to <code>true</code> if the * queried edit part's {@link EditPart#isSelectable()} method returns * <code>true</code>. * * @see TargetingTool#getTargetingConditional() */ protected EditPartViewer.Conditional getTargetingConditional() { return new EditPartViewer.Conditional() { public boolean evaluate(EditPart editpart) { EditPart targetEditPart = editpart .getTargetEditPart(getTargetRequest()); return targetEditPart != null && targetEditPart.isSelectable(); } }; } /** * Returns the target hover request. If <code>null</code>, it will be * created via {@link #createHoverRequest()}. * * @return the hover request */ protected Request getTargetHoverRequest() { if (hoverRequest == null) createHoverRequest(); return hoverRequest; } /** * If there is a {@link Handle} under the mouse, this method sets the drag * tracker returned from the handle. If there's an {@link EditPart} under * the mouse, this method sets the drag tracker returned from the edit part. * * @see AbstractTool#handleButtonDown(int) */ protected boolean handleButtonDown(int button) { if (!stateTransition(STATE_INITIAL, STATE_DRAG)) { resetHover(); return true; } resetHover(); EditPartViewer viewer = getCurrentViewer(); Point p = getLocation(); if (getDragTracker() != null) getDragTracker().deactivate(); if (viewer instanceof GraphicalViewer) { Handle handle = ((GraphicalViewer) viewer).findHandleAt(p); if (handle != null) { setDragTracker(handle.getDragTracker()); return true; } } updateTargetRequest(); ((SelectionRequest) getTargetRequest()).setLastButtonPressed(button); updateTargetUnderMouse(); EditPart editpart = getTargetEditPart(); if (editpart != null) { setDragTracker(editpart.getDragTracker(getTargetRequest())); lockTargetEditPart(editpart); return true; } return false; } /** * Resets this tool when the last button is released. * * @see AbstractTool#handleButtonUp(int) */ protected boolean handleButtonUp(int button) { if (getCurrentInput().isAnyButtonDown()) return false; ((SelectionRequest) getTargetRequest()).setLastButtonPressed(0); setDragTracker(null); setState(STATE_INITIAL); unlockTargetEditPart(); return true; } /** * @see AbstractTool#handleCommandStackChanged() */ protected boolean handleCommandStackChanged() { if (getDragTracker() == null) return super.handleCommandStackChanged(); return false; } /** * Sets the drag tracker to <code>null</code> and goes into the initial * state when focus is lost. * * @see AbstractTool#handleFocusLost() */ protected boolean handleFocusLost() { if (isInState(STATE_ACCESSIBLE_DRAG | STATE_ACCESSIBLE_DRAG_IN_PROGRESS | STATE_DRAG | STATE_DRAG_IN_PROGRESS)) { if (getDragTracker() != null) setDragTracker(null); setState(STATE_INITIAL); return true; } return false; } /** * Called when the mouse hovers. Calls {@link #showHoverFeedback()}. * * @see AbstractTool#handleHover() */ protected boolean handleHover() { setHoverActive(true); showHoverFeedback(); return true; } /** * Called when the mouse hover stops (i.e. the mouse moves or a button is * clicked). Calls {@link #eraseHoverFeedback()}. * * @see TargetingTool#handleHoverStop() */ protected boolean handleHoverStop() { eraseHoverFeedback(); return true; } /** * Processes key down events. Specifically, arrow keys for moving edit * parts, the ESC key for aborting a drag, the period '.' key for traversing * handles, and the ENTER key for committing a drag. If none of these keys * were pressed and the current viewer has a {@link KeyHandler}, it calls * {@link KeyHandler#keyPressed(KeyEvent)}. * * @see AbstractTool#handleKeyDown(KeyEvent) */ protected boolean handleKeyDown(KeyEvent e) { resetHover(); if (acceptArrowKey(e)) if (stateTransition(STATE_ACCESSIBLE_DRAG, STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) return true; if (acceptAbort(e)) { if (getDragTracker() != null) setDragTracker(null); if (isInState(STATE_TRAVERSE_HANDLE | STATE_ACCESSIBLE_DRAG | STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) placeMouseInViewer(getStartLocation().getTranslated(6, 6)); setState(STATE_INITIAL); setLastHandleProvider(null); return true; } if (acceptTraverseHandle(e)) { if (isInState(STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) if (getDragTracker() != null) getDragTracker().commitDrag(); if (isInState(STATE_ACCESSIBLE_DRAG | STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) { setDragTracker(null); getCurrentViewer().flush(); } if (!handleTraverseHandle(e)) setState(STATE_INITIAL); return true; } if (acceptDragCommit(e)) { if (getDragTracker() != null) getDragTracker().commitDrag(); setDragTracker(null); setState(STATE_INITIAL); handleIndex--; placeMouseInViewer(getLocation().getTranslated(6, 6)); return true; } if (isInState(STATE_INITIAL)) { if (getCurrentViewer().getKeyHandler() != null) return getCurrentViewer().getKeyHandler().keyPressed(e); } return false; } /** * If in the initial state and the viewer has a {@link KeyHandler}, calls * {@link KeyHandler#keyReleased(KeyEvent)} sending it the given key event. * * @see AbstractTool#handleKeyUp(KeyEvent) */ protected boolean handleKeyUp(KeyEvent e) { if (isInState(STATE_INITIAL) && getCurrentViewer().getKeyHandler() != null && getCurrentViewer().getKeyHandler().keyReleased(e)) return true; return false; } /** * If in the initial state, updates the request and the mouse target and * asks to show target feedback. If in the traverse handle state, finds the * next handle, moves the mouse cursor to that handle, and gets a drag * tracker from the handle. * * @see AbstractTool#handleMove() */ protected boolean handleMove() { if (stateTransition(STATE_ACCESSIBLE_DRAG, STATE_INITIAL)) setDragTracker(null); if (isInState(STATE_INITIAL)) { updateTargetRequest(); updateTargetUnderMouse(); showTargetFeedback(); return true; } else if (isInState(STATE_TRAVERSE_HANDLE)) { EditPartViewer viewer = getCurrentViewer(); if (viewer instanceof GraphicalViewer) { Handle handle = ((GraphicalViewer) viewer) .findHandleAt(getLocation()); if (handle != null) { setState(STATE_ACCESSIBLE_DRAG); setStartLocation(getLocation()); setDragTracker(handle.getDragTracker()); return true; } else { setState(STATE_INITIAL); } } } return false; } /** * If there's a drag tracker, calls handleNativeDragFinished() on the drag * tracker and then sets the drag tracker to <code>null</code>. * * @see AbstractTool#handleNativeDragFinished(DragSourceEvent) */ public boolean handleNativeDragFinished(DragSourceEvent event) { if (getDragTracker() != null) getDragTracker().nativeDragFinished(event, getCurrentViewer()); setDragTracker(null); unlockTargetEditPart(); return true; } /** * If there's a drag tracker, calls nativeDragStarted() on the drag tracker. * * @see AbstractTool#handleNativeDragStarted(DragSourceEvent) */ public boolean handleNativeDragStarted(DragSourceEvent event) { if (getDragTracker() != null) getDragTracker().nativeDragStarted(event, getCurrentViewer()); setState(STATE_INITIAL); return true; } private boolean handleTraverseHandle(KeyEvent e) { EditPart focus = getCurrentViewer().getFocusEditPart(); if (focus.getSelected() == EditPart.SELECTED_NONE) return false; getCurrentViewer().reveal(focus); AccessibleHandleProvider provider; provider = (AccessibleHandleProvider) focus .getAdapter(AccessibleHandleProvider.class); if (provider == null || provider.getAccessibleHandleLocations().isEmpty()) return false; /* * At this point, a handle provider with 1 or more handles has been * obtained */ setState(STATE_TRAVERSE_HANDLE); List locations = provider.getAccessibleHandleLocations(); // Goto next index, wrapping if necessary if (e.character == '.') handleIndex = (++handleIndex) % locations.size(); else handleIndex = (--handleIndex + locations.size()) % locations.size(); if (getLastHandleProvider() != focus) { handleIndex = 0; setLastHandleProvider(focus); } Point loc = (Point) locations.get(handleIndex); Point current = new Point(getCurrentViewer().getControl().toControl( Display.getCurrent().getCursorLocation())); if (current.equals(loc)) { // The cursor is already at the location that it is to be moved to. // So, we // move to the next handle instead. If there are no more handles, // then we // cancel the drag. if (locations.size() > 1) if (e.character == '.') handleIndex = (++handleIndex) % locations.size(); else handleIndex = (--handleIndex + locations.size()) % locations.size(); else { placeMouseInViewer(loc.getTranslated(6, 6)); return false; } } placeMouseInViewer((Point) locations.get(handleIndex)); return true; } /** * If there's a drag tracker, sets it to <code>null</code> and then sets * this tool's state to the initial state. * * @see AbstractTool#handleViewerExited() */ protected boolean handleViewerExited() { if (isInState(STATE_ACCESSIBLE_DRAG | STATE_ACCESSIBLE_DRAG_IN_PROGRESS | STATE_TRAVERSE_HANDLE | STATE_DRAG | STATE_DRAG_IN_PROGRESS)) { if (getDragTracker() != null) setDragTracker(null); setState(STATE_INITIAL); } return super.handleViewerExited(); } /** * Forwards the key down event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#keyDown(KeyEvent, * org.eclipse.gef.EditPartViewer) */ public void keyDown(KeyEvent evt, EditPartViewer viewer) { if (getDragTracker() != null) getDragTracker().keyDown(evt, viewer); super.keyDown(evt, viewer); } /** * Forwards the key up event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#keyUp(KeyEvent, org.eclipse.gef.EditPartViewer) */ public void keyUp(KeyEvent evt, EditPartViewer viewer) { if (getDragTracker() != null) getDragTracker().keyUp(evt, viewer); super.keyUp(evt, viewer); } /** * Forwards the mouse down event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#mouseDown(MouseEvent, * org.eclipse.gef.EditPartViewer) */ public void mouseDown(MouseEvent e, EditPartViewer viewer) { super.mouseDown(e, viewer); if (getDragTracker() != null) getDragTracker().mouseDown(e, viewer); } /** * Forwards the mouse double clicked event to the drag tracker, if one * exists. * * @see org.eclipse.gef.Tool#mouseDoubleClick(MouseEvent, * org.eclipse.gef.EditPartViewer) */ public void mouseDoubleClick(MouseEvent e, EditPartViewer viewer) { super.mouseDoubleClick(e, viewer); if (getDragTracker() != null) getDragTracker().mouseDoubleClick(e, viewer); } /** * Forwards the mouse drag event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#mouseDrag(MouseEvent, * org.eclipse.gef.EditPartViewer) */ public void mouseDrag(MouseEvent e, EditPartViewer viewer) { if (getDragTracker() != null) getDragTracker().mouseDrag(e, viewer); super.mouseDrag(e, viewer); } /** * Forwards the mouse hover event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#mouseHover(MouseEvent, * org.eclipse.gef.EditPartViewer) */ public void mouseHover(MouseEvent me, EditPartViewer viewer) { if (getDragTracker() != null) getDragTracker().mouseHover(me, viewer); super.mouseHover(me, viewer); } /** * Forwards the mouse move event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#mouseMove(MouseEvent, * org.eclipse.gef.EditPartViewer) */ public void mouseMove(MouseEvent me, EditPartViewer viewer) { if (getDragTracker() != null) getDragTracker().mouseMove(me, viewer); super.mouseMove(me, viewer); } /** * Forwards the mouse up event to the drag tracker, if one exists. * * @see org.eclipse.gef.Tool#mouseUp(MouseEvent, * org.eclipse.gef.EditPartViewer) */ public void mouseUp(MouseEvent e, EditPartViewer viewer) { if (getDragTracker() != null) { getDragTracker().mouseUp(e, viewer); } super.mouseUp(e, viewer); } /** * Delegates the scrolling to the DragTracker (if there is one). If not, * invokes the super method. * * @see org.eclipse.gef.Tool#mouseWheelScrolled(org.eclipse.swt.widgets.Event, * org.eclipse.gef.EditPartViewer) */ public void mouseWheelScrolled(Event event, EditPartViewer viewer) { if (getDragTracker() != null) { getDragTracker().mouseWheelScrolled(event, viewer); event.doit = false; } else super.mouseWheelScrolled(event, viewer); } /** * If there is a drag tracker, this method does nothing so that the drag * tracker can take care of the cursor. Otherwise, calls <code>super</code>. * * @see AbstractTool#refreshCursor() */ protected void refreshCursor() { // If we have a DragTracker, let it control the Cursor if (getDragTracker() == null) super.refreshCursor(); } /** * Sets the drag tracker for this SelectionTool. If the current drag tracker * is not <code>null</code>, this method deactivates it. If the new drag * tracker is not <code>null</code>, this method will activate it and set * the {@link EditDomain} and {@link EditPartViewer}. * * @param newDragTracker * the new drag tracker */ public void setDragTracker(DragTracker newDragTracker) { if (newDragTracker == dragTracker) return; if (dragTracker != null) dragTracker.deactivate(); dragTracker = newDragTracker; // if (!getCurrentInput().isMouseButtonDown(3)) // setMouseCapture(dragTracker != null); if (newDragTracker != null) { newDragTracker.setEditDomain(getDomain()); newDragTracker.activate(); newDragTracker.setViewer(getCurrentViewer()); } refreshCursor(); } private void setLastHandleProvider(EditPart part) { // if (part == null) // cachedHandlePart = null; // else // cachedHandlePart = new WeakReference(part); cachedHandlePart = part; } /** * Asks the target edit part (if there is one) to show hover feedback via * {@link EditPart#showTargetFeedback(Request)} with a hover request. */ protected void showHoverFeedback() { if (getTargetEditPart() == null) return; if (getTargetHoverRequest() == null) return; getTargetEditPart().showTargetFeedback(getTargetHoverRequest()); } /** * Updates the location of the hover request. */ protected void updateHoverRequest() { LocationRequest request = (LocationRequest) getTargetHoverRequest(); request.setLocation(getLocation()); } /** * Sets the modifiers , type and location of the target request (which is a * {@link SelectionRequest}) and then calls {@link #updateHoverRequest()}. * * @see TargetingTool#updateTargetRequest() */ protected void updateTargetRequest() { SelectionRequest request = (SelectionRequest) getTargetRequest(); request.setModifiers(getCurrentInput().getModifiers()); request.setType(getCommandName()); request.setLocation(getLocation()); updateHoverRequest(); } /** * @see AbstractTool#getDebugNameForState(int) */ protected String getDebugNameForState(int state) { if (state == STATE_TRAVERSE_HANDLE) return "Traverse Handle"; //$NON-NLS-1$ return super.getDebugNameForState(state); } }