Java tutorial
/******************************************************************************* * <copyright> * * Copyright (c) 2016 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 * mwenz - Bug 439689 - DiagramEdtior.setPictogramElementForSelection adds SelectionBorders to invisible PictogramElements * mwenz - Bug 407894 - Luna: After DiagramsInViews change graphical viewer is configured and initialized only by a workaround * mwenz - Bug 433779 - DiagramBehaviour.setInput() is not extensible * mwenz - Bug 470038 - NullPointerException in DiagramBehavior.unregisterDiagramResourceSetListener * mwenz - Bug 470150 - NullPointerException in DiagramBehavior.getAdapter * mwenz - Bug 477526 - NullPointerException in DiagramBehavior.addGefListeners * mwenz - Bug 480961 - NullPointerException below ScrollingGraphicalViewer.reveal * edeley - Bug 498164 - Providing a JFace ResourceManager in DiagramBehavior to manage SWT resources * mwenz - Bug 500851 - Probable NullPointerException in DiagramBehavior.disposeAfterGefDispose * </copyright> * *******************************************************************************/ package org.eclipse.graphiti.ui.editor; 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.services.Graphiti; import org.eclipse.graphiti.tb.DefaultToolBehaviorProvider; import org.eclipse.graphiti.tb.IToolBehaviorProvider; 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.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; 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 IConfigurationProvider 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; private ResourceManager resourceManager = null; public DiagramBehavior(IDiagramContainerUI diagramContainer) { super(); this.setDiagramContainer(diagramContainer); } /** * Setter for the associated {@link IDiagramContainerUI container} * displaying the diagram of this behavior. Note that once the container has * been set it must not be changed any more. * * @param diagramContainer * the diagramContainer to set * @since 0.12 */ protected void setDiagramContainer(IDiagramContainerUI diagramContainer) { if (this.diagramContainer != null) { throw new IllegalStateException("diagramContainer must not be changed once it has been set"); } 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$ } setDiagramEditorInput((IDiagramEditorInput) input); // Retrieve the diagram Diagram diagram = getPersistencyBehavior().loadDiagram(getInput().getUri()); if (diagram == null) { // Can happen if editor is started with invalid URI setEditorInitializationError("No Diagram found for URI '" + getInput().getUri().toString()); //$NON-NLS-1$ return; } IDiagramTypeProvider diagramTypeProvider = initDiagramTypeProvider(diagram); initConfigurationProvider(diagramTypeProvider); getRefreshBehavior().handleAutoUpdateAtStartup(); getDiagramContainer().refreshTitle(); registerBusinessObjectsListener(); registerDiagramResourceSetListener(); } /** * Creates and initializes a new {@link IDiagramTypeProvider} for the given * {@link Diagram} and diagram type provider ID. * * @param diagram * The diagram * @param providerId * The diagram type provider ID * * @since 0.12 */ protected IDiagramTypeProvider initDiagramTypeProvider(Diagram diagram) { String providerId = getDiagramTypeProviderId(diagram); IDiagramTypeProvider diagramTypeProvider = GraphitiUi.getExtensionManager() .createDiagramTypeProvider(providerId); if (diagramTypeProvider == null) { setEditorInitializationError("Could not find diagram type provider for " + diagram.getDiagramTypeId()); //$NON-NLS-1$ return null; } diagramTypeProvider.init(diagram, this); return diagramTypeProvider; } /** * Gets the diagram type provider ID for the diagram. The default * implementation checks if the input defines one and will use that that, * otherwise it will retrieve the ID of the first diagram type provider that * is registered for the type of the passed diagram. * * In case no ID is found this method will set the editor initialization * error and throw an {@link AssertionFailedException}. * * @param diagram * The diagram to find the ID for. * @since 0.12 */ protected String getDiagramTypeProviderId(Diagram diagram) { String providerId = getInput().getProviderId(); // If provider is null then take the first installed provider for this // diagram type if (providerId == null) { providerId = GraphitiUi.getExtensionManager().getDiagramTypeProviderId(diagram.getDiagramTypeId()); getInput().setProviderId(providerId); } if (providerId == null) { String message = "DiagramEditorInput does not convey a Provider ID '" + getInput() //$NON-NLS-1$ + ". See the error log for details."; //$NON-NLS-1$ setEditorInitializationError(message); Assert.isNotNull(providerId, message); } return providerId; } /** * 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) { IDiagramContainerUI diagramContainer = getDiagramContainer(); if (diagramContainer != 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); } IDiagramContainerUI diagramContainer = getDiagramContainer(); diagramContainer.setGraphicalViewer(viewer); diagramContainer.configureGraphicalViewer(); diagramContainer.hookGraphicalViewer(); diagramContainer.initializeGraphicalViewer(); } /** * 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) getDiagramContainer().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 = getDiagramContainer().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(getDiagramContainer().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(getDiagramContainer().getGraphicalViewer())); return result; } /** * Returns the error text of the error that occurred while initializing this * behavior and its components. In case this method reports the error text * and error UI may be shown instead of the normal diagram viewer. * * @return The error text in case an error has occurred, <code>null</code> * otherwise */ protected String getEditorInitializationError() { return editorInitializationError; } /** * Sets the error text for an error that occured during the initialization * of this {@link DiagramEditor}. Setting a <code>non-null</code> value * indicates an error has occurred, setting <code>null</code> indicates * everything went fine. * * @param editorInitializationError * The error message * @since 0.12 */ protected void setEditorInitializationError(String editorInitializationError) { this.editorInitializationError = 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(getEditorInitializationError()); 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 (getEditorInitializationError() != 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 = getDiagramContainer().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 getDiagramContainer().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 = getDiagramContainer().getGraphicalViewer().getEditPartRegistry(); if (editPartRegistry != null) { for (int i = 0; i < pictogramElements.length; i++) { PictogramElement pe = pictogramElements[i]; /* * Bug 439689 - Check for visible state of PictogramElement as * it might have changed and the edit part state might not have * been updated yet. */ if (pe != null && pe.isVisible()) { 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() { GraphicalViewer graphicalViewer = getDiagramContainer().getGraphicalViewer(); // Bug 480961 - Prevent NullPointerException in // async call (might be no longer available when // the async call is executed). if (graphicalViewer.getControl() != null) { graphicalViewer.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 = getDiagramContainer().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 = getDiagramContainer().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. * * @since 0.12 */ public 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. * * @since 0.12 */ public 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) { IDiagramTypeProvider diagramTypeProvider = cfgProvider.getDiagramTypeProvider(); if (diagramTypeProvider != null) { IToolBehaviorProvider toolBehaviorProvider = diagramTypeProvider.getCurrentToolBehaviorProvider(); if (toolBehaviorProvider != null) { Object ret = toolBehaviorProvider.getAdapter(type); if (ret != null) { return ret; } } } } IDiagramContainerUI diagramContainer = getDiagramContainer(); 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(); } return null; } /** * Returns the {@link ConfigurationProvider} for this behavior. It is mainly * a wrapper around various objects that are connected to the diagram * behavior. * * @return an {@link IConfigurationProvider} instance. * @since 0.12 */ public 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 (getDiagramContainer().getGraphicalViewer() != null) { return getDiagramContainer().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 = getDiagramContainer().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) getDiagramContainer().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; } /** * Initializes the given {@link IConfigurationProvider} for this * {@link DiagramBehavior} instance and sets it. * * @param configurationProvider * The configuration provider * * @since 0.12 */ protected void initConfigurationProvider(IDiagramTypeProvider diagramTypeProvider) { configurationProvider = createConfigurationProvider(diagramTypeProvider); // initialize configuration-provider depending on this editor configurationProvider.setWorkbenchPart(parentPart); if (getDiagramContainer().getGraphicalViewer() != null) { initializeGraphicalViewer(); } if (getDiagramContainer() instanceof IEditorPart) { DefaultEditDomain editDomain = new DefaultEditDomain((IEditorPart) getDiagramContainer()); getDiagramContainer().setEditDomain(editDomain); } CommandStack commandStack = new GFCommandStack(configurationProvider, getEditingDomain()); getEditDomain().setCommandStack(commandStack); } /** * Creates a new {@link ConfigurationProvider} for this * {@link DiagramBehavior} and the given {@link IDiagramTypeProvider}. The * default implementation will create a default * {@link ConfigurationProvider} which should suite for all Graphiti * clients. * * @since 0.12 */ protected IConfigurationProvider createConfigurationProvider(IDiagramTypeProvider diagramTypeProvider) { return new ConfigurationProvider(this, diagramTypeProvider); } 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(getDiagramContainer().getGraphicalViewer(), getDiagramContainer().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; } IDiagramContainerUI diagramContainer = getDiagramContainer(); 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; } IDiagramContainerUI diagramContainer = getDiagramContainer(); 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(); IDiagramContainerUI diagramContainer = getDiagramContainer(); 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 = getDiagramContainer().getGraphicalViewer(); if (viewer != null) { Control control = viewer.getControl(); if (control instanceof FigureCanvas) { return (FigureCanvas) control; } } return null; } private GFFigureCanvas getGFFigureCanvas() { GraphicalViewer viewer = getDiagramContainer().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 editingDomain = getEditingDomain(); if (editingDomain != null) { editingDomain.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; } /** * Setter for the field storing the editor input. * * @param diagramEditorInput * The new input * @since 0.12 */ protected void setDiagramEditorInput(IDiagramEditorInput diagramEditorInput) { this.diagramEditorInput = 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() IDiagramContainerUI diagramContainer = getDiagramContainer(); 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); } if (resourceManager != null) { resourceManager.dispose(); } } /** * 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 getDiagramContainer().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; } /** * Creates a new instance of a {@link ResourceManager} implementation. * <p> * By default this creates a {@link LocalResourceManager} instance. * Subclasses may override this method to enforce the usage of another * implementation. * </p> * @return A new instance of a {@link ResourceManager}. * * @since 0.14 */ protected ResourceManager createResourceManager() { return new LocalResourceManager(JFaceResources.getResources()); } /** * * @return the JFace {@link ResourceManager} to be used to create images, * fonts etc for an open diagram. * * @since 0.14 */ public ResourceManager getResourceManager() { synchronized (this) { // don't initialize in the constructor as that seems to break mock // DiagramBehavior instances in e.g. CustomUndoableFeatureTest. if (resourceManager == null) { resourceManager = createResourceManager(); } } return resourceManager; } }