blob: 1f274c561d3282de7eca0da2a310b3d26b4705c6 [file] [log] [blame]
/*******************************************************************************
* <copyright>
*
* Copyright (c) 2014 SRC
* 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:
* pjpaulin - Bug 352120 - Initial API, implementation and documentation
* mwenz - Bug 394315 - Enable injecting behavior objects in DiagramEditor
* mwenz - Bug 401859 - Graphiti DiagramEditor#dispose() does not release the editor related objects
* mwenz - Bug 407510 - Color background without Grid Layer turned to gray
* fvelasco - Bug 403664 - Enable DoubleClickFeature on the diagram background
* mwenz - Bug 433650 - Editor in in dirty state after a Save
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.ui.editor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.draw2d.FigureCanvas;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.transaction.RecordingCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.SnapToGrid;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackEvent;
import org.eclipse.gef.commands.CommandStackEventListener;
import org.eclipse.gef.editparts.GridLayer;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.AlignmentAction;
import org.eclipse.gef.ui.actions.DirectEditAction;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.actions.MatchHeightAction;
import org.eclipse.gef.ui.actions.MatchWidthAction;
import org.eclipse.gef.ui.actions.ToggleGridAction;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.palette.FlyoutPaletteComposite.FlyoutPreferences;
import org.eclipse.gef.ui.palette.PaletteViewerProvider;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.graphiti.DiagramScrollingBehavior;
import org.eclipse.graphiti.dt.IDiagramTypeProvider;
import org.eclipse.graphiti.features.IAddFeature;
import org.eclipse.graphiti.features.IFeature;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IPrintFeature;
import org.eclipse.graphiti.features.ISaveImageFeature;
import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.IContext;
import org.eclipse.graphiti.internal.command.AddFeatureCommandWithContext;
import org.eclipse.graphiti.internal.command.FeatureCommandWithContext;
import org.eclipse.graphiti.internal.command.GenericFeatureCommandWithContext;
import org.eclipse.graphiti.internal.services.GraphitiInternal;
import org.eclipse.graphiti.mm.pictograms.Diagram;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.platform.IDiagramEditor;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.tb.DefaultToolBehaviorProvider;
import org.eclipse.graphiti.tb.IToolBehaviorProvider;
import org.eclipse.graphiti.ui.internal.T;
import org.eclipse.graphiti.ui.internal.action.CopyAction;
import org.eclipse.graphiti.ui.internal.action.DeleteAction;
import org.eclipse.graphiti.ui.internal.action.FeatureExecutionHandler;
import org.eclipse.graphiti.ui.internal.action.PasteAction;
import org.eclipse.graphiti.ui.internal.action.PrintGraphicalViewerAction;
import org.eclipse.graphiti.ui.internal.action.RemoveAction;
import org.eclipse.graphiti.ui.internal.action.SaveImageAction;
import org.eclipse.graphiti.ui.internal.action.ToggleContextButtonPadAction;
import org.eclipse.graphiti.ui.internal.action.UpdateAction;
import org.eclipse.graphiti.ui.internal.command.GefCommandWrapper;
import org.eclipse.graphiti.ui.internal.config.ConfigurationProvider;
import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal;
import org.eclipse.graphiti.ui.internal.contextbuttons.ContextButtonManagerForPad;
import org.eclipse.graphiti.ui.internal.contextbuttons.IContextButtonManager;
import org.eclipse.graphiti.ui.internal.dnd.GFTemplateTransferDropTargetListener;
import org.eclipse.graphiti.ui.internal.dnd.ObjectsTransferDropTargetListener;
import org.eclipse.graphiti.ui.internal.editor.DiagramChangeListener;
import org.eclipse.graphiti.ui.internal.editor.DomainModelChangeListener;
import org.eclipse.graphiti.ui.internal.editor.GFCommandStack;
import org.eclipse.graphiti.ui.internal.editor.GFFigureCanvas;
import org.eclipse.graphiti.ui.internal.editor.GFScrollingGraphicalViewer;
import org.eclipse.graphiti.ui.internal.editor.GraphitiScrollingGraphicalViewer;
import org.eclipse.graphiti.ui.internal.util.gef.ScalableRootEditPartAnimated;
import org.eclipse.graphiti.ui.platform.IConfigurationProvider;
import org.eclipse.graphiti.ui.services.GraphitiUi;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.util.TransferDropTargetListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
/**
* Provides the common functionality needed to display and manage diagrams.
*
* Diagrams can be displayed either in a simple SWT {@link Composite}, in a
* {@link ViewPart} or in an {@link IEditorPart}, so it's not possible to
* provide common functionality through sub-classing.
*
* @since 0.10
*/
public class DiagramBehavior implements IDiagramBehaviorUI {
private IDiagramContainerUI diagramContainer;
private DefaultUpdateBehavior updateBehavior;
private DefaultPaletteBehavior paletteBehaviour;
private DefaultPersistencyBehavior persistencyBehavior;
private DefaultMarkerBehavior markerBehavior;
private DefaultRefreshBehavior refreshBehavior;
private PictogramElement pictogramElementsForSelection[];
private IConfigurationProviderInternal configurationProvider;
private Point mouseLocation;
private KeyHandler keyHandler;
private DiagramScrollingBehavior diagramScrollingBehavior;
private boolean directEditingActive = false;
private CommandStackEventListener gefCommandStackListener;
private DiagramChangeListener diagramChangeListener;
private DomainModelChangeListener domainModelListener;
private IDiagramEditorInput diagramEditorInput;
private String editorInitializationError = null;
private IWorkbenchPart parentPart;
private ContextMenuProvider contextMenuProvider = null;
public DiagramBehavior(IDiagramContainerUI diagramContainer) {
this.diagramContainer = diagramContainer;
}
/**
* Returns the associated container displaying the diagram of this behavior
* object.
*
* @return The associated {@link IDiagramContainerUI} instance.
*/
public IDiagramContainerUI getDiagramContainer() {
return diagramContainer;
}
/**
* Creates the behavior extension that deals with markers. See
* {@link DefaultMarkerBehavior} for details and the default implementation.
* Override to change the marker behavior.
*
* @return a new instance of {@link DefaultMarkerBehavior}
*/
protected DefaultMarkerBehavior createMarkerBehavior() {
return new DefaultMarkerBehavior(this);
}
/**
* Returns the instance of the marker behavior that is used with this
* behavior. To change the behavior override {@link #createMarkerBehavior()}
* .
*
* @return the used instance of the marker behavior, by default a
* {@link DefaultMarkerBehavior}.
*/
protected DefaultMarkerBehavior getMarkerBehavior() {
return markerBehavior;
}
/**
* Creates the behavior extension that deals with the update handling. See
* {@link DefaultUpdateBehavior} for details and the default implementation.
* Override to change the update behavior.
*
* @return a new instance of {@link DefaultUpdateBehavior}
*/
protected DefaultUpdateBehavior createUpdateBehavior() {
return new DefaultUpdateBehavior(this);
}
/**
* Returns the instance of the update behavior that is used with this
* behavior. To change the behavior override {@link #createUpdateBehavior()}
* .
*
* @return the used instance of the marker behavior, by default a
* {@link DefaultUpdateBehavior}.
*/
public DefaultUpdateBehavior getUpdateBehavior() {
return updateBehavior;
}
/**
* Creates the behavior extension that deals with the palette handling. See
* {@link DefaultPaletteBehavior} for details and the default
* implementation. Override to change the palette behavior.
*
* @return a new instance of {@link DefaultPaletteBehavior}
*/
protected DefaultPaletteBehavior createPaletteBehaviour() {
return new DefaultPaletteBehavior(this);
}
/**
* Returns the instance of the palette behavior that is used with this
* behavior. To change the behavior override
* {@link #createPaletteBehaviour()} .
*
* @return the used instance of the palette behavior, by default a
* {@link DefaultPaletteBehavior}.
*/
protected DefaultPaletteBehavior getPaletteBehavior() {
return paletteBehaviour;
}
/**
* Creates the behavior extension that deals with the persistence handling.
* See {@link DefaultPersistencyBehavior} for details and the default
* implementation. Override to change the persistence behavior.
*
* @return a new instance of {@link DefaultPersistencyBehavior}
*/
protected DefaultPersistencyBehavior createPersistencyBehavior() {
return new DefaultPersistencyBehavior(this);
}
/**
* Returns the instance of the persistency behavior that is used with this
* behavior. To change the behavior override
* {@link #createPersistencyBehavior()} .
*
* @return the used instance of the persistency behavior, by default a
* {@link DefaultPersistencyBehavior}.
*/
protected DefaultPersistencyBehavior getPersistencyBehavior() {
return persistencyBehavior;
}
/**
* Creates the behavior extension that deals with the refresh handling. See
* {@link DefaultRefreshBehavior} for details and the default
* implementation. Override to change the refresh behavior.
*
* @return a new instance of {@link DefaultRefreshBehavior}
*/
protected DefaultRefreshBehavior createRefreshBehavior() {
return new DefaultRefreshBehavior(this);
}
/**
* Returns the instance of the refresh behavior that is used with this
* behavior. To change the behavior override
* {@link #createRefreshBehavior()} .
*
* @return the used instance of the refresh behavior, by default a
* {@link DefaultRefreshBehavior}.
*/
public DefaultRefreshBehavior getRefreshBehavior() {
return refreshBehavior;
}
// ------------------ Initialization ---------------------------------------
/**
* Hook to initialize the default sub behavior instances used by this editor
* behavior. The default implementation simply delegates to the create
* methods for the various objects. In case other default behavior
* implementation should be used, clients should override these create
* methods instead of this method.
*
* @see #createMarkerBehavior()
* @see #createUpdateBehavior()
* @see #createPaletteBehaviour()
* @see #createPersistencyBehavior()
* @see #createRefreshBehavior()
*/
protected void initDefaultBehaviors() {
// Initialize behavior objects first, they are needed already within the
// init method. We cannot create these objects in the constructor of
// diagram editor because that would prevent injecting them, see
// Bugzilla 394315
markerBehavior = createMarkerBehavior();
updateBehavior = createUpdateBehavior();
paletteBehaviour = createPaletteBehaviour();
persistencyBehavior = createPersistencyBehavior();
refreshBehavior = createRefreshBehavior();
}
/**
* Sets the given {@link IDiagramEditorInput} object as the input for this
* behavior instance. The default implementation here cares about loading
* the diagram from the EMF {@link Resource} the input points to, sets the
* ID of the {@link IDiagramTypeProvider} for the diagram given in the
* input, registers listeners (by delegating to
* {@link #registerDiagramResourceSetListener()} and
* {@link #registerBusinessObjectsListener()}) and does the refreshing of
* the UI.
*
* @param input
* the {@link DiagramEditorInput} instance to use within this
* behavior.
*/
protected void setInput(IDiagramEditorInput input) {
// Check the input
if (input == null) {
throw new IllegalArgumentException("The IEditorInput must not be null"); //$NON-NLS-1$
}
diagramEditorInput = (IDiagramEditorInput) input;
Diagram diagram = getPersistencyBehavior().loadDiagram(diagramEditorInput.getUri());
// can happen if editor is started with invalid URI
if (diagram == null) {
editorInitializationError = "No Diagram found for URI '" + diagramEditorInput.getUri().toString(); //$NON-NLS-1$
return;
}
String providerId = diagramEditorInput.getProviderId();
// if provider is null then take the first installed provider for
// this diagram type
if (providerId == null) {
providerId = GraphitiUi.getExtensionManager().getDiagramTypeProviderId(diagram.getDiagramTypeId());
diagramEditorInput.setProviderId(providerId);
}
if (providerId == null) {
editorInitializationError = "DiagramEditorInput does not convey a Provider ID '" + diagramEditorInput; //$NON-NLS-1$
return;
}
Assert.isNotNull(providerId, "DiagramEditorInput does not convey a Provider ID '" + diagramEditorInput //$NON-NLS-1$
+ "'. . See the error log for details."); //$NON-NLS-1$
// Get according diagram-type-provider
IDiagramTypeProvider diagramTypeProvider = GraphitiUi.getExtensionManager().createDiagramTypeProvider(
providerId);
if (diagramTypeProvider == null) {
editorInitializationError = "Could not find diagram type provider for " + diagram.getDiagramTypeId(); //$NON-NLS-1$
return;
}
diagramTypeProvider.init(diagram, this);
IConfigurationProviderInternal configurationProvider = new ConfigurationProvider(this, diagramTypeProvider);
setConfigurationProvider(configurationProvider);
getRefreshBehavior().handleAutoUpdateAtStartup();
registerBusinessObjectsListener();
registerDiagramResourceSetListener();
diagramContainer.refreshTitle();
}
/**
* Adds the needed GEF listeners after the edit domain is initialized
*/
protected void addGefListeners() {
getDiagramTypeProvider().postInit();
gefCommandStackListener = new CommandStackEventListener() {
public void stackChanged(CommandStackEvent event) {
// Only fire if triggered from UI thread
if (Display.getCurrent() != null) {
diagramContainer.updateDirtyState();
// Promote the changes to the command stack also to the
// action bars and registered actions to correctly reflect
// e.g. undo/redo in the menu (introduced to enable removing
// NOP commands from the command stack
diagramContainer.commandStackChanged(event);
}
}
};
getEditDomain().getCommandStack().addCommandStackEventListener(gefCommandStackListener);
}
/**
* Creates the GraphicalViewer on the specified {@link Composite} and
* initializes it.
*
* @param parent
* the parent composite
*/
protected void createGraphicalViewer(Composite parent) {
GraphicalViewer viewer;
if (getDiagramScrollingBehavior() == DiagramScrollingBehavior.SCROLLBARS_ALWAYS_VISIBLE) {
viewer = new GFScrollingGraphicalViewer(this);
((GFScrollingGraphicalViewer) viewer).createGFControl(parent);
} else {
viewer = new GraphitiScrollingGraphicalViewer(this);
viewer.createControl(parent);
}
diagramContainer.setGraphicalViewer(viewer);
// FIXME: pull method configureGraphicalViewer up to IDiagramContainerUI
// and call it on the interface
if (diagramContainer instanceof DiagramEditor) {
((DiagramEditor) diagramContainer).configureGraphicalViewer();
} else if (diagramContainer instanceof DiagramComposite) {
((DiagramComposite) diagramContainer).configureGraphicalViewer();
} else {
try {
Method method = diagramContainer.getClass().getMethod("configureGraphicalViewer");
method.invoke(diagramContainer);
} catch (Exception e) {
T.racer()
.error("A class implementing IDiagramContainerUI must additionally implement also the method 'public void configureGraphicalViewer()' that initializes the GEF editor used inside the container. The method should have been added to the interface, but that was no longer possible because the bug was only detected in a late phase.",
e);
}
}
// end
diagramContainer.hookGraphicalViewer();
// FIXME: pull method initializeGraphicalViewer up to
// IDiagramContainerUI and call it on the interface
if (diagramContainer instanceof DiagramEditor) {
((DiagramEditor) diagramContainer).initializeGraphicalViewer();
} else if (diagramContainer instanceof DiagramComposite) {
((DiagramComposite) diagramContainer).initializeGraphicalViewer();
} else {
try {
Method method = diagramContainer.getClass().getMethod("initializeGraphicalViewer");
method.invoke(diagramContainer);
} catch (Exception e) {
T.racer()
.error("A class implementing IDiagramContainerUI must additionally implement also the method 'public void initializeGraphicalViewer()' that initializes the GEF editor used inside the container. The method should have been added to the interface, but that was no longer possible because the bug was only detected in a late phase.",
e);
}
}
// end
}
/**
* Called to configure the behavior viewer, before it receives its content.
* The default-implementation is for example doing the following: configure
* the ZoomManager, registering Actions... Here everything is done, which is
* independent of the {@link IConfigurationProvider}.
*
* @see org.eclipse.gef.ui.parts.GraphicalEditor#configureGraphicalViewer()
*/
protected void configureGraphicalViewer() {
ScrollingGraphicalViewer viewer = (ScrollingGraphicalViewer) diagramContainer.getGraphicalViewer();
ScalableRootEditPartAnimated rootEditPart = new ScalableRootEditPartAnimated(viewer, getConfigurationProvider()) {
protected GridLayer createGridLayer() {
return new org.eclipse.graphiti.ui.internal.util.draw2d.GridLayer(
(IConfigurationProviderInternal) getConfigurationProvider());
}
};
// configure ZoomManager
viewer.setRootEditPart(rootEditPart); // support
// animation of the zoom
ZoomManager zoomManager = rootEditPart.getZoomManager();
List<String> zoomLevels = new ArrayList<String>(3);
zoomLevels.add(ZoomManager.FIT_ALL);
zoomLevels.add(ZoomManager.FIT_WIDTH);
zoomLevels.add(ZoomManager.FIT_HEIGHT);
zoomManager.setZoomLevelContributions(zoomLevels);
IToolBehaviorProvider toolBehaviorProvider = getConfigurationProvider().getDiagramTypeProvider()
.getCurrentToolBehaviorProvider();
zoomManager.setZoomLevels(toolBehaviorProvider.getZoomLevels());
this.initActionRegistry(zoomManager);
// set the keyhandler.
viewer.setKeyHandler((new GraphicalViewerKeyHandler(viewer)).setParent(getCommonKeyHandler()));
// settings for grid and guides
Diagram diagram = getConfigurationProvider().getDiagram();
boolean snapToGrid = diagram.isSnapToGrid();
int horizontalGridUnit = diagram.getGridUnit();
int verticalGridUnit = diagram.getVerticalGridUnit();
if (verticalGridUnit == -1) {
// No vertical grid unit set (or old diagram before 0.8): use
// vertical grid unit
verticalGridUnit = horizontalGridUnit;
}
boolean gridVisisble = (horizontalGridUnit > 0) && (verticalGridUnit > 0);
viewer.setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE, new Boolean(gridVisisble));
viewer.setProperty(SnapToGrid.PROPERTY_GRID_ENABLED, new Boolean(snapToGrid));
viewer.setProperty(SnapToGrid.PROPERTY_GRID_SPACING, new Dimension(horizontalGridUnit, verticalGridUnit));
viewer.setProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED, toolBehaviorProvider.isShowGuides());
// context button manager
IConfigurationProviderInternal configurationProvider = (IConfigurationProviderInternal) this
.getConfigurationProvider();
configurationProvider.setContextButtonManager(new ContextButtonManagerForPad(this, configurationProvider
.getResourceRegistry()));
/* sw: make scroll bars always visible */
if (getDiagramScrollingBehavior() == DiagramScrollingBehavior.SCROLLBARS_ALWAYS_VISIBLE) {
GFFigureCanvas figureCanvas = getGFFigureCanvas();
if (figureCanvas != null) {
figureCanvas.setScrollBarVisibility(FigureCanvas.ALWAYS);
}
}
}
/**
* Called to initialize the behavior viewer with its content. Here
* everything is done, which is dependent of the
* {@link IConfigurationProvider}.
*
* @see org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette#initializeGraphicalViewer()
*/
protected void initializeGraphicalViewer() {
// register Actions
IFeatureProvider featureProvider = getConfigurationProvider().getDiagramTypeProvider().getFeatureProvider();
if (featureProvider != null) {
IPrintFeature pf = featureProvider.getPrintFeature();
if (pf != null && parentPart != null) {
registerAction(new PrintGraphicalViewerAction(this, getConfigurationProvider()));
}
}
// setting ContextMenuProvider
contextMenuProvider = createContextMenuProvider();
GraphicalViewer graphicalViewer = diagramContainer.getGraphicalViewer();
if (contextMenuProvider != null) {
graphicalViewer.setContextMenu(contextMenuProvider);
// the registration allows an extension of the context-menu by other
// plugins
if (shouldRegisterContextMenu() && parentPart != null) {
parentPart.getSite().registerContextMenu(contextMenuProvider, graphicalViewer);
}
}
// set contents
graphicalViewer.setEditPartFactory(((IConfigurationProviderInternal) getConfigurationProvider())
.getEditPartFactory());
graphicalViewer.setContents(getConfigurationProvider().getDiagram());
getPaletteBehavior().initializeViewer();
graphicalViewer.getControl().addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
setMouseLocation(e.x, e.y);
}
});
List<TransferDropTargetListener> objectDropTargetListeners = createBusinessObjectDropTargetListeners();
for (TransferDropTargetListener dropTargetListener : objectDropTargetListeners) {
graphicalViewer.addDropTargetListener(dropTargetListener);
}
TransferDropTargetListener paletteDropTargetListener = createPaletteDropTargetListener();
if (paletteDropTargetListener != null) {
graphicalViewer.addDropTargetListener(paletteDropTargetListener);
}
}
/**
* Creates the drop target listener that is used for adding new objects from
* the palette via drag and drop. Clients may change the default behavior by
* providing their own drop target listener or disable drag and drop from
* the palette by returning null.
*
* @return An instance of the {@link TransferDropTargetListener} that
* handles dropping new objects from the palette or
* <code>null</code> to disable dropping from the palette.
* @since 0.10
*/
protected TransferDropTargetListener createPaletteDropTargetListener() {
return new GFTemplateTransferDropTargetListener(diagramContainer.getGraphicalViewer(), this);
}
/**
* Creates a list of drop target listeners that enable dropping domain
* objects into the diagram, e.g. from the project explorer. By adding
* additional listeners other sources may be enabled, simply returning an
* empty list will disable drag and drop into the editor.
*
* @return a {@link List} containing all the
* {@link TransferDropTargetListener} that shall be registered in
* the editor.
* @since 0.10
*/
protected List<TransferDropTargetListener> createBusinessObjectDropTargetListeners() {
ArrayList<TransferDropTargetListener> result = new ArrayList<TransferDropTargetListener>(1);
result.add(new ObjectsTransferDropTargetListener(diagramContainer.getGraphicalViewer()));
return result;
}
/**
* Returns if an error has occured while initializing this behavior and its
* components. In case this method reports <code>true</code> and error UI
* may be shown instead of the normal diagram viewer.
*
* @return <code>true</code> in case an error has occured,
* <code>false</code> otherwise
*/
protected String getEditorInitializationError() {
return editorInitializationError;
}
/**
* Creates the default error page in case an error occurred while
* initializing this behavior.
*
* @param parent
* The parent {@link Composite} to add the UI to
*/
protected void createErrorPartControl(Composite parent) {
Display display = parent.getDisplay();
// Define colors as desired, in high contrast mode use system defaults
Color backgroundColor;
final Color foregroundColor;
final Color separatorColor;
if (display.getHighContrast()) {
backgroundColor = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
foregroundColor = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
separatorColor = foregroundColor;
} else {
backgroundColor = display.getSystemColor(SWT.COLOR_WHITE);
foregroundColor = display.getSystemColor(SWT.COLOR_DARK_BLUE);
separatorColor = new Color(display, 152, 170, 203);
}
ScrolledComposite scrolledComposite = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL);
scrolledComposite.setAlwaysShowScrollBars(false);
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setExpandVertical(true);
scrolledComposite.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if (separatorColor != foregroundColor) {
// Dispose the color only if it was created as additional
// color
separatorColor.dispose();
}
}
});
Composite composite = new Composite(scrolledComposite, SWT.NONE);
composite.setBackground(backgroundColor);
composite.setLayout(new GridLayout());
Composite separator = new Composite(composite, SWT.NO_FOCUS);
separator.setBackground(separatorColor);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.heightHint = 2;
data.verticalIndent = 50;
separator.setLayoutData(data);
StyledText widget = new StyledText(composite, SWT.READ_ONLY | SWT.MULTI);
widget.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
widget.setText(editorInitializationError);
widget.setBackground(backgroundColor);
widget.setForeground(foregroundColor);
widget.setCaret(null);
scrolledComposite.setContent(composite);
scrolledComposite.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
// ------------------- Dirty state -----------------------------------------
/**
* Returns the dirty state of this behavior object
*
* @return <code>true</code> in case the stored saved command is different
* from the next undo command.
*/
protected boolean isDirty() {
return getPersistencyBehavior().isDirty();
}
// ---------------------- Palette --------------------------------------- //
/**
* Delegates to the method (or the method in a subclass of)
* {@link DefaultPaletteBehavior#createPaletteViewerProvider()
* #createPaletteViewerProvider()} to create the
* {@link PaletteViewerProvider} used inside the GEF editor.
*
* @return the {@link PaletteViewerProvider} to use
*/
protected final PaletteViewerProvider createPaletteViewerProvider() {
if (editorInitializationError != null) {
// Editor input is erroneous, show error page instead of diagram and
// do not initialize the palette to avoid exceptions
return null;
}
return paletteBehaviour.createPaletteViewerProvider();
}
/**
* Delegates to the method (or the method in a subclass of)
* {@link DefaultPaletteBehavior#getPalettePreferences()}. To change the
* palette override the behavior there.
*
* @return the {@link PaletteViewerProvider} preferences to use.
*/
protected final FlyoutPreferences getPalettePreferences() {
return getPaletteBehavior().getPalettePreferences();
}
/**
* Returns the {@link PaletteRoot} to use in the GEF editor by delegating to
* {@link DefaultPaletteBehavior#getPaletteRoot()}.
*
* @return the {@link PaletteRoot} to use
*/
protected final PaletteRoot getPaletteRoot() {
return getPaletteBehavior().getPaletteRoot();
}
// ---------------------- Refresh --------------------------------------- //
/**
* Triggers a complete refresh of the behavior visualization (content,
* title, tooltip, palette and decorators) by delegating to
* {@link DefaultRefreshBehavior#refresh()}.
*/
public void refresh() {
getRefreshBehavior().refresh();
}
/**
* Refreshes the rendering decorators (image decorators and the like) by
* delegating to
* {@link DefaultRefreshBehavior#refreshRenderingDecorators(PictogramElement)}
* for the given {@link PictogramElement}.
*
* @param pe
* the {@link PictogramElement} for which the decorators shall be
* refreshed.
*/
public void refreshRenderingDecorators(PictogramElement pe) {
getRefreshBehavior().refreshRenderingDecorators(pe);
}
/**
* Refreshes the palette to correctly reflect all available creation tools
* for the available create features and the currently enabled selection
* tools.
*/
public void refreshPalette() {
getPaletteBehavior().refreshPalette();
}
/**
* Refreshes the content of the editor (what's shown inside the diagram
* itself).
*/
public void refreshContent() {
Diagram currentDiagram = getDiagramTypeProvider().getDiagram();
if (GraphitiInternal.getEmfService().isObjectAlive(currentDiagram)) {
refresh();
} else {
IDiagramEditorInput diagramEditorInput = diagramContainer.getDiagramEditorInput();
// resolve diagram in reloaded resource
Diagram diagram = getPersistencyBehavior().loadDiagram(diagramEditorInput.getUri());
IDiagramTypeProvider diagramTypeProvider = getConfigurationProvider().getDiagramTypeProvider();
diagramTypeProvider.resourceReloaded(diagram);
// clean performance hashtables which have references
getRefreshBehavior().initRefresh();
// to old proxies
setPictogramElementsForSelection(null);
// create new edit parts
diagramContainer.getGraphicalViewer().setContents(diagram);
getRefreshBehavior().handleAutoUpdateAtReset();
}
}
// ---------------------- Selection ------------------------------------- //
/**
* Selects the given {@link PictogramElement}s in the diagram.
*
* @param pictogramElements
* an array of {@link PictogramElement}s to select.
*/
protected void selectPictogramElements(PictogramElement[] pictogramElements) {
List<EditPart> editParts = new ArrayList<EditPart>();
Map<?, ?> editPartRegistry = diagramContainer.getGraphicalViewer().getEditPartRegistry();
if (editPartRegistry != null) {
for (int i = 0; i < pictogramElements.length; i++) {
PictogramElement pe = pictogramElements[i];
Object obj = editPartRegistry.get(pe);
/*
* Add all EditParts to a list to be selected. Bug 324556: Only
* add EditParts that allow selection to the list, e.g.
* invisible objects will cause an IllegalArgumentException in
* AbstractEditPart.setSelected (GEF) when setSelected is
* called.
*/
if (obj instanceof EditPart && ((EditPart) obj).isSelectable()) {
editParts.add((EditPart) obj);
}
}
if (parentPart != null)
{
parentPart.getSite().getSelectionProvider()
.setSelection(new StructuredSelection(editParts));
}
if (editParts.size() > 0) {
final EditPart editpart = editParts.get(0);
// if the editPart is newly created it is possible that his
// figure has not a valid bounds. Hence we have to wait for
// the UI update (for the validation of the figure tree).
// Otherwise the reveal method can't work correctly.
Display.getDefault().asyncExec(new Runnable() {
public void run() {
diagramContainer.getGraphicalViewer().reveal(editpart);
}
});
}
}
}
/**
* Returns the {@link PictogramElement}s that are currently selected in the
* diagram.
*
* @return an array of {@link PictogramElement}s.
*/
public PictogramElement[] getSelectedPictogramElements() {
PictogramElement pe[] = new PictogramElement[0];
ISelectionProvider selectionProvider = null;
if (parentPart == null) {
selectionProvider = diagramContainer.getGraphicalViewer();
} else {
selectionProvider = parentPart.getSite().getSelectionProvider();
}
if (selectionProvider != null) {
ISelection s = selectionProvider.getSelection();
if (s instanceof IStructuredSelection) {
IStructuredSelection sel = (IStructuredSelection) s;
List<PictogramElement> list = new ArrayList<PictogramElement>();
for (Iterator<?> iter = sel.iterator(); iter.hasNext();) {
Object o = iter.next();
if (o instanceof EditPart) {
EditPart editPart = (EditPart) o;
if (editPart.getModel() instanceof PictogramElement) {
list.add((PictogramElement) editPart.getModel());
}
}
}
pe = list.toArray(new PictogramElement[0]);
}
}
return pe;
}
/**
* Sets one {@link PictogramElement} for later selection.
* <p>
* The methods {@link #getPictogramElementsForSelection()},
* {@link #setPictogramElementForSelection(PictogramElement)},
* {@link #setPictogramElementsForSelection(PictogramElement[])} and
* {@link #selectBufferedPictogramElements()} offer the possibility to use a
* deferred selection mechanism: via the setters, {@link PictogramElement}s
* can be stored for a selection operation that is triggered lateron during
* a general refresh via the method
* {@link #selectBufferedPictogramElements()}. This mechanism is used e.g.
* in the Graphiti framework in direct editing to restore the previous
* selection, but can also be used by clients.
*
* @param pictogramElement
* the {@link PictogramElement} that shall be stored for later
* selection
*/
public void setPictogramElementForSelection(PictogramElement pictogramElement) {
if (pictogramElement == null) {
pictogramElementsForSelection = null;
} else {
pictogramElementsForSelection = new PictogramElement[] { pictogramElement };
}
}
/**
* Sets {@link PictogramElement}s for later selection.
* <p>
* The methods {@link #getPictogramElementsForSelection()},
* {@link #setPictogramElementForSelection(PictogramElement)},
* {@link #setPictogramElementsForSelection(PictogramElement[])} and
* {@link #selectBufferedPictogramElements()} offer the possibility to use a
* deferred selection mechanism: via the setters, {@link PictogramElement}s
* can be stored for a selection operation that is triggered later on during
* a general refresh via the method
* {@link #selectBufferedPictogramElements()}. This mechanism is used e.g.
* in the Graphiti framework in direct editing to restore the previous
* selection, but can also be used by clients.
*
* @param pictogramElements
* the {@link PictogramElement}s that shall be stored for later
* selection
*/
protected void setPictogramElementsForSelection(PictogramElement[] pictogramElements) {
pictogramElementsForSelection = pictogramElements;
}
/**
* Triggers the selection for the {@link PictogramElement}s that are stored
* for later selection. Can be called e.g during a general refresh of the
* editor or after another operation needing another selection is finished
* (an example in the framework is direct editing).
* <p>
* The methods {@link #getPictogramElementsForSelection()},
* {@link #setPictogramElementForSelection(PictogramElement)},
* {@link #setPictogramElementsForSelection(PictogramElement[])} and
* {@link #selectBufferedPictogramElements()} offer the possibility to use a
* deferred selection mechanism: via the setters, {@link PictogramElement}s
* can be stored for a selection operation that is triggered later on during
* a general refresh via the method
* {@link #selectBufferedPictogramElements()}. This mechanism is used e.g.
* in the Graphiti framework in direct editing to restore the previous
* selection, but can also be used by clients.
*/
public void selectBufferedPictogramElements() {
if (getPictogramElementsForSelection() != null) {
selectPictogramElements(getPictogramElementsForSelection());
setPictogramElementsForSelection(null);
}
}
/**
* Returns the {@link PictogramElement}s that are set for later selection.
* <p>
* The methods {@link #getPictogramElementsForSelection()},
* {@link #setPictogramElementForSelection(PictogramElement)},
* {@link #setPictogramElementsForSelection(PictogramElement[])} and
* {@link #selectBufferedPictogramElements()} offer the possibility to use a
* deferred selection mechanism: via the setters, {@link PictogramElement}s
* can be stored for a selection operation that is triggered lateron during
* a general refresh via the method
* {@link #selectBufferedPictogramElements()}. This mechanism is used e.g.
* in the Graphiti framework in direct editing to restore the previous
* selection, but can also be used by clients.
*
* @return the {@link PictogramElement}s stored for later selection
*/
protected PictogramElement[] getPictogramElementsForSelection() {
return pictogramElementsForSelection;
}
// ---------------------- Other ----------------------------------------- //
/**
* Returns the EMF {@link TransactionalEditingDomain} used within this
* behavior object by delegating to the update behavior extension, by
* default {@link DefaultUpdateBehavior#getEditingDomain()}.
*
* @return the {@link TransactionalEditingDomain} instance used in the
* behavior
*/
public TransactionalEditingDomain getEditingDomain() {
return getUpdateBehavior().getEditingDomain();
}
/**
* The EMF {@link ResourceSet} used within this {@link DiagramBehavior}. The
* resource set is always associated in a 1:1 relation to the
* {@link TransactionalEditingDomain}.
*
* @return the resource set used within this behavior object
*/
public ResourceSet getResourceSet() {
ResourceSet ret = null;
TransactionalEditingDomain editingDomain = getEditingDomain();
if (editingDomain != null) {
ret = editingDomain.getResourceSet();
}
return ret;
}
/**
* Returns the {@link IDiagramTypeProvider} instance associated with this
* {@link DiagramBehavior}. There is always a 1:1 relation between the
* behavior and the provider.
*
* @return the associated {@link IDiagramTypeProvider} instance.
*/
public IDiagramTypeProvider getDiagramTypeProvider() {
IConfigurationProvider cfgProvider = getConfigurationProvider();
if (cfgProvider != null)
return cfgProvider.getDiagramTypeProvider();
return null;
}
/**
* Executes the given {@link IFeature} with the given {@link IContext} in
* the scope of this {@link DiagramBehavior}, meaning within its
* {@link TransactionalEditingDomain} and on its
* {@link org.eclipse.emf.common.command.CommandStack}.
*
* @param feature
* the feature to execute
* @param context
* the context to use. In case the passed feature is a
* {@link IAddFeature} this context needs to be an instance of
* {@link IAddContext}, otherwise an
* {@link AssertionFailedException} will be thrown.
* @return in case of an {@link IAddFeature} being passed as feature the
* newly added {@link PictogramElement} will be returned (in case
* the add method returning it), in all other cases
* <code>null</code>
*
* @since 0.9
*/
public Object executeFeature(IFeature feature, IContext context) {
Object returnValue = null;
DefaultEditDomain domain = diagramContainer.getEditDomain();
// Make sure the editor is valid
Assert.isNotNull(domain);
CommandStack commandStack = domain.getCommandStack();
// Create the correct feature command
FeatureCommandWithContext featureCommand = null;
if (feature instanceof IAddFeature) {
// Context must fit to the feature
Assert.isTrue(context instanceof IAddContext);
featureCommand = new AddFeatureCommandWithContext(feature, context);
} else {
featureCommand = new GenericFeatureCommandWithContext(feature, context);
}
// Execute the feature using the command
GefCommandWrapper commandWrapper = new GefCommandWrapper(featureCommand, getEditingDomain());
commandStack.execute(commandWrapper);
if (featureCommand instanceof AddFeatureCommandWithContext) {
// In case of an add feature, select the newly added shape
PictogramElement addedPictogramElement = ((AddFeatureCommandWithContext) featureCommand)
.getAddedPictogramElements();
if (addedPictogramElement != null) {
setPictogramElementForSelection(addedPictogramElement);
}
// Store the added pictogram element as return value
returnValue = addedPictogramElement;
}
return returnValue;
}
/**
* Should be called (e.g. by the various behavior instances) before mass EMF
* resource operations are triggered (e.g. saving all resources). Can be
* used to disable eventing for performance reasons. See
* {@link #enableAdapters()} as well.<br>
* Important note: make sure that you re-enable eventing using
* {@link #enableAdapters()} after the operation has finished (best in a
* finally clause to do that also in case of exceptions), otherwise strange
* errors may happen.
*/
protected void disableAdapters() {
markerBehavior.disableProblemIndicationUpdate();
updateBehavior.setAdapterActive(false);
}
/**
* Should be called by the various behavior instances after mass EMF
* resource operations have been triggered (e.g. saving all resources). Can
* be used to re-enable eventing after it was disabled for performance
* reasons. See {@link #disableAdapters()} as well.<br>
* Must be called after {@link #disableAdapters()} has been called and the
* operation has finshed (best in a finally clause to also enable the
* exception case), otherwise strange errors may occur within the editor.
*/
protected void enableAdapters() {
markerBehavior.enableProblemIndicationUpdate();
updateBehavior.setAdapterActive(true);
}
/**
* Checks if this behavior is alive.
*
* @return <code>true</code>, if editor contains a model connector and a
* valid Diagram, <code>false</code> otherwise.
*/
public boolean isAlive() {
IConfigurationProvider cp = getConfigurationProvider();
if (cp != null) {
TransactionalEditingDomain editingDomain = getEditingDomain();
Diagram diagram = cp.getDiagram();
if (editingDomain != null && GraphitiInternal.getEmfService().isObjectAlive(diagram)) {
return true;
}
}
return false;
}
/**
* Hook that is called by the holder of the
* {@link TransactionalEditingDomain} ({@link DefaultUpdateBehavior} or a
* subclass of it) after the editing domain has been initialized. Can be
* used to e.g. register additional listeners on the domain.<br>
* The default implementation notifies the marker behavior extension to
* register its listeners.
*/
protected void editingDomainInitialized() {
markerBehavior.initialize();
}
/**
* Implements the Eclipse {@link IAdaptable} interface. This implementation
* first delegates to the {@link IToolBehaviorProvider#getAdapter(Class)}
* method and checks if something is returned. In case the return value is
* <code>null</code> it returns adapters for ZoomManager,
* IPropertySheetPage, Diagram, KeyHandler, SelectionSynchronizer and
* IContextButtonManager. It also delegates to the super implementation in
* {@link GraphicalEditorWithFlyoutPalette#getAdapter(Class)}.
*
* @param type
* the type to which shall be adapted
* @return the adapter instance
*/
public Object getAdapter(@SuppressWarnings("rawtypes") Class type) {
IConfigurationProvider cfgProvider = getConfigurationProvider();
if (cfgProvider != null) {
IToolBehaviorProvider tbp = cfgProvider.getDiagramTypeProvider().getCurrentToolBehaviorProvider();
if (tbp != null) {
Object ret = tbp.getAdapter(type);
if (ret != null) {
return ret;
}
}
}
GraphicalViewer viewer = diagramContainer.getGraphicalViewer();
if (type == ZoomManager.class && viewer != null) {
return viewer.getProperty(ZoomManager.class.toString());
}
if (type == IPropertySheetPage.class) {
if (cfgProvider != null && diagramContainer instanceof ITabbedPropertySheetPageContributor) {
ITabbedPropertySheetPageContributor contributor = (ITabbedPropertySheetPageContributor) diagramContainer;
if (contributor.getContributorId() != null) {
return new TabbedPropertySheetPage(contributor);
}
}
return null; // not yet initialized
}
if (type == Diagram.class) {
IDiagramTypeProvider diagramTypeProvider = getDiagramTypeProvider();
if (diagramTypeProvider != null) {
return diagramTypeProvider.getDiagram();
} else {
return null;
}
}
if (type == KeyHandler.class) {
return getCommonKeyHandler();
}
if (type == IContextButtonManager.class) {
return ((IConfigurationProviderInternal) getConfigurationProvider()).getContextButtonManager();
}
if (type == IDiagramEditor.class) {
return getDiagramContainer();
}
return null;
}
/**
* Returns the {@link ConfigurationProvider} for this behavior. It is mainly
* a wrapper around varius objects that are connected to the diagram
* behavior.
*
* @return an {@link IConfigurationProvider} instance.
*/
protected IConfigurationProvider getConfigurationProvider() {
return configurationProvider;
}
/**
* Returns the contents {@link EditPart} of this behavior. This is the
* topmost EditPart in the {@link GraphicalViewer}.
*
* @return The contents {@link EditPart} of this behavior.
*/
public EditPart getContentEditPart() {
if (diagramContainer.getGraphicalViewer() != null) {
return diagramContainer.getGraphicalViewer().getContents();
}
return null;
}
/**
* Method to retrieve the GEF {@link EditPart} for a given
* {@link PictogramElement}.
*
* @param pe
* the {@link PictogramElement} to retrieve the GEF
* representation for
* @return the GEF {@link GraphicalEditPart} that represents the given
* {@link PictogramElement}.
*/
public GraphicalEditPart getEditPartForPictogramElement(PictogramElement pe) {
Map<?, ?> editPartRegistry = diagramContainer.getGraphicalViewer().getEditPartRegistry();
if (editPartRegistry != null) {
Object obj = editPartRegistry.get(pe);
if (obj instanceof GraphicalEditPart) {
GraphicalEditPart ep = (GraphicalEditPart) obj;
return ep;
}
}
return null;
}
/**
* Gets the current mouse location as a {@link Point}.
*
* @return the mouse location
*/
public Point getMouseLocation() {
if (mouseLocation == null) {
mouseLocation = new Point();
}
return mouseLocation;
}
/**
* Calculates the mouse location depending on scrollbars and zoom factor.
*
* @param nativeLocation
* the native location given as {@link Point}
* @return the {@link Point} of the real mouse location
*/
public Point calculateRealMouseLocation(Point nativeLocation) {
Point ret = new Point(nativeLocation);
Point viewLocation;
// view location depends on the current scroll bar position
if (getDiagramScrollingBehavior() == DiagramScrollingBehavior.SCROLLBARS_ALWAYS_VISIBLE) {
viewLocation = getGFFigureCanvas().getViewport().getViewLocation();
} else {
viewLocation = getFigureCanvas().getViewport().getViewLocation();
}
ret.x += viewLocation.x;
ret.y += viewLocation.y;
ZoomManager zoomManager = (ZoomManager) diagramContainer.getGraphicalViewer()
.getProperty(ZoomManager.class.toString());
ret = ret.getScaled(1 / zoomManager.getZoom());
return ret;
}
/**
* Returns if direct editing is currently active for this behavior.
*
* @return <code>true</code> in case direct editing is currently active
* within this editor, <code>false</code> otherwise.
*/
public boolean isDirectEditingActive() {
return directEditingActive;
}
/**
* Sets that direct editing is now active in the behavior or not. Note that
* this flag set to <code>true</code> does not actually start direct editing
* it is simply an indication that prevents certain operations from running
* (e.g. refresh)
*
* @param directEditingActive
* <code>true</code> to set the flag to direct editing currently
* active, <code>false</code> otherwise.
*/
public void setDirectEditingActive(boolean directEditingActive) {
this.directEditingActive = directEditingActive;
((IConfigurationProviderInternal) getConfigurationProvider()).getContextButtonManager()
.hideContextButtonsInstantly();
}
/**
* Returns the zoom level currently used in the diagram.
*
* @return the zoom level
*/
public double getZoomLevel() {
ZoomManager zoomManager = (ZoomManager) getAdapter(ZoomManager.class);
if (zoomManager == null)
return 1;
/*
* avoid long running calculations for large diagrams and zoom factors
* below 5%
*/
return Math.max(0.05D, zoomManager.getZoom());
}
/**
* Method to retrieve the Draw2D {@link IFigure} for a given
* {@link PictogramElement}.
*
* @param pe
* the {@link PictogramElement} to retrieve the Draw2D
* representation for
* @return the Draw2D {@link IFigure} that represents the given
* {@link PictogramElement}.
*/
public IFigure getFigureForPictogramElement(PictogramElement pe) {
GraphicalEditPart ep = getEditPartForPictogramElement(pe);
if (ep != null) {
return ep.getFigure();
}
return null;
}
private void setConfigurationProvider(IConfigurationProviderInternal configurationProvider) {
this.configurationProvider = configurationProvider;
// initialize configuration-provider depending on this editor
configurationProvider.setWorkbenchPart(parentPart);
if (diagramContainer.getGraphicalViewer() != null) {
initializeGraphicalViewer();
}
if (diagramContainer instanceof IEditorPart) {
DefaultEditDomain editDomain = new DefaultEditDomain((IEditorPart) diagramContainer);
diagramContainer.setEditDomain(editDomain);
}
CommandStack commandStack = new GFCommandStack(configurationProvider, getEditingDomain());
getEditDomain().setCommandStack(commandStack);
}
private void setMouseLocation(int x, int y) {
getMouseLocation().setLocation(x, y);
}
/**
* Returns a new {@link ContextMenuProvider}. Clients can return null, if no
* context-menu shall be displayed.
*
* @return A new instance of {@link ContextMenuProvider}.
*/
protected ContextMenuProvider createContextMenuProvider() {
return new DiagramEditorContextMenuProvider(diagramContainer.getGraphicalViewer(),
diagramContainer.getActionRegistry(),
getConfigurationProvider());
}
/**
* Allows subclasses to prevent that the diagram context menu should be
* registered for extensions at Eclipse. By default others can provide
* extensions to the menu (default return value of this method is
* <code>true</code>). By returning <code>false</code> any extension of the
* context menu can be prevented.
* <p>
* For details see Bugzilla 345347
* (https://bugs.eclipse.org/bugs/show_bug.cgi?id=345347).
*
* @return <code>true</code> in case extensions shall be allowed (default),
* <code>false</code> otherwise.
* @since 0.9
*/
protected boolean shouldRegisterContextMenu() {
return true;
}
/**
* Registers the given action with the Eclipse {@link ActionRegistry}.
*
* @param action
* the action to register
* @since 0.9
*/
protected void registerAction(IAction action) {
if (action == null || parentPart == null) {
return;
}
diagramContainer.getActionRegistry().registerAction(action);
if (action.getActionDefinitionId() != null) {
IHandlerService hs = (IHandlerService) parentPart.getSite()
.getService(IHandlerService.class);
hs.activateHandler(action.getActionDefinitionId(), new ActionHandler(action));
}
@SuppressWarnings("unchecked")
List<String> selectionActions = diagramContainer.getSelectionActions();
selectionActions.add(action.getId());
}
/**
* Initializes the action registry with the predefined actions (update,
* remove, delete, copy, paste, zooming, direct editing, alignment and
* toggling actions for the diagram grip and hiding of the context button
* pad.
*
* @param zoomManager
* the GEF zoom manager to use
*/
protected void initActionRegistry(ZoomManager zoomManager) {
if (parentPart == null)
{
return;
}
final ActionRegistry actionRegistry = diagramContainer.getActionRegistry();
@SuppressWarnings("unchecked")
final List<String> selectionActions = diagramContainer.getSelectionActions();
// register predefined actions (e.g. update, remove, delete, ...)
IAction action = new UpdateAction(parentPart, getConfigurationProvider());
actionRegistry.registerAction(action);
selectionActions.add(action.getId());
action = new RemoveAction(parentPart, getConfigurationProvider());
actionRegistry.registerAction(action);
selectionActions.add(action.getId());
action = new DeleteAction(parentPart, getConfigurationProvider());
actionRegistry.registerAction(action);
selectionActions.add(action.getId());
action = new CopyAction(parentPart, getConfigurationProvider());
actionRegistry.registerAction(action);
selectionActions.add(action.getId());
action = new PasteAction(parentPart, getConfigurationProvider());
actionRegistry.registerAction(action);
selectionActions.add(action.getId());
IFeatureProvider fp = getConfigurationProvider().getDiagramTypeProvider().getFeatureProvider();
if (fp != null) {
ISaveImageFeature sf = fp.getSaveImageFeature();
if (sf != null) {
action = new SaveImageAction(this, getConfigurationProvider());
actionRegistry.registerAction(action);
selectionActions.add(action.getId());
}
}
registerAction(new ZoomInAction(zoomManager));
registerAction(new ZoomOutAction(zoomManager));
registerAction(new DirectEditAction(parentPart));
registerAction(new AlignmentAction(parentPart, PositionConstants.LEFT));
registerAction(new AlignmentAction(parentPart, PositionConstants.RIGHT));
registerAction(new AlignmentAction(parentPart, PositionConstants.TOP));
registerAction(new AlignmentAction(parentPart, PositionConstants.BOTTOM));
registerAction(new AlignmentAction(parentPart, PositionConstants.CENTER));
registerAction(new AlignmentAction(parentPart, PositionConstants.MIDDLE));
registerAction(new MatchWidthAction(parentPart));
registerAction(new MatchHeightAction(parentPart));
IAction showGrid = new ToggleGridAction(diagramContainer.getGraphicalViewer());
diagramContainer.getActionRegistry().registerAction(showGrid);
// Bug 323351: Add button to toggle a flag if the context pad buttons
// shall be shown or not
IAction toggleContextButtonPad = new ToggleContextButtonPadAction(this);
toggleContextButtonPad.setChecked(false);
actionRegistry.registerAction(toggleContextButtonPad);
// End bug 323351
IHandlerService hs = (IHandlerService) parentPart.getSite()
.getService(IHandlerService.class);
hs.activateHandler(FeatureExecutionHandler.COMMAND_ID, new FeatureExecutionHandler(getConfigurationProvider()));
}
/**
* Returns the KeyHandler with common bindings to be used for both the
* Outline and the Graphical Viewer.
*
* @return The KeyHandler with common bindings for both the Outline and the
* Graphical Viewer.
* @since 0.9
*/
protected KeyHandler getCommonKeyHandler() {
if (keyHandler == null) {
keyHandler = new KeyHandler();
keyHandler.put(KeyStroke.getPressed(SWT.DEL, 127, 0),
diagramContainer.getActionRegistry().getAction(ActionFactory.DELETE.getId()));
keyHandler.put(KeyStroke.getPressed(SWT.DEL, 127, SWT.SHIFT), diagramContainer.getActionRegistry()
.getAction(RemoveAction.ACTION_ID));
keyHandler.put(KeyStroke.getPressed(SWT.F2, 0),
diagramContainer.getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
keyHandler.put(KeyStroke.getPressed('c', SWT.CTRL),
diagramContainer.getActionRegistry().getAction(ActionFactory.COPY.getId()));
keyHandler.put(KeyStroke.getPressed('v', SWT.CTRL),
diagramContainer.getActionRegistry().getAction(ActionFactory.PASTE.getId()));
// _keyHandler.put(KeyStroke.getPressed((char) 1, 'a', SWT.CTRL),
// getActionRegistry().getAction(ActionFactory.SELECT_ALL.getId()));
}
return keyHandler;
}
/**
* Gets the diagram scrolling behavior.
*
* @return the diagram scrolling behavior
* @deprecated Scroll bar based infinite canvas is a workaround for GEF
* limitations.
*
* @see DefaultToolBehaviorProvider#getDiagramScrollingBehavior()
*/
@Deprecated
private DiagramScrollingBehavior getDiagramScrollingBehavior() {
if (diagramScrollingBehavior == null) {
IToolBehaviorProvider tbp = getConfigurationProvider().getDiagramTypeProvider()
.getCurrentToolBehaviorProvider();
diagramScrollingBehavior = tbp.getDiagramScrollingBehavior();
}
return diagramScrollingBehavior;
}
private FigureCanvas getFigureCanvas() {
GraphicalViewer viewer = diagramContainer.getGraphicalViewer();
if (viewer != null) {
Control control = viewer.getControl();
if (control instanceof FigureCanvas) {
return (FigureCanvas) control;
}
}
return null;
}
private GFFigureCanvas getGFFigureCanvas() {
GraphicalViewer viewer = diagramContainer.getGraphicalViewer();
if (viewer != null) {
Control control = viewer.getControl();
if (control instanceof GFFigureCanvas) {
return (GFFigureCanvas) control;
}
}
return null;
}
/**
* Hook to unregister the listeners for diagram changes.
*
* @see #registerDiagramResourceSetListener()
*/
protected void unregisterDiagramResourceSetListener() {
if (diagramChangeListener != null) {
diagramChangeListener.stopListening();
TransactionalEditingDomain eDomain = getEditingDomain();
eDomain.removeResourceSetListener(diagramChangeListener);
}
}
/**
* Hook that is called to unregister the listeners for changes of the
* business objects (domain objects).
*
* @see DiagramBehavior#registerBusinessObjectsListener()
*/
protected void unregisterBusinessObjectsListener() {
if (domainModelListener != null) {
TransactionalEditingDomain eDomain = getEditingDomain();
eDomain.removeResourceSetListener(domainModelListener);
}
}
/**
* Hook to register listeners for diagram changes. The listener will be
* notified with all events and has to filter for the ones regarding the
* diagram.<br>
* Note that additional listeners registered here should also be
* unregistered in {@link #unregisterDiagramResourceSetListener()}.
*/
protected void registerDiagramResourceSetListener() {
diagramChangeListener = new DiagramChangeListener(this);
TransactionalEditingDomain eDomain = getEditingDomain();
eDomain.addResourceSetListener(diagramChangeListener);
}
/**
* Hook that is called to register listeners for changes of the business
* objects (domain objects) in the resource set of the editor. The default
* implementation registers the {@link DomainModelChangeListener}.<br>
* Note that additional listeners registered here should also be
* unregistered in {@link #unregisterBusinessObjectsListener()}.
*/
protected void registerBusinessObjectsListener() {
domainModelListener = new DomainModelChangeListener(this);
TransactionalEditingDomain eDomain = getEditingDomain();
eDomain.addResourceSetListener(domainModelListener);
}
/**
* Returns the {@link DiagramEditorInput} instance used in this behavior.
*
* @return An {@link IDiagramEditorInput} instance.
*/
protected IDiagramEditorInput getInput() {
return this.diagramEditorInput;
}
/**
* The part of the dispose that should happen before the GEF dispose.
* Disposes this {@link DiagramBehavior} instance and frees all used
* resources and clears all references. Also delegates to all the behavior
* extensions to also free their resources (e.g. and most important is the
* {@link TransactionalEditingDomain} held by the
* {@link DefaultPersistencyBehavior}. Always delegate to
* <code>super.dispose()</code> in case you override this method!
*/
protected void disposeBeforeGefDispose() {
unregisterDiagramResourceSetListener();
unregisterBusinessObjectsListener();
if (getConfigurationProvider() != null) {
getConfigurationProvider().dispose();
}
if (paletteBehaviour != null) {
paletteBehaviour.dispose();
}
markerBehavior.dispose();
// unregister selection listener, registered during createPartControl()
if (diagramContainer instanceof ISelectionListener) {
if (diagramContainer.getSite() != null && diagramContainer.getSite().getPage() != null) {
diagramContainer.getSite().getPage().removeSelectionListener((ISelectionListener) diagramContainer);
}
}
if (getEditDomain() != null && getEditDomain().getCommandStack() != null) {
getEditDomain().getCommandStack().removeCommandStackEventListener(gefCommandStackListener);
getEditDomain().getCommandStack().dispose();
}
DefaultUpdateBehavior behavior = getUpdateBehavior();
behavior.dispose();
if (contextMenuProvider != null) {
contextMenuProvider.dispose();
contextMenuProvider = null;
}
}
/**
* The part of the dispose that should happen after the GEF dispose. Empties
* the command stack of the edit domain. Always delegate to
* <code>super.dispose()</code> in case you override this method!
*/
protected void disposeAfterGefDispose() {
if (getEditDomain() != null) {
getEditDomain().setCommandStack(null);
}
}
/**
* We provide migration from 0.8.0 to 0.9.0. You can override if you want to
* migrate manually. WARNING: If your diagram is under version control, this
* method can cause a check out dialog to be opened etc.
*
* @since 0.9
*/
protected void migrateDiagramModelIfNecessary() {
final Diagram diagram = getDiagramTypeProvider().getDiagram();
if (Graphiti.getMigrationService().shouldMigrate080To090(diagram)) {
getEditingDomain().getCommandStack().execute(new RecordingCommand(getEditingDomain()) {
@Override
protected void doExecute() {
Graphiti.getMigrationService().migrate080To090(diagram);
}
});
}
}
/**
* Delegation method to retrieve the GEF edit domain also here. Simply
* delegates to the container.
*
* @return The GEF edit domain used used in the container
*/
public DefaultEditDomain getEditDomain() {
return diagramContainer.getEditDomain();
}
/**
* Sets the parent {@link IWorkbenchPart} for this behavior. Can be used to
* embed this behavior in various UIs.
*
* @param parentPart
*/
protected void setParentPart(IWorkbenchPart parentPart) {
this.parentPart = parentPart;
}
/**
* Returns the parent {@link IWorkbenchPart} this behavior is embedded into.
* May be <code>null</code> in case the behavior is embedded in a non part
* UI, like a popup.
*
* @return The parent {@link IWorkbenchPart} or <code>null</code> in case it
* does not exist
*/
protected IWorkbenchPart getParentPart() {
return parentPart;
}
}