Skip to content

Creating a GEF Editor – Part 12: Enable Save Action on the Editor

Last updated on 2014-09-07

Previous Tutorial: Creating a GEF Editor – Part 11: Creating Link Bendpoints

So, after managing to break my laptop’s screen last week I’ve been pretty down… but as the saying goes, the show must go on. Here goes a small tutorial that fixes something that is probably bothering you (It bothered me a lot): saving the model file. While eclipse does detect that the file has changed before closing the editor, having the possibility to check that the file can be saved (which also validates that the EMF) is very useful. And the solution is also pretty simple.

The GEF graphical editor already registers the SaveAction when created. What is left to do is somehow tell it to enable and disable itself. Since all changes to the model (and therefore the file) are done using commands, the file must be saved if there was a change in the command stack since the last time the file was saved. Thankfully most of the work was already done for us and we need to do two small changes.

  1. First, the GEF editor is informed whenever there is a change in the CommandStack calling its commandStackChanged method (this occurs because the GraphicalEditor is adds itself as a listener to the CommandStack when it is initialized). When this happens, the isDirty method of the GraphicalEditor will return true when called… but the problem is that the SaveAction does not recalculate if it is enabled automatically, so we must tell it by firing a property change, in this case using the PROP_DIRTY property of the GraphicalEditor (this is of course done in the commandStackChanged).
  2. Secondly, after the file is saved we must also save the location in the CommandStack.

So after all the talking, here is updated code of the OPMGraphicalEditor (the relevant changes are in lines 61 and 93-100):

package com.vainolo.phd.opm.gef.editor;

import java.io.IOException;
import java.util.EventObject;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;

import com.vainolo.phd.opm.gef.editor.part.OPMEditPartFactory;
import com.vainolo.phd.opm.model.OPMPackage;
import com.vainolo.phd.opm.model.OPMObjectProcessDiagram;

public class OPMGraphicalEditor extends GraphicalEditorWithFlyoutPalette {

	private Resource opdResource;
	private OPMObjectProcessDiagram opd;
	
	public OPMGraphicalEditor() {
		setEditDomain(new DefaultEditDomain(this));
	}
	
	@Override protected void initializeGraphicalViewer() {
		super.initializeGraphicalViewer();
		getGraphicalViewer().setContents(opd);
	}	
	
	@Override protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();
		getGraphicalViewer().setEditPartFactory(new OPMEditPartFactory());
	}	
	
	@Override protected PaletteRoot getPaletteRoot() {
		return new OPMGraphicalEditorPalette();
	}

	/**
	 * Save the model using the resource from which it was
	 * opened, and mark the current location in the 
	 * {@link CommandStack}.
	 */
	@Override public void doSave(IProgressMonitor monitor) {
		if(opdResource == null) {
			return;
		}
		
		try {
			opdResource.save(null);
            getCommandStack().markSaveLocation();			
		} catch(IOException e) {
			// TODO do something smarter.
			e.printStackTrace();
			opdResource = null;
		}
	}

	@Override public void init(IEditorSite site, IEditorInput input) throws PartInitException {
		super.init(site, input);
		
		loadInput(input);
	}
	
	private void loadInput(IEditorInput input) {
		OPMPackage.eINSTANCE.eClass(); // This initializes the OPMPackage singleton implementation.
		ResourceSet resourceSet = new ResourceSetImpl();
		if(input instanceof IFileEditorInput) {
			IFileEditorInput fileInput = (IFileEditorInput) input;
			IFile file = fileInput.getFile();
			opdResource = resourceSet.createResource(URI.createURI(file.getLocationURI().toString()));
			try {
				opdResource.load(null);
				opd = (OPMObjectProcessDiagram) opdResource.getContents().get(0);
			} catch(IOException e) {
				// TODO do something smarter.
				e.printStackTrace();
				opdResource = null;
			}
		}
	}
	
	/**
	 * Fire a {@link IEditorPart#PROP_DIRTY} property change and
	 * call super implementation.
	 */
	@Override public void commandStackChanged(EventObject event) {
        firePropertyChange(PROP_DIRTY);
        super.commandStackChanged(event);
    }	
}

Fire up your editor, edit something and hit Save. Works great, right?

You can find the final project files here.

And a last reminder, if you like my tutorials please support my work by donating for the good cause of fixing my laptop (upper right corner of your screen).

Next Tutorial: Creating an OPM GEF Editor – Part 13: Adding Procedural Links

Published inProgramming

3 Comments

  1. PaulB PaulB

    For the part “something smarter” I have the following:
    final Shell shell = getSite().getWorkbenchWindow().getShell();

    final MessageBox messageBox = new MessageBox(shell, SWT.ICON_ERROR);
    messageBox.setMessage("Could not save due to: " + io.getMessage());
    messageBox.open();

Leave a Reply to PaulBCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Musings of a Strange Loop

Subscribe now to keep reading and get access to the full archive.

Continue reading