/******************************************************************************* * MontiCore Language Workbench * Copyright (c) 2015, 2016, MontiCore, All rights reserved. * * This project is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this project. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package de.monticore.genericgraphics; import java.util.EventObject; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.gef.DefaultEditDomain; import org.eclipse.gef.RootEditPart; import org.eclipse.gef.ui.parts.GraphicalEditor; import org.eclipse.gef.ui.parts.SelectionSynchronizer; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.part.FileEditorInput; import de.monticore.editorconnector.util.ExtensionRegistryUtils; import de.monticore.genericgraphics.controller.editparts.IMCEditPart; import de.monticore.genericgraphics.controller.persistence.ErrorCollector; import de.monticore.genericgraphics.controller.persistence.IGraphicsLoader; import de.monticore.genericgraphics.controller.util.ASTNodeProblemReportHandler; import de.monticore.genericgraphics.view.layout.ILayoutAlgorithm; import de.se_rwth.langeditor.texteditor.TextEditorImpl; /** * <p> * The Generic Graphical Editor for MontiCore Class Diagrams. * </p> * <p> * The Generic Graphical Editor makes use of the following utils: * <ul> * <li>{@link IGraphicsLoader}: for loading and saving the model and view data</li> * <li>{@link SelectionSynchronizer}: for synchronizing selection between multiple editors</li> * <li>{@link ILayoutAlgorithm}: for layouting nodes of the diagram.</li> * <li>A ResourceTracker: for reacting on model file changes due to other editors * <ul> * <li>File contents changed and was saved => refresh content of this editor</li> * <li>File was renamed => reload the file. Note: the view file is not moved automatically.</li> * <li>File was deleted => close editor.</li> * </ul> * </li> * </ul> * </p> * <p> * Different functionality is provided by the Generic Graphical Editor without changing anything, * and just implementing the abstract methods: * <ul> * <li>Loading of model data/view data and combination of both</li> * <li>Redo / Undo functionality for nearly all actions</li> * <li>Observing the underlying model file for changes and updating correctly</li> * <li>Providing a Overview View for the Editor</li> * <li>Showing Problems during parsing in Problems view</li> * <li>Show Problems in Figures</li> * <li>Providing printing functionality</li> * <li>Providing zoom functionality</li> * <li>Providing export to jpg, ico, bmp, gif, png -images</li> * </ul> * </p> * <p> * There are several methods that allow specific configuration, without the need to change any of * the existing methods.<br> * <br> * The following methods need to be implemented to provide functionality needed by the editor: * <ul> * <li>{@link #createEditPartFactory()}</li> * <li>{@link #createPersistenceUtil()}</li> * <li>{@link #createDSLTool(String[])}</li> * <li>{@link #createLayoutAlgorithm()}</li> * <li>{@link #getContents()}</li> * </ul> * Furthermore, the following methods allow to customize the process of loading model data * <ul> * <li>{@link #beforeModelLoad()}</li> * <li>{@link #afterModelLoadBeforeViewLoad()}</li> * <li>{@link #afterViewLoad()}</li> * </ul> * These methods are called as follows: * <ul> * <li>{@link #beforeModelLoad()}</li> * <li>loading of model data</li> * <li>{@link #afterModelLoadBeforeViewLoad()}</li> * <li>loading of view information</li> * <li>{@link #afterViewLoad()}</li> * <li>contents of GraphicalViewer is set with {@link #getContents()}</li> * <li>combination of model and view information with {@link #createLayoutAlgorithm()}</li> * </ul> * </p> * <p> * There are flags to set, for changing default behavior: * <ul> * <li>{link {@link #isAlwaysRefresh()}, {@link #setAlwaysRefresh(boolean)}: If <tt>true</tt> the * editor will reload the model after the model file changed. This means, e.g., that the model file * is reloaded when a single space is inserted. If <tt>false</tt> the editor is reloaded when the * model file changed and was saved.</li> * </ul> * </p> * <b>Note</b>: when overriding (non-abstract) methods (e.g. <code>foo()</code> ), you should always * call <code>super.foo()</code> first in your method.<br> * * @author Tim Enger */ public class GenericGraphicsEditor extends GraphicalEditor { private GenericGraphicsViewer viewer; private IFile inputFile; private Composite parentControl; private TextEditorImpl editor; /** * Constructor */ public GenericGraphicsEditor(TextEditorImpl editor) { this.editor = editor; setEditDomain(new DefaultEditDomain(this)); } /** * Override to assign, configure and initialize the Viewer by ourselves. Mostly equivalent to * GraphicalEditor.createPartControl(Composite). <br> * <br> * The model to be displayed is retrieved from the corresponding textual editor. If this editor is * opened already, there is no problem. However, if it isn't, the viewer will only be created and * configured and a part listener is added to the workbench to recognize when the textual editor * is opened. When the textual editor gets opened, the graphical viewer's contents will be * initialized by calling {@code createPartControl(Composite)} again. <br> * <b>It is necessary</b> to set the viewer as the selection provider the first time * {@code createPartControl(Composite)} is called, otherwise selection changes in the graphical * viewer will not be recognized by selection listeners that are added to a workbench window's * {@link ISelectionService}. */ @Override public void createPartControl(Composite parent) { // only create viewer on first call if (parentControl == null) { try { viewer = ExtensionRegistryUtils.getViewer(inputFile); if (viewer != null) { viewer.init(getSite(), inputFile, editor); viewer.createControl(parent); viewer.configure(); setGraphicalViewer(viewer); // hookGraphicalViewer() getSelectionSynchronizer().addViewer(getGraphicalViewer()); getSite().setSelectionProvider(getGraphicalViewer()); } } catch (CoreException e1) { e1.printStackTrace(); } } parentControl = parent; } @Override public void initializeGraphicalViewer() { viewer.refreshContents(); } @Override protected void setInput(IEditorInput input) { super.setInput(input); if (input instanceof IFileEditorInput) { inputFile = ((IFileEditorInput) input).getFile(); } if (input != null && input instanceof IFileEditorInput) { IFile file = ((IFileEditorInput) input).getFile(); if (viewer != null) viewer.setInput(file); setPartName(file.getName() + " (Graphical View)"); } } public void setInput(IFile input) { setInput(new FileEditorInput(input)); } @Override public void doSave(IProgressMonitor monitor) { viewer.doSave(monitor); getCommandStack().markSaveLocation(); } // Mark the editor dirty when command stack changes // (layout is changed) @Override public void commandStackChanged(EventObject event) { firePropertyChange(IEditorPart.PROP_DIRTY); super.commandStackChanged(event); } /** * <p> * Show problem reports in: * <ul> * <li>Eclipse Problem View</li> * <li>Graphic Representation</li> * </ul> * </p> * <p> * This method is called during the initialization/refresh of the editor and uses the methods of * the {@link ASTNodeProblemReportHandler} util. * </p> * <p> * <b>Note</b>: This method is intended to be overwritten by subclasses if they want to change the * problem report handling. * </p> * * @param ec The {@link ErrorCollector} used as input for the {@link ProblemReport ProblemReports} */ @SuppressWarnings("unchecked") public void showProblemReports() { ASTNodeProblemReportHandler .showProblemReportsInGraphics(getGraphicalViewer().getEditPartRegistry().values()); // TODO check for exception always occurring at the start of the editor // when this is active // ASTNodeProblemReportHandler.showProblemReportsInProblemsView(getEditorInputFile(), // ec); } /*******************************************/ /************ GETTER & SETTERS *************/ /*******************************************/ /** * @return The editor input as {@link IFile}. */ public IFile getEditorInputFile() { if (getEditorInput() != null) { return ((IFileEditorInput) getEditorInput()).getFile(); } return null; } /** * Every GEF editor has a {@link RootEditPart}. This {@link RootEditPart} has a single child, * called <i>contents</i> editpart representing the model data of the editor. * * @return The contents {@link IMCEditPart}. */ public IMCEditPart getContentEditPart() { return (IMCEditPart) getGraphicalViewer().getRootEditPart().getContents(); } @Override public GenericGraphicsViewer getGraphicalViewer() { return viewer; } }