blob: 8720421dadc8e94ba0d19a4479f392b9fd677b8a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation 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
*******************************************************************************/
package org.eclipse.gef.tools;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.rap.swt.SWT;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.SharedCursors;
import org.eclipse.gef.util.EditPartUtilities;
/**
* A Tool which selects multiple {@link EditPart}s inside a rectangular area of
* a Graphical Viewer. Selection behavior can be configured by selecting (via
* {@link #setMarqueeBehavior(int)}) one of six supported marquee behaviors,
* which are:
*
* <ul>
* <li>{@link #BEHAVIOR_NODES_CONTAINED}</li>
* <li>{@link #BEHAVIOR_NODES_TOUCHED}</li>
* <li>{@link #BEHAVIOR_CONNECTIONS_CONTAINED}</li>
* <li>{@link #BEHAVIOR_CONNECTIONS_TOUCHED}</li>
* <li>{@link #BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS}</li>
* <li>{@link #BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS}</li>
* </ul>
*
* By default, only edit parts whose figure's are on the primary layer that are
* fully enclosed in the marquee selection rectangle will be considered (
* {@link #BEHAVIOR_NODES_CONTAINED}).
*
* Besides different marquee behaviors, the tool supports different modes, which
* the user can influence by pressing modifier keys at the beginning of the drag
* operation. I.e. if the SHIFT key is pressed at the beginning of the drag, the
* enclosed items will be appended to the current viewer selection. If the MOD1
* key is pressed at the beginning of the drag, the enclosed items will have
* their selection state inverted.
*
* @author ebordeau
* @author dlee
* @author rhudson
* @author delee
* @author msorens
* @author pshah
* @author anyssen
*/
public class MarqueeSelectionTool extends AbstractTool {
class MarqueeRectangleFigure extends Figure {
private static final int DELAY = 110; // animation delay in millisecond
private int offset = 0;
private boolean schedulePaint = true;
/**
* @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics)
*/
protected void paintFigure(Graphics graphics) {
Rectangle bounds = getBounds().getCopy();
graphics.translate(getLocation());
if (Platform.WS_COCOA.equals(Platform.getWS())) {
// Bugzilla 303659 setXORMode(true) broken on MAC COCOA
graphics.setForegroundColor(ColorConstants.black);
graphics.setBackgroundColor(ColorConstants.black);
} else {
graphics.setXORMode(true);
graphics.setForegroundColor(ColorConstants.white);
graphics.setBackgroundColor(ColorConstants.black);
}
graphics.setLineStyle(Graphics.LINE_DOT);
int[] points = new int[6];
points[0] = 0 + offset;
points[1] = 0;
points[2] = bounds.width - 1;
points[3] = 0;
points[4] = bounds.width - 1;
points[5] = bounds.height - 1;
graphics.drawPolyline(points);
points[0] = 0;
points[1] = 0 + offset;
points[2] = 0;
points[3] = bounds.height - 1;
points[4] = bounds.width - 1;
points[5] = bounds.height - 1;
graphics.drawPolyline(points);
graphics.translate(getLocation().getNegated());
if (schedulePaint) {
Display.getCurrent().timerExec(DELAY, new Runnable() {
public void run() {
offset++;
if (offset > 5)
offset = 0;
schedulePaint = true;
repaint();
}
});
}
schedulePaint = false;
}
}
/**
* This behavior selects connections that intersect the marquee rectangle.
*
* @since 3.7
*/
public static final int BEHAVIOR_CONNECTIONS_CONTAINED = new Integer(6)
.intValue();
/**
* This behavior selects connections that intersect the marquee rectangle.
*
* @since 3.1
*/
public static final int BEHAVIOR_CONNECTIONS_TOUCHED = new Integer(2)
.intValue();
/**
* This behavior selects nodes completely encompassed by the marquee
* rectangle. This is the default behavior for this tool.
*
* @since 3.1
*/
public static final int BEHAVIOR_NODES_CONTAINED = new Integer(1)
.intValue();
/**
* This behavior selects nodes completely encompassed by the marquee
* rectangle, and all connections between those nodes.
*
* @since 3.7
*/
public static final int BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS = new Integer(
3).intValue();
/**
* This behavior selects nodes that intersect the marquee rectangle.
*
* @since 3.7
*/
public static final int BEHAVIOR_NODES_TOUCHED = new Integer(4).intValue();
/**
* This behavior selects nodes that intersect the marquee rectangle.
*
* @since 3.7
*/
public static final int BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS = new Integer(
5).intValue();
/**
* This behavior selects nodes completely encompassed by the marquee
* rectangle, and all connections between those nodes.
*
* @since 3.1
* @deprecated use {@link #BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS}
* instead.
*/
public static final int BEHAVIOR_NODES_AND_CONNECTIONS = BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS;
static final int DEFAULT_MODE = 0;
static final int TOGGLE_MODE = 1;
static final int APPEND_MODE = 2;
private static final Request MARQUEE_REQUEST = new Request(
RequestConstants.REQ_SELECTION);
/**
* The property to be used in
* {@link AbstractTool#setProperties(java.util.Map)} for
* {@link #setMarqueeBehavior(int)}.
*/
public static final Object PROPERTY_MARQUEE_BEHAVIOR = "marqueeBehavior"; //$NON-NLS-1$
/**
* Constant defining the default marquee selection behavior.
*
* @since 3.7
*/
public static final int DEFAULT_MARQUEE_BEHAVIOR = BEHAVIOR_NODES_CONTAINED;
private Set allChildren = new HashSet();
private int marqueeBehavior = DEFAULT_MARQUEE_BEHAVIOR;
private Figure marqueeRectangleFigure;
private int mode;
private Collection selectedEditParts;
private Request targetRequest;
/**
* Creates a new MarqueeSelectionTool of default type
* {@link #BEHAVIOR_NODES_CONTAINED}.
*/
public MarqueeSelectionTool() {
setDefaultCursor(SharedCursors.CROSS);
setUnloadWhenFinished(false);
}
/**
* @see org.eclipse.gef.tools.AbstractTool#applyProperty(java.lang.Object,
* java.lang.Object)
*/
protected void applyProperty(Object key, Object value) {
if (PROPERTY_MARQUEE_BEHAVIOR.equals(key)) {
if (value instanceof Integer)
setMarqueeBehavior(((Integer) value).intValue());
return;
}
super.applyProperty(key, value);
}
/**
* Called from {@link #performMarqueeSelect()} to determine those
* {@link EditPart}s that are affected by the current marquee selection. In
* default and append mode, the edit parts returned here will become
* selected in the current viewer's new selection (which is calculated and
* set in {@link #performMarqueeSelect()}), while in toggle mode their
* selection state will be inverted.
*
* Calculation is delegated to
* {@link #calculatePrimaryMarqueeSelectedEditParts()} and
* {@link #calculateSecondaryMarqueeSelectedEditParts(Collection)} to
* compute the set of marquee selected edit parts in a two step-process,
* where all directly affected edit parts are determined first, and those
* indirectly affected (related connections in case of
* {@link #BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS}, or
* {@link #BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS}) afterwards.
*
* Clients may overwrite to customize the calculation of marquee selected
* edit parts.
*
* @return A collection containing all edit parts that should be regarded as
* being included in the current marquee selection, i.e. which
* should get selected in default or append mode, and whose
* selection state should get inverted in toggle mode.
* @since 3.7
*/
protected Collection calculateMarqueeSelectedEditParts() {
Collection marqueeSelectedEditParts = new HashSet();
marqueeSelectedEditParts
.addAll(calculatePrimaryMarqueeSelectedEditParts());
marqueeSelectedEditParts
.addAll(calculateSecondaryMarqueeSelectedEditParts(marqueeSelectedEditParts));
return marqueeSelectedEditParts;
}
/**
* Responsible of calculating those edit parts that should be regarded as
* directly affected by the current marquee selection. By default, the
* method calculates which edit parts are potential candidates based on the
* current marquee behavior and delegates to
* {@link #isMarqueeSelectable(GraphicalEditPart)} and
* {@link #isPrimaryMarqueeSelectedEditPart(GraphicalEditPart)} to decide
* whether the candidate is to be included in the marquee selection.
*
* @return A {@link Collection} containing all {@link EditPart}s that should
* be regarded as being directly affected by the current marquee
* selection.
* @since 3.7
*/
private Collection calculatePrimaryMarqueeSelectedEditParts() {
Collection editPartsToProcess = new HashSet();
if (marqueeBehavior != BEHAVIOR_CONNECTIONS_CONTAINED
&& marqueeBehavior != BEHAVIOR_CONNECTIONS_TOUCHED) {
// process nodes
editPartsToProcess.addAll(EditPartUtilities
.getAllChildren((GraphicalEditPart) getCurrentViewer()
.getRootEditPart()));
}
if (marqueeBehavior != BEHAVIOR_NODES_CONTAINED
&& marqueeBehavior != BEHAVIOR_NODES_TOUCHED) {
// process connections
editPartsToProcess
.addAll(EditPartUtilities
.getAllNestedConnectionEditParts((GraphicalEditPart) getCurrentViewer()
.getRootEditPart()));
}
// process all edit parts and determine which are affected by the
// current marquee selection
Collection marqueeSelectedEditParts = new ArrayList();
for (Iterator iterator = editPartsToProcess.iterator(); iterator
.hasNext();) {
GraphicalEditPart editPart = (GraphicalEditPart) iterator.next();
if (isMarqueeSelectable(editPart)
&& isPrimaryMarqueeSelectedEditPart(editPart)) {
marqueeSelectedEditParts.add(editPart);
}
}
return marqueeSelectedEditParts;
}
/**
* Responsible of calculating those edit parts that should be regarded as
* being indirectly affected by the marquee selection. By default, the
* method calculates which edit parts are potential candidates based on the
* current marquee behavior and delegates to
* {@link #isMarqueeSelectable(GraphicalEditPart)} and
* {@link #isSecondaryMarqueeSelectedEditPart(Collection, EditPart)} to
* decide whether the candidate is to be included in the marquee selection.
*
* @param directlyMarqueeSelectedEditParts
* A collection containing those {@link EditPart}s that were
* already identified as being directly affected by the marquee
* selection
* @return A {@link Collection} containing all {@link EditPart}s that are
* indirectly affected by the current marquee selection
* @since 3.7
*/
private Collection calculateSecondaryMarqueeSelectedEditParts(
Collection directlyMarqueeSelectedEditParts) {
Collection editPartsToProcess = new HashSet();
for (Iterator iterator = directlyMarqueeSelectedEditParts.iterator(); iterator
.hasNext();) {
GraphicalEditPart marqueeSelectedEditPart = (GraphicalEditPart) iterator
.next();
editPartsToProcess.addAll(marqueeSelectedEditPart
.getSourceConnections());
editPartsToProcess.addAll(marqueeSelectedEditPart
.getTargetConnections());
}
// process all edit parts and decide, whether they are indirectly
// affected by marquee selection
Collection secondaryMarqueeSelectedEditParts = new HashSet();
for (Iterator iterator = editPartsToProcess.iterator(); iterator
.hasNext();) {
GraphicalEditPart editPart = (GraphicalEditPart) iterator.next();
if (isSecondaryMarqueeSelectedEditPart(
directlyMarqueeSelectedEditParts, editPart)) {
secondaryMarqueeSelectedEditParts.add(editPart);
}
}
return secondaryMarqueeSelectedEditParts;
}
private Request createTargetRequest() {
return MARQUEE_REQUEST;
}
/**
* Erases feedback if necessary and puts the tool into the terminal state.
*/
public void deactivate() {
if (isInState(STATE_DRAG_IN_PROGRESS)) {
eraseMarqueeFeedback();
eraseTargetFeedback();
}
super.deactivate();
allChildren.clear();
setState(STATE_TERMINAL);
}
private void eraseMarqueeFeedback() {
if (marqueeRectangleFigure != null) {
removeFeedback(marqueeRectangleFigure);
marqueeRectangleFigure = null;
}
}
private void eraseTargetFeedback() {
if (selectedEditParts == null)
return;
Iterator oldEditParts = selectedEditParts.iterator();
while (oldEditParts.hasNext()) {
EditPart editPart = (EditPart) oldEditParts.next();
editPart.eraseTargetFeedback(getTargetRequest());
}
}
/**
* @see org.eclipse.gef.tools.AbstractTool#getCommandName()
*/
protected String getCommandName() {
return REQ_SELECTION;
}
/**
* Returns the current marquee selection rectangle.
*
* @return A {@link Rectangle} representing the current marquee selection.
* @since 3.7
*/
protected Rectangle getCurrentMarqueeSelectionRectangle() {
return new Rectangle(getStartLocation(), getLocation());
}
/**
* Returns the current selection mode, i.e. default, append, or toggle
*
* @return on of {@link #DEFAULT_MODE}, {@link #APPEND_MODE}, or
* {@link #TOGGLE_MODE}
* @since 3.7
*/
protected int getCurrentSelectionMode() {
return mode;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#getDebugName()
*/
protected String getDebugName() {
return "Marquee Tool: " + marqueeBehavior;//$NON-NLS-1$
}
private IFigure getMarqueeFeedbackFigure() {
if (marqueeRectangleFigure == null) {
marqueeRectangleFigure = new MarqueeRectangleFigure();
addFeedback(marqueeRectangleFigure);
}
return marqueeRectangleFigure;
}
private Request getTargetRequest() {
if (targetRequest == null)
targetRequest = createTargetRequest();
return targetRequest;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleButtonDown(int)
*/
protected boolean handleButtonDown(int button) {
if (!isCurrentViewerGraphical())
return true;
if (button != 1) {
setState(STATE_INVALID);
handleInvalidInput();
}
if (stateTransition(STATE_INITIAL, STATE_DRAG_IN_PROGRESS)) {
if (getCurrentInput().isModKeyDown(SWT.MOD1))
setSelectionMode(TOGGLE_MODE);
else if (getCurrentInput().isShiftKeyDown())
setSelectionMode(APPEND_MODE);
else
setSelectionMode(DEFAULT_MODE);
}
return true;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
*/
protected boolean handleButtonUp(int button) {
if (stateTransition(STATE_DRAG_IN_PROGRESS, STATE_TERMINAL)) {
eraseTargetFeedback();
eraseMarqueeFeedback();
performMarqueeSelect();
}
handleFinished();
return true;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress()
*/
protected boolean handleDragInProgress() {
if (isInState(STATE_DRAG | STATE_DRAG_IN_PROGRESS)) {
showMarqueeFeedback();
eraseTargetFeedback();
selectedEditParts = calculateMarqueeSelectedEditParts();
showTargetFeedback();
}
return true;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleFocusLost()
*/
protected boolean handleFocusLost() {
if (isInState(STATE_DRAG | STATE_DRAG_IN_PROGRESS)) {
handleFinished();
return true;
}
return false;
}
/**
* This method is called when mouse or keyboard input is invalid and erases
* the feedback.
*
* @return <code>true</code>
*/
protected boolean handleInvalidInput() {
eraseTargetFeedback();
eraseMarqueeFeedback();
return true;
}
/**
* Handles high-level processing of a key down event. KeyEvents are
* forwarded to the current viewer's {@link KeyHandler}, via
* {@link KeyHandler#keyPressed(KeyEvent)}.
*
* @see AbstractTool#handleKeyDown(KeyEvent)
*/
protected boolean handleKeyDown(KeyEvent e) {
if (super.handleKeyDown(e))
return true;
if (getCurrentViewer().getKeyHandler() != null)
return getCurrentViewer().getKeyHandler().keyPressed(e);
return false;
}
/**
* Decides whether the given edit part may potentially be included in the
* current marquee selection.
*
* @param editPart
* the {@link EditPart} of interest
* @return <code>true</code> if the given edit part may be included into the
* marquee selection, <code>false</code> otherwise
* @since 3.7
*/
protected boolean isMarqueeSelectable(GraphicalEditPart editPart) {
// IMPORTANT: MarqueeSelectionTool is not a TargetingTool, thus the
// pre-selection does not depend on hit-testing. Therefore, the visible
// state of the edit part's figure has to be taken into consideration as
// well.
return editPart.getTargetEditPart(MARQUEE_REQUEST) == editPart
&& editPart.isSelectable()
&& FigureUtilities.isNotFullyClipped(editPart.getFigure());
}
/**
* Determines which edit parts are directly affected by the current marquee
* selection. Calculation is performed by regarding the current marquee
* selection rectangle ( {@link #getCurrentMarqueeSelectionRectangle()}),
* taking into consideration the current marquee behavior (contained vs.
* touched) that was provided ( {@link #setMarqueeBehavior(int)} ).
*
* @param editPart
* the {@link EditPart} whose state is to be determined
* @return <code>true</code> if the {@link EditPart} should be regarded as
* being included in the current marquee selection,
* <code>false</code> otherwise.
* @since 3.7
*/
private boolean isPrimaryMarqueeSelectedEditPart(GraphicalEditPart editPart) {
// figure bounds are used to determine if edit part is included in
// selection
IFigure figure = editPart.getFigure();
Rectangle r = figure.getBounds().getCopy();
figure.translateToAbsolute(r);
boolean included = false;
Rectangle marqueeSelectionRectangle = getCurrentMarqueeSelectionRectangle();
if (editPart instanceof ConnectionEditPart) {
if (marqueeBehavior == BEHAVIOR_CONNECTIONS_TOUCHED
|| marqueeBehavior == BEHAVIOR_CONNECTIONS_CONTAINED) {
if (marqueeSelectionRectangle.intersects(r)) {
// children will contain ConnectionEditParts only in case
// behavior is BEHAVIOR_CONNECTIONS_TOUCHED or
// BEHAVIOR_CONNECTIONS_CONTAINED
Rectangle relMarqueeRect = Rectangle.SINGLETON;
figure.translateToRelative(relMarqueeRect
.setBounds(marqueeSelectionRectangle));
if (marqueeBehavior == BEHAVIOR_CONNECTIONS_TOUCHED) {
included = ((Connection) figure).getPoints()
.intersects(relMarqueeRect);
} else if (marqueeBehavior == BEHAVIOR_CONNECTIONS_CONTAINED) {
included = relMarqueeRect
.contains(((Connection) figure).getPoints()
.getBounds());
}
}
}
} else {
// otherwise children will only be 'node' edit parts
if (marqueeBehavior == BEHAVIOR_NODES_TOUCHED
|| marqueeBehavior == BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS) {
included = marqueeSelectionRectangle.intersects(r);
} else if (marqueeBehavior == BEHAVIOR_NODES_CONTAINED
|| marqueeBehavior == BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS) {
included = marqueeSelectionRectangle.contains(r);
}
}
return included;
}
/**
* Determines which edit parts are indirectly affected by the current
* marquee selection through those edit parts being directly affected. In
* case of {@link #BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS} or
* {@link #BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS} marquee behavior,
* this method will be used to calculate the related connections after all
* respective nodes have been identified as primary selected edit parts.
*
* @param directlyMarqueeSelectedEditParts
* A collection of {@link EditPart}s which are regarded to be
* directly included in the current marquee selection.
* @param editPart
* the {@link EditPart} of concern
* @return <code>true</code> if the {@link EditPart} should be regarded as
* being included in the current marquee selection,
* <code>false</code> otherwise.
* @since 3.7
*/
private boolean isSecondaryMarqueeSelectedEditPart(
Collection directlyMarqueeSelectedEditParts, EditPart editPart) {
boolean included = false;
if (editPart instanceof ConnectionEditPart
&& (marqueeBehavior == BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS || marqueeBehavior == BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS)) {
// connections are included, if related nodes are included
ConnectionEditPart connection = (ConnectionEditPart) editPart;
GraphicalEditPart source = (GraphicalEditPart) connection
.getSource();
GraphicalEditPart target = (GraphicalEditPart) connection
.getTarget();
boolean sourceIncludedInMarqueeSelection = directlyMarqueeSelectedEditParts
.contains(source);
boolean targetIncludedInMarqueeSelection = directlyMarqueeSelectedEditParts
.contains(target);
if (mode == DEFAULT_MODE) {
// in default mode, select connection if source and
// target are included in marqee selection
included = sourceIncludedInMarqueeSelection
&& targetIncludedInMarqueeSelection;
} else if (mode == APPEND_MODE) {
// in append mode, the current viewer selection is of interest
// as well, so select connection if not already selected and
// source and target are already selected or will get selected
included = connection.getSelected() == EditPart.SELECTED_NONE
&& (getCurrentViewer().getSelectedEditParts().contains(
source) || sourceIncludedInMarqueeSelection)
&& (getCurrentViewer().getSelectedEditParts().contains(
target) || targetIncludedInMarqueeSelection);
} else if (mode == TOGGLE_MODE) {
if (connection.getSelected() == EditPart.SELECTED_NONE) {
// connection is currently deselected, include it in the
// marquee selection, i.e. select it, if one of
// source or target will become selected in the new viewer
// selection
included = ((source.getSelected() == EditPart.SELECTED_NONE && sourceIncludedInMarqueeSelection) || (source
.getSelected() != EditPart.SELECTED_NONE && !sourceIncludedInMarqueeSelection))
&& ((target.getSelected() == EditPart.SELECTED_NONE && targetIncludedInMarqueeSelection) || (target
.getSelected() != EditPart.SELECTED_NONE && !targetIncludedInMarqueeSelection));
} else {
// connection is currently selected, include it in marquee
// selection, i.e. deselect it, if one of source or target
// will become deselected in the new viewer selection
included = (source.getSelected() != EditPart.SELECTED_NONE && sourceIncludedInMarqueeSelection)
|| (target.getSelected() != EditPart.SELECTED_NONE && targetIncludedInMarqueeSelection);
}
}
}
return included;
}
/**
* MarqueeSelectionTool is only interested in GraphicalViewers, not
* TreeViewers.
*
* @see org.eclipse.gef.tools.AbstractTool#isViewerImportant(org.eclipse.gef.EditPartViewer)
*/
protected boolean isViewerImportant(EditPartViewer viewer) {
return isCurrentViewerGraphical();
}
private boolean isCurrentViewerGraphical() {
return getCurrentViewer() instanceof GraphicalViewer;
}
/**
* Calculates and sets a new viewer selection based on the current marquee
* selection.
*
* By default, this method delegates to
* {@link #calculateMarqueeSelectedEditParts()} to obtain the set of edit
* parts, which should be regarded as being affected by the current marquee
* selection. It then calculates a new viewer selection based on the current
* selection state of all affected edit parts and the current selection mode
* of the tool ( {@link #getCurrentSelectionMode()}), as well as the current
* selection of the viewer (in case of APPEND mode), which is then passed to
* the current viewer.
*
* @since 3.7
*/
protected void performMarqueeSelect() {
// determine which edit parts are affected by the current marquee
// selection
Collection marqueeSelectedEditParts = calculateMarqueeSelectedEditParts();
// calculate nodes/connections that are to be selected/deselected,
// dependent on the current mode of the tool
Collection editPartsToSelect = new LinkedHashSet();
Collection editPartsToDeselect = new HashSet();
for (Iterator iterator = marqueeSelectedEditParts.iterator(); iterator
.hasNext();) {
EditPart affectedEditPart = (EditPart) iterator.next();
if (affectedEditPart.getSelected() == EditPart.SELECTED_NONE
|| getCurrentSelectionMode() != TOGGLE_MODE)
editPartsToSelect.add(affectedEditPart);
else
editPartsToDeselect.add(affectedEditPart);
}
// include the current viewer selection, if not in DEFAULT mode.
if (getCurrentSelectionMode() != DEFAULT_MODE) {
editPartsToSelect.addAll(getCurrentViewer().getSelectedEditParts());
editPartsToSelect.removeAll(editPartsToDeselect);
}
getCurrentViewer().setSelection(
new StructuredSelection(editPartsToSelect.toArray()));
}
/**
* Sets the type of parts that this tool will select. This method should
* only be invoked once: when the tool is being initialized.
*
* @param type
* {@link #BEHAVIOR_CONNECTIONS_TOUCHED} or
* {@link #BEHAVIOR_CONNECTIONS_CONTAINED}
* {@link #BEHAVIOR_NODES_TOUCHED} or
* {@link #BEHAVIOR_NODES_CONTAINED} or
* {@link #BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS} or
* {@link #BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS}
* @since 3.1
*/
public void setMarqueeBehavior(int type) {
if (type != BEHAVIOR_CONNECTIONS_TOUCHED
&& type != BEHAVIOR_CONNECTIONS_CONTAINED
&& type != BEHAVIOR_NODES_TOUCHED
&& type != BEHAVIOR_NODES_TOUCHED_AND_RELATED_CONNECTIONS
&& type != BEHAVIOR_NODES_CONTAINED
&& type != BEHAVIOR_NODES_CONTAINED_AND_RELATED_CONNECTIONS)
throw new IllegalArgumentException(
"Invalid marquee behaviour specified."); //$NON-NLS-1$
marqueeBehavior = type;
}
private void setSelectionMode(int mode) {
this.mode = mode;
}
/**
* @see org.eclipse.gef.Tool#setViewer(org.eclipse.gef.EditPartViewer)
*/
public void setViewer(EditPartViewer viewer) {
if (viewer == getCurrentViewer())
return;
super.setViewer(viewer);
if (viewer instanceof GraphicalViewer)
setDefaultCursor(SharedCursors.CROSS);
else
setDefaultCursor(SharedCursors.NO);
}
private void showMarqueeFeedback() {
Rectangle rect = getCurrentMarqueeSelectionRectangle().getCopy();
getMarqueeFeedbackFigure().translateToRelative(rect);
getMarqueeFeedbackFigure().setBounds(rect);
}
private void showTargetFeedback() {
for (Iterator itr = selectedEditParts.iterator(); itr.hasNext();) {
EditPart editPart = (EditPart) itr.next();
editPart.showTargetFeedback(getTargetRequest());
}
}
}