/******************************************************************************* * Copyright (c) 2016 itemis AG and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthias Wienand (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.mvc.tests.fx.ui; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import java.util.Map; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.AbstractOperation; import org.eclipse.core.commands.operations.DefaultOperationHistory; import org.eclipse.core.commands.operations.IOperationHistory; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.commands.operations.IUndoableOperation; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.gef.fx.swt.canvas.FXCanvasEx; import org.eclipse.gef.fx.swt.canvas.IFXCanvasFactory; import org.eclipse.gef.mvc.fx.MvcFxModule; import org.eclipse.gef.mvc.fx.operations.ITransactionalOperation; import org.eclipse.gef.mvc.fx.parts.IContentPart; import org.eclipse.gef.mvc.fx.parts.IContentPartFactory; import org.eclipse.gef.mvc.fx.ui.parts.AbstractFXEditor; import org.eclipse.gef.mvc.fx.ui.parts.HistoryBasedDirtyStateProvider; import org.eclipse.gef.mvc.fx.ui.parts.IDirtyStateProvider; import org.eclipse.gef.mvc.fx.ui.parts.IDirtyStateProviderFactory; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.internal.part.NullEditorInput; import org.junit.Test; import com.google.inject.Guice; import com.google.inject.Injector; import javafx.embed.swt.FXCanvas; import javafx.scene.Node; public class AbstractFXEditorTests { private static class ContentIrrelevantOperation extends AbstractOperation implements ITransactionalOperation { public ContentIrrelevantOperation() { super("ContentIrrelevant"); } @Override public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return Status.OK_STATUS; } @Override public boolean isContentRelevant() { return false; } @Override public boolean isNoOp() { return false; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } @Override public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return Status.OK_STATUS; } } private static class ContentRelevantOperation extends AbstractOperation implements ITransactionalOperation { public ContentRelevantOperation() { super("ContentRelevant"); } @Override public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return Status.OK_STATUS; } @Override public boolean isContentRelevant() { return true; } @Override public boolean isNoOp() { return false; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } @Override public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return Status.OK_STATUS; } } private final class FXEditor extends AbstractFXEditor { private FXEditor(Injector injector) { super(injector); } @Override protected void createActions() { } @Override protected void disposeActions() { } @Override public void doSave(IProgressMonitor monitor) { } @Override public void doSaveAs() { } @Override public boolean isSaveAsAllowed() { return false; } @Override protected void setSite(IWorkbenchPartSite site) { } } private static class Module extends MvcFxModule { /** * Binds an {@link IFXCanvasFactory} that creates an {@link FXCanvasEx}. */ protected void bindFXCanvasFactory() { // TODO: change to assisted inject binder().bind(IFXCanvasFactory.class).toInstance(new IFXCanvasFactory() { @Override public FXCanvas createCanvas(Composite parent, int style) { return new FXCanvasEx(parent, style); } }); } protected void bindIContentPartFactory() { binder().bind(IContentPartFactory.class).toInstance(new IContentPartFactory() { @Override public IContentPart<? extends Node> createContentPart(Object content, Map<Object, Object> contextMap) { return null; } }); } /** * Binds a factory for the creation of * {@link HistoryBasedDirtyStateProvider} as * {@link IDirtyStateProvider}. */ protected void bindIDirtyStateProviderFactory() { binder().bind(IDirtyStateProviderFactory.class).toInstance(new IDirtyStateProviderFactory() { @Override public IDirtyStateProvider create(IWorkbenchPart workbenchPart) { return new HistoryBasedDirtyStateProvider( (IOperationHistory) workbenchPart.getAdapter(IOperationHistory.class), (IUndoContext) workbenchPart.getAdapter(IUndoContext.class)); } }); } /** * Binds {@link IOperationHistory} to the operation history of the * Eclipse workbench. */ @Override protected void bindIOperationHistory() { binder().bind(IOperationHistory.class).to(DefaultOperationHistory.class); } @Override protected void configure() { super.configure(); bindIOperationHistory(); bindIContentPartFactory(); bindFXCanvasFactory(); bindIDirtyStateProviderFactory(); } } @Test public void test_dirty_when_content_relevant_operation_is_added() { // create injector (adjust module bindings for test) Injector injector = Guice.createInjector(new Module()); // create editor AbstractFXEditor editor = new FXEditor(injector); try { editor.init(null, new NullEditorInput()); } catch (PartInitException e) { e.printStackTrace(); } assertFalse(editor.isDirty()); // execute content relevant operation ContentRelevantOperation operation = new ContentRelevantOperation(); IUndoContext undoContext = (IUndoContext) editor.getAdapter(IUndoContext.class); operation.addContext(undoContext); try { ((IOperationHistory) editor.getAdapter(IOperationHistory.class)).execute(operation, null, null); } catch (ExecutionException e) { e.printStackTrace(); } assertTrue(editor.isDirty()); } @Test public void test_independent_dirty_state_01() { // create injector (adjust module bindings for test) Injector injector = Guice.createInjector(new Module()); // create first editor AbstractFXEditor editor1 = new FXEditor(injector); try { editor1.init(null, new NullEditorInput()); } catch (PartInitException e) { e.printStackTrace(); } assertFalse(editor1.isDirty()); // create second editor AbstractFXEditor editor2 = new FXEditor(injector); try { editor2.init(null, new NullEditorInput()); } catch (PartInitException e) { e.printStackTrace(); } assertFalse(editor2.isDirty()); IOperationHistory operationHistory1 = (IOperationHistory) editor1.getAdapter(IOperationHistory.class); IOperationHistory operationHistory2 = (IOperationHistory) editor2.getAdapter(IOperationHistory.class); assertNotSame(operationHistory1, operationHistory2); IUndoContext undoContext1 = (IUndoContext) editor1.getAdapter(IUndoContext.class); IUndoContext undoContext2 = (IUndoContext) editor2.getAdapter(IUndoContext.class); assertNotSame(undoContext1, undoContext2); // make first dirty IUndoableOperation operation = new ContentRelevantOperation(); operation.addContext(undoContext1); try { operationHistory1.execute(operation, null, null); } catch (ExecutionException e) { e.printStackTrace(); } assertTrue(editor1.isDirty()); assertFalse(editor2.isDirty()); // make second dirty operation = new ContentRelevantOperation(); operation.addContext(undoContext2); try { operationHistory2.execute(operation, null, null); } catch (ExecutionException e) { e.printStackTrace(); } assertTrue(editor1.isDirty()); assertTrue(editor2.isDirty()); } @Test public void test_independent_dirty_state_02() { // create injector (adjust module bindings for test) Injector injector = Guice.createInjector(new Module()); // create first editor AbstractFXEditor editor1 = new FXEditor(injector); try { editor1.init(null, new NullEditorInput()); } catch (PartInitException e) { e.printStackTrace(); } assertFalse(editor1.isDirty()); // create second editor AbstractFXEditor editor2 = new FXEditor(injector); try { editor2.init(null, new NullEditorInput()); } catch (PartInitException e) { e.printStackTrace(); } assertFalse(editor2.isDirty()); // make second dirty IUndoableOperation operation = new ContentRelevantOperation(); operation.addContext((IUndoContext) editor2.getAdapter(IUndoContext.class)); try { ((IOperationHistory) editor2.getAdapter(IOperationHistory.class)).execute(operation, null, null); } catch (ExecutionException e) { e.printStackTrace(); } assertFalse(editor1.isDirty()); assertTrue(editor2.isDirty()); // make first dirty operation = new ContentRelevantOperation(); operation.addContext((IUndoContext) editor1.getAdapter(IUndoContext.class)); try { ((IOperationHistory) editor1.getAdapter(IOperationHistory.class)).execute(operation, null, null); } catch (ExecutionException e) { e.printStackTrace(); } assertTrue(editor1.isDirty()); assertTrue(editor2.isDirty()); } @Test public void test_not_dirty_when_content_irrelevant_operation_is_added() { // create injector (adjust module bindings for test) Injector injector = Guice.createInjector(new Module()); // create editor AbstractFXEditor editor = new FXEditor(injector); try { editor.init(null, new NullEditorInput()); } catch (PartInitException e) { e.printStackTrace(); } assertFalse(editor.isDirty()); // execute content relevant operation IUndoableOperation operation = new ContentIrrelevantOperation(); operation.addContext((IUndoContext) editor.getAdapter(IUndoContext.class)); try { ((IOperationHistory) editor.getAdapter(IOperationHistory.class)).execute(operation, null, null); } catch (ExecutionException e) { e.printStackTrace(); } assertFalse(editor.isDirty()); } }