/* ******************************************************************************
* Copyright (c) 2006-2012 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
package org.xmind.gef.tool;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.xmind.gef.GEF;
import org.xmind.gef.GraphicalViewer;
import org.xmind.gef.IGraphicalViewer;
import org.xmind.gef.IViewer;
import org.xmind.gef.Request;
import org.xmind.gef.command.ICommandStack;
import org.xmind.gef.event.KeyEvent;
import org.xmind.gef.event.MouseDragEvent;
import org.xmind.gef.event.MouseEvent;
import org.xmind.gef.event.MouseWheelEvent;
import org.xmind.gef.part.IGraphicalEditPart;
import org.xmind.gef.part.IPart;
/**
* @author Brian Sun
*/
public class SelectTool extends GraphicalTool {
private static final List<IPart> EMPTY_PARTS = Collections.emptyList();
private IPart sequenceStart = null;
private boolean ignoreResetSeqStart = false;
private IPart toSelectOnMouseUp = null;
protected void setToSelectOnMouseUp(IPart part) {
toSelectOnMouseUp = part;
}
public void mouseDoubleClick(MouseEvent me, IViewer viewer) {
setToSelectOnMouseUp(null);
super.mouseDoubleClick(me, viewer);
}
protected boolean handleMouseDoubleClick(MouseEvent me) {
if (me.leftOrRight) {
IPart source = (IPart) me.target;
if (source.hasRole(GEF.ROLE_EDITABLE)) {
resetSeqSelectStart();
handleEditRequest(createEditRequestOnDoubleClick(source, me));
me.consume();
return true;
}
}
return super.handleMouseDoubleClick(me);
}
protected Request createEditRequestOnDoubleClick(IPart source, MouseEvent me) {
return new Request(GEF.REQ_EDIT).setPrimaryTarget(me.target)
.setDomain(getDomain()).setViewer(getTargetViewer());
}
public void mouseDown(MouseEvent me, IViewer viewer) {
setToSelectOnMouseUp(null);
super.mouseDown(me, viewer);
}
protected boolean handleMouseDown(MouseEvent me) {
getStatus().setStatus(GEF.ST_MOUSE_PRESSED, true);
getStatus().setStatus(GEF.ST_MOUSE_RIGHT, !me.leftOrRight);
if (isToSelectOnMouseUp(me.target, getTargetViewer())) {
setToSelectOnMouseUp(me.target);
} else {
mouseSelect(me.target);
}
return true;
}
/**
* @param target
*/
protected void mouseSelect(IPart target) {
if (target.hasRole(GEF.ROLE_SELECTABLE)) {
if (getStatus().isStatus(GEF.ST_CONTROL_PRESSED)) {
getTargetViewer().setFocused(target);
} else if (!target.getStatus().isSelected()) {
if (getStatus().isStatus(GEF.ST_SHIFT_PRESSED)
&& getTargetViewer().getFocused() != null) {
sequenceSelect(target);
} else {
selectSingle(target);
}
}
} else if (!getStatus().isStatus(GEF.ST_CONTROL_PRESSED)) {
handleSelectNone(getTargetViewer());
}
}
protected boolean isToSelectOnMouseUp(IPart part, IViewer viewer) {
return part == viewer.getRootPart();
}
public void mouseDrag(MouseDragEvent me, IViewer viewer) {
setToSelectOnMouseUp(null);
super.mouseDrag(me, viewer);
}
protected boolean handleMouseDrag(MouseDragEvent me) {
resetSeqSelectStart();
if (getStatus().isStatus(GEF.ST_NO_DRAGGING)) {
me.consume();
return true;
}
IPart dragSource = me.source;
String toolType = null;
if (canStartBrowsing(dragSource, me)) {
toolType = getBrowseToolId();
} else {
if (canMove(dragSource, me)) {
toolType = getMoveTool(dragSource, me);
} else if (dragSource == getTargetViewer().getRootPart()) {
toolType = getAreaSelectToolId();
}
}
ITool tool = getTool(toolType);
if (tool != null && tool != this) {
changeToMoveTool(toolType, tool, dragSource, me);
me.consume();
return true;
}
return super.handleMouseDrag(me);
}
protected String getBrowseToolId() {
return GEF.TOOL_BROWSE;
}
protected String getAreaSelectToolId() {
return GEF.TOOL_AREASELECT;
}
protected void changeToMoveTool(String moveToolType, ITool moveTool,
IPart dragSource, MouseDragEvent me) {
if (moveTool instanceof ISourceTool
&& dragSource instanceof IGraphicalEditPart) {
((ISourceTool) moveTool).setSource((IGraphicalEditPart) dragSource);
}
if (moveTool instanceof IGraphicalTool) {
((IGraphicalTool) moveTool).setCursorPosition(getCursorPosition());
}
if (moveTool instanceof IDraggingTool) {
((IDraggingTool) moveTool).setStartingPosition(me.startingLocation);
}
changeActiveTool(moveToolType);
if (moveTool == getDomain().getActiveTool()) {
moveTool.mouseDrag(me, getTargetViewer());
}
}
protected boolean canStartBrowsing(IPart host, MouseDragEvent me) {
return getStatus().isStatus(GEF.ST_MOUSE_RIGHT)
|| (getStatus().isStatus(GEF.ST_MOUSE_PRESSED) && host
.hasRole(GEF.ROLE_MAP_MOVABLE));
}
protected boolean canMove(IPart host, MouseDragEvent me) {
return host.hasRole(GEF.ROLE_MOVABLE);
}
protected String getMoveTool(IPart source, MouseDragEvent me) {
return GEF.TOOL_MOVE;
}
protected boolean handleMouseEntered(MouseEvent me) {
IPart target = me.target;
if (target != null && target.hasRole(GEF.ROLE_SELECTABLE)) {
setPreSelected(target);
} else {
setPreSelected(null);
}
return super.handleMouseEntered(me);
}
protected void setPreSelected(IPart target) {
getTargetViewer().setPreselected(target);
}
protected boolean handleMouseHover(MouseEvent me) {
getStatus().setStatus(GEF.ST_MOUSE_HOVER, true);
return super.handleMouseHover(me);
}
public void mouseLongPressed(MouseEvent me, IViewer viewer) {
setToSelectOnMouseUp(null);
super.mouseLongPressed(me, viewer);
}
protected boolean handleLongPressed(MouseEvent me) {
if (me.leftOrRight && !getStatus().isStatus(GEF.ST_SHIFT_PRESSED)
&& !getStatus().isStatus(GEF.ST_CONTROL_PRESSED)
&& !getStatus().isStatus(GEF.ST_ALT_PRESSED)) {
String browseToolId = getBrowseToolId();
if (browseToolId != null) {
ITool tool = getTool(browseToolId);
if (tool != null) {
if (tool instanceof IDraggingTool) {
((IDraggingTool) tool)
.setStartingPosition(getCursorPosition());
}
if (tool instanceof IGraphicalTool) {
((IGraphicalTool) tool)
.setCursorPosition(getCursorPosition());
}
changeActiveTool(browseToolId);
if (tool == getDomain().getActiveTool()) {
me.consume();
}
return true;
}
}
}
return super.handleLongPressed(me);
}
public void mouseMove(MouseEvent me, IViewer viewer) {
setToSelectOnMouseUp(null);
super.mouseMove(me, viewer);
}
protected boolean handleMouseMove(MouseEvent me) {
getStatus().setStatus(GEF.ST_MOUSE_HOVER, false);
return super.handleMouseMove(me);
}
/**
* @see org.xmind.gef.tool.AbstractTool#mouseUp(org.xmind.gef.event.MouseEvent)
*/
@Override
public boolean handleMouseUp(MouseEvent me) {
getStatus().setStatus(GEF.ST_NO_DRAGGING, false);
getStatus().setStatus(GEF.ST_MOUSE_PRESSED, false);
IPart host = me.target;
if (getStatus().isStatus(GEF.ST_CONTROL_PRESSED)) {
if (host.hasRole(GEF.ROLE_SELECTABLE)) {
multiSelect(host);
}
}
if (toSelectOnMouseUp != null) {
mouseSelect(toSelectOnMouseUp);
setToSelectOnMouseUp(null);
}
if (!me.leftOrRight) {
getStatus().setStatus(GEF.ST_MOUSE_RIGHT, false);
if (!getStatus().isStatus(GEF.ST_HIDE_CMENU)) {
IGraphicalViewer viewer = getTargetViewer();
if (viewer instanceof GraphicalViewer) {
((GraphicalViewer) viewer).hideToolTip();
}
}
}
return true;
}
protected boolean handleKeyDown(KeyEvent ke) {
int state = ke.getState();
int key = ke.keyCode;
IGraphicalViewer viewer = getTargetViewer();
if (viewer != null) {
if (isNavigationKey(state, key)) {
return handleNavigationKeyDown(viewer, state, key);
}
}
return super.handleKeyDown(ke);
}
protected boolean handleNavigationKeyDown(IGraphicalViewer viewer,
int state, int key) {
String type = getNavigationRequestType(key);
if (type != null) {
Request request = createTargetedRequest(type, viewer, false);
request.setPrimaryTarget(viewer.getFocusedPart());
if (request.hasTargets()) {
boolean sequential = isSequentialNavigation(state);
if (sequential) {
request.setParameter(GEF.PARAM_NAV_SEQUENTIAL, Boolean.TRUE);
if (getSequenceStart() == null) {
IPart newStart = request.getPrimaryTarget();
setSequenceStart(newStart);
}
request.setParameter(GEF.PARAM_NAV_SEQUENCE_START,
getSequenceStart());
}
return handleNavRequest(request, sequential);
}
}
return true;
}
protected boolean handleNavRequest(Request request, boolean sequential) {
getDomain().handleRequest(request);
Object result = request.getResult(GEF.RESULT_NAVIGATION);
if (result != null && result instanceof IPart[]) {
IPart[] toSelect = (IPart[]) result;
IPart toFocus = (IPart) request.getResult(GEF.RESULT_NEW_FOCUS);
navigateTo(Arrays.asList(toSelect), toFocus, sequential);
return true;
}
return false;
}
protected void navigateTo(List<IPart> toSelect, boolean sequential) {
navigateTo(toSelect, null, sequential);
}
protected void navigateTo(List<IPart> toSelect, IPart toFocus,
boolean sequential) {
if (sequential) {
ignoreResetSeqStart = true;
}
select(toSelect,
toFocus == null || !toSelect.contains(toFocus) ? (toSelect
.isEmpty() ? null : toSelect.get(0)) : toFocus);
if (sequential)
ignoreResetSeqStart = false;
}
protected boolean isNavigationKey(int state, int key) {
if (state == 0 || state == SWT.MOD2) {
return key == SWT.ARROW_UP || key == SWT.ARROW_DOWN
|| key == SWT.ARROW_LEFT || key == SWT.ARROW_RIGHT
|| key == SWT.HOME || key == SWT.END;
}
return false;
}
protected boolean isSequentialNavigation(int state) {
return state == SWT.MOD2;
}
protected String getNavigationRequestType(int key) {
switch (key) {
case SWT.ARROW_UP:
return GEF.REQ_NAV_UP;
case SWT.ARROW_DOWN:
return GEF.REQ_NAV_DOWN;
case SWT.ARROW_LEFT:
return GEF.REQ_NAV_LEFT;
case SWT.ARROW_RIGHT:
return GEF.REQ_NAV_RIGHT;
case SWT.HOME:
return GEF.REQ_NAV_BEGINNING;
case SWT.END:
return GEF.REQ_NAV_END;
}
return null;
}
protected void handleNonTargetedRequest(Request request) {
String requestType = request.getType();
if (GEF.REQ_UNDO.equals(requestType)) {
ICommandStack cs = getDomain().getCommandStack();
if (cs.canUndo())
cs.undo();
return;
} else if (GEF.REQ_REDO.equals(requestType)) {
ICommandStack cs = getDomain().getCommandStack();
if (cs.canRedo())
cs.redo();
return;
} else if (request.getTargetViewer() != null) {
if (GEF.REQ_SELECT_NONE.equals(requestType)) {
handleSelectNone(request.getTargetViewer());
return;
} else if (GEF.REQ_SELECT_ALL.equals(requestType)) {
handleSelectAll(request.getTargetViewer());
return;
} else if (allowsFillTargets(request)) {
fillTargets(request, request.getTargetViewer(), false);
if (request.hasTargets()) {
handleTargetedRequest(request);
return;
}
}
}
super.handleNonTargetedRequest(request);
}
protected boolean allowsFillTargets(Request request) {
return true;
}
protected void handleTargetedRequest(Request request) {
String requestType = request.getType();
if (GEF.REQ_SELECT.equals(requestType)) {
select(request.getTargets(), request.getPrimaryTarget());
} else if (GEF.REQ_SELECT_SINGLE.equals(requestType)) {
selectSingle(request.getPrimaryTarget());
} else if (GEF.REQ_EDIT.equals(requestType)) {
handleEditRequest(request);
} else {
super.handleTargetedRequest(request);
}
}
protected void handleSelectAll(IViewer viewer) {
List<IPart> allSelectable = collectAllSelectable(viewer.getRootPart(),
new ArrayList<IPart>());
IPart toFocus = getTargetViewer().getFocusedPart();
select(allSelectable, toFocus);
}
protected List<IPart> collectAllSelectable(IPart parent, List<IPart> results) {
for (IPart child : parent.getChildren()) {
if (isSelectableOnSelectAll(child)) {
results.add(child);
}
collectAllSelectable(child, results);
}
return results;
}
protected boolean isSelectableOnSelectAll(IPart child) {
return getTargetViewer().getSelectionSupport().isSelectable(child);
}
/**
* @return the lastSeqStart
*/
public IPart getSequenceStart() {
return sequenceStart;
}
protected List<IPart> getSequenceParts(IPart start, IPart end) {
return Arrays.asList(start, end);
}
protected void select(List<? extends IPart> toSelect, IPart toFocus) {
IGraphicalViewer viewer = getTargetViewer();
if (viewer != null) {
IPart lastFocused = viewer.getFocusedPart();
viewer.setSelection(new StructuredSelection(toSelect), true);
resetSeqSelectStart();
viewer.setFocused(getToFocus(toFocus, lastFocused));
}
}
protected IPart getToFocus(IPart toFocus, IPart lastFocused) {
if (isFocusable(toFocus, lastFocused)) {
return toFocus;
}
return null;
}
protected boolean isFocusable(IPart toFocus, IPart lastFocused) {
if (lastFocused != null && lastFocused.getStatus().isSelected())
return true;
return toFocus != null && toFocus.getStatus().isSelected();
}
protected void multiSelect(IPart host) {
if (!host.getStatus().isSelected()) {
getTargetViewer().getSelectionSupport().appendSelection(host);
} else {
getTargetViewer().getSelectionSupport().deselect(host);
}
}
protected void handleSelectNone(IViewer viewer) {
select(EMPTY_PARTS, null);
}
protected void selectSingle(IPart target) {
select(Collections.singletonList(target), target);
}
protected void sequenceSelect(IPart target) {
if (getSequenceStart() == null) {
setSequenceStart(getTargetViewer().getFocusedPart());
}
List<IPart> seqParts = getSequenceParts(getSequenceStart(), target);
if (seqParts != null && !seqParts.isEmpty()) {
ignoreResetSeqStart = true;
select(seqParts, target);
ignoreResetSeqStart = false;
}
}
protected void setSequenceStart(IPart start) {
this.sequenceStart = start;
}
public void resetSeqSelectStart() {
if (ignoreResetSeqStart)
return;
setSequenceStart(null);
}
protected void handleEditRequest(Request request) {
startEditing(request.getPrimaryTarget(), request);
}
protected void startEditing(IPart source, Request request) {
//selectSingle(source);
String editToolType = getEditTool(source, request);
ITool et = getTool(editToolType);
if (et != null && et != this) {
changeActiveTool(editToolType);
if (et == getDomain().getActiveTool()) {
et.handleRequest(request);
}
}
}
protected String getEditTool(IPart source, Request request) {
return GEF.TOOL_EDIT;
}
@Override
protected boolean handleWheelScrolled(MouseWheelEvent me) {
if (getStatus().isStatus(GEF.ST_CONTROL_PRESSED)) {
if (handleZoomByScroll(me))
return true;
}
return super.handleWheelScrolled(me);
}
protected boolean handleZoomByScroll(MouseWheelEvent me) {
if (me.upOrDown) {
getDomain().handleRequest(GEF.REQ_ZOOMIN, getTargetViewer());
} else {
getDomain().handleRequest(GEF.REQ_ZOOMOUT, getTargetViewer());
}
me.doIt = false;
return true;
}
}