Skip to content
Snippets Groups Projects
Commit 6ee2cd97 authored by Tobias Ask's avatar Tobias Ask
Browse files

Synchronize external changes

Add a resource change listener that tracks changes made to the editor's document from outside of Eclipse itself, so that the editor is in sync with the underlying file at all times. Note that this works best when the "Refresh using native hooks or pulling" option is enabled; otherwise, a refresh of the workspace is required to detect the changes.
parent 93b336b9
No related branches found
No related tags found
No related merge requests found
Pipeline #45771 failed
...@@ -34,6 +34,10 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList ...@@ -34,6 +34,10 @@ public class JavaProjectGlossary extends Glossary implements IElementChangedList
this.fxmlLocation = fxmlLocation; this.fxmlLocation = fxmlLocation;
} }
public void setFxmlLocation(URL fxmlLocation) {
this.fxmlLocation = fxmlLocation;
}
@Override @Override
public List<String> queryControllerClasses(URL fxmlLocation) { public List<String> queryControllerClasses(URL fxmlLocation) {
if (candidateControllers == null) { if (candidateControllers == null) {
......
package no.tobask.sb4e.editors;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
public class EditorInputWatcher implements IResourceChangeListener {
private IFile input;
private IInputChangeListener listener;
public EditorInputWatcher(IFile input, IInputChangeListener listener) {
this.input = input;
this.listener = listener;
}
public void setInput(IFile input) {
this.input = input;
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
IResourceDelta delta = event.getDelta();
if (delta == null) {
return;
}
delta = delta.findMember(input.getFullPath());
if (delta != null) {
int flags = delta.getFlags();
switch (delta.getKind()) {
case IResourceDelta.CHANGED:
if ((IResourceDelta.CONTENT & flags) != 0) {
listener.inputContentChanged();
}
break;
case IResourceDelta.REMOVED:
if ((IResourceDelta.MOVED_TO & flags) != 0) {
listener.inputMoved(delta.getMovedToPath());
} else {
listener.inputRemoved();
}
break;
default:
break;
}
}
}
}
...@@ -5,7 +5,6 @@ import java.io.IOException; ...@@ -5,7 +5,6 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import org.eclipse.core.commands.operations.IOperationHistory; import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.ObjectUndoContext; import org.eclipse.core.commands.operations.ObjectUndoContext;
...@@ -13,18 +12,24 @@ import org.eclipse.core.commands.operations.OperationHistoryFactory; ...@@ -13,18 +12,24 @@ import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField; import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT; import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IEditorReference;
...@@ -35,7 +40,6 @@ import org.eclipse.ui.operations.RedoActionHandler; ...@@ -35,7 +40,6 @@ import org.eclipse.ui.operations.RedoActionHandler;
import org.eclipse.ui.operations.UndoActionHandler; import org.eclipse.ui.operations.UndoActionHandler;
import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.part.FileEditorInput;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController; import com.oracle.javafx.scenebuilder.kit.editor.EditorController;
import com.oracle.javafx.scenebuilder.kit.editor.JobManager; import com.oracle.javafx.scenebuilder.kit.editor.JobManager;
import com.oracle.javafx.scenebuilder.kit.editor.EditorController.ControlAction; import com.oracle.javafx.scenebuilder.kit.editor.EditorController.ControlAction;
...@@ -44,6 +48,7 @@ import com.oracle.javafx.scenebuilder.kit.editor.job.Job; ...@@ -44,6 +48,7 @@ import com.oracle.javafx.scenebuilder.kit.editor.job.Job;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument; import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.embed.swt.FXCanvas; import javafx.embed.swt.FXCanvas;
import no.tobask.sb4e.CustomClassLoaderLibrary; import no.tobask.sb4e.CustomClassLoaderLibrary;
...@@ -54,7 +59,7 @@ import no.tobask.sb4e.handlers.SceneBuilderControlActionHandler; ...@@ -54,7 +59,7 @@ import no.tobask.sb4e.handlers.SceneBuilderControlActionHandler;
import no.tobask.sb4e.handlers.SceneBuilderEditActionHandler; import no.tobask.sb4e.handlers.SceneBuilderEditActionHandler;
import no.tobask.sb4e.handlers.SceneBuilderOperation; import no.tobask.sb4e.handlers.SceneBuilderOperation;
public class FXMLEditor extends EditorPart { public class FXMLEditor extends EditorPart implements IInputChangeListener {
private EditorController editorController; private EditorController editorController;
private FXCanvas canvas; private FXCanvas canvas;
...@@ -71,6 +76,8 @@ public class FXMLEditor extends EditorPart { ...@@ -71,6 +76,8 @@ public class FXMLEditor extends EditorPart {
private IAction deleteHandler; private IAction deleteHandler;
private UndoActionHandler undoActionHandler; private UndoActionHandler undoActionHandler;
private RedoActionHandler redoActionHandler; private RedoActionHandler redoActionHandler;
private String fxmlText = null;
private EditorInputWatcher inputWatcher;
private ChangeListener<Number> editorSelectionListener = (oV, oldNum, newNum) -> { private ChangeListener<Number> editorSelectionListener = (oV, oldNum, newNum) -> {
FXOMObject fxomRoot = editorController.getFxomDocument().getFxomRoot(); FXOMObject fxomRoot = editorController.getFxomDocument().getFxomRoot();
...@@ -96,8 +103,7 @@ public class FXMLEditor extends EditorPart { ...@@ -96,8 +103,7 @@ public class FXMLEditor extends EditorPart {
if (fxId != null) { if (fxId != null) {
int[] location = getLocation(fxId, controllerClass); int[] location = getLocation(fxId, controllerClass);
if (location != null) { if (location != null) {
IFile file = ((FileEditorInput) editor.getEditorInput()) IFile file = ((FileEditorInput) editor.getEditorInput()).getFile();
.getFile();
IDE.gotoMarker(editor, createMarker(file, location)); IDE.gotoMarker(editor, createMarker(file, location));
} }
} }
...@@ -127,6 +133,8 @@ public class FXMLEditor extends EditorPart { ...@@ -127,6 +133,8 @@ public class FXMLEditor extends EditorPart {
undoContext = new ObjectUndoContext(this); undoContext = new ObjectUndoContext(this);
undoActionHandler = new UndoActionHandler(site, undoContext); undoActionHandler = new UndoActionHandler(site, undoContext);
redoActionHandler = new RedoActionHandler(site, undoContext); redoActionHandler = new RedoActionHandler(site, undoContext);
inputWatcher = new EditorInputWatcher(((FileEditorInput) input).getFile(), this);
ResourcesPlugin.getWorkspace().addResourceChangeListener(inputWatcher);
} }
@Override @Override
...@@ -136,12 +144,13 @@ public class FXMLEditor extends EditorPart { ...@@ -136,12 +144,13 @@ public class FXMLEditor extends EditorPart {
canvas = new FXCanvas(parent, SWT.None); canvas = new FXCanvas(parent, SWT.None);
editorController = new EditorController(); editorController = new EditorController();
EditorWindowController editorWindowController = new EditorWindowController(editorController); EditorWindowController editorWindowController = new EditorWindowController(editorController);
// make sure the other controllers have their references set before setting the input file // make sure the other controllers have their references set before setting the
// input file
// for the editor controller so they can react to the update // for the editor controller so they can react to the update
try { try {
editorController.setLibrary(new CustomClassLoaderLibrary(new EclipseProjectsClassLoader())); editorController.setLibrary(new CustomClassLoaderLibrary(new EclipseProjectsClassLoader()));
editorController.setFxmlTextAndLocation(FXOMDocument fxmlText = FXOMDocument.readContentFromURL(fxmlUrl);
.readContentFromURL(fxmlUrl), fxmlUrl); editorController.setFxmlTextAndLocation(fxmlText, fxmlUrl);
FXOMObject root = editorController.getFxomDocument().getFxomRoot(); FXOMObject root = editorController.getFxomDocument().getFxomRoot();
String controllerName = null; String controllerName = null;
...@@ -179,19 +188,16 @@ public class FXMLEditor extends EditorPart { ...@@ -179,19 +188,16 @@ public class FXMLEditor extends EditorPart {
@Override @Override
public void doSave(IProgressMonitor monitor) { public void doSave(IProgressMonitor monitor) {
IFile file = ((FileEditorInput) getEditorInput()).getFile(); IFile file = ((FileEditorInput) getEditorInput()).getFile();
byte[] fxmlBytes = null;
try {
fxmlBytes = editorController.getFxmlText().getBytes("UTF-8");
} catch (UnsupportedEncodingException e2) {
e2.printStackTrace();
}
try { try {
String newFxmlText = editorController.getFxmlText();
byte[] fxmlBytes = newFxmlText.getBytes("UTF-8");
updateDirtyStatus(false);
file.setContents(new ByteArrayInputStream(fxmlBytes), IResource.FORCE, monitor); file.setContents(new ByteArrayInputStream(fxmlBytes), IResource.FORCE, monitor);
} catch (CoreException e1) { this.fxmlText = newFxmlText;
e1.printStackTrace(); } catch (UnsupportedEncodingException | CoreException e) {
e.printStackTrace();
updateDirtyStatus(true);
} }
dirty = false;
firePropertyChange(PROP_DIRTY);
} }
@Override @Override
...@@ -215,6 +221,9 @@ public class FXMLEditor extends EditorPart { ...@@ -215,6 +221,9 @@ public class FXMLEditor extends EditorPart {
if (glossary != null) { if (glossary != null) {
JavaCore.removeElementChangedListener(glossary); JavaCore.removeElementChangedListener(glossary);
} }
if (inputWatcher != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(inputWatcher);
}
} }
public IAction getUndoActionHandler() { public IAction getUndoActionHandler() {
...@@ -248,8 +257,9 @@ public class FXMLEditor extends EditorPart { ...@@ -248,8 +257,9 @@ public class FXMLEditor extends EditorPart {
private void setupUndoRedo() { private void setupUndoRedo() {
JobManager jobManager = editorController.getJobManager(); JobManager jobManager = editorController.getJobManager();
jobManager.revisionProperty().addListener((observable, oldValue, newValue) -> { jobManager.revisionProperty().addListener((observable, oldValue, newValue) -> {
dirty = jobManager.canUndo(); String newFxmlText = editorController.getFxmlText();
firePropertyChange(PROP_DIRTY); updateDirtyStatus(!newFxmlText.equals(this.fxmlText));
if (!jobManager.canRedo()) { if (!jobManager.canRedo()) {
// only add unique, previously unseen jobs to the operation history // only add unique, previously unseen jobs to the operation history
Job currentJob = jobManager.getCurrentJob(); Job currentJob = jobManager.getCurrentJob();
...@@ -273,8 +283,8 @@ public class FXMLEditor extends EditorPart { ...@@ -273,8 +283,8 @@ public class FXMLEditor extends EditorPart {
IEditorPart editor = editorRef.getEditor(true); IEditorPart editor = editorRef.getEditor(true);
if (editor != null && editor.getEditorInput() instanceof FileEditorInput) { if (editor != null && editor.getEditorInput() instanceof FileEditorInput) {
IFile file = ((FileEditorInput) editor.getEditorInput()).getFile(); IFile file = ((FileEditorInput) editor.getEditorInput()).getFile();
if (file.getName().equals(controllerClass.getElementName()) && if (file.getName().equals(controllerClass.getElementName())
getSite().getPage().isPartVisible(editor)) { && getSite().getPage().isPartVisible(editor)) {
return editor; return editor;
} }
} }
...@@ -299,9 +309,70 @@ public class FXMLEditor extends EditorPart { ...@@ -299,9 +309,70 @@ public class FXMLEditor extends EditorPart {
} }
private boolean isFreshJob(Job job) { private boolean isFreshJob(Job job) {
SceneBuilderOperation topOperation = (SceneBuilderOperation) operationHistory SceneBuilderOperation topOperation = (SceneBuilderOperation) operationHistory.getRedoOperation(undoContext);
.getRedoOperation(undoContext);
return topOperation == null || topOperation.getJob() != job; return topOperation == null || topOperation.getJob() != job;
} }
private void loadFxml(URL location) {
Platform.runLater(() -> {
try {
fxmlText = FXOMDocument.readContentFromURL(location);
editorController.setFxmlTextAndLocation(fxmlText, fxmlUrl);
glossary.setFxmlLocation(fxmlUrl);
updateDirtyStatus(false);
} catch (IOException e) {
e.printStackTrace();
}
});
}
private void updateDirtyStatus(boolean dirty) {
boolean previousStatus = this.dirty;
this.dirty = dirty;
if (previousStatus != dirty) {
firePropertyChange(PROP_DIRTY);
}
}
@Override
public void inputRemoved() {
Display display = getSite().getShell().getDisplay();
display.asyncExec(() -> getSite().getPage().closeEditor(FXMLEditor.this, false));
}
@Override
public void inputContentChanged() {
if (dirty) {
Display display = getSite().getShell().getDisplay();
display.asyncExec(() -> {
boolean reload = (Dialog.OK != MessageDialog.open(MessageDialog.WARNING, getSite().getShell(),
"Reload data?",
getEditorInput().getName() + " has changed outside editor, would you like to reload it?",
SWT.SHEET, "Keep editing", "Reload data"));
if (reload) {
loadFxml(fxmlUrl);
}
});
} else {
loadFxml(fxmlUrl);
}
}
@Override
public void inputMoved(IPath newLocation) {
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(newLocation);
FileEditorInput newInput = new FileEditorInput(file);
try {
loadFxml(file.getLocationURI().toURL());
Display display = getSite().getShell().getDisplay();
display.asyncExec(() -> {
setInputWithNotify(newInput);
setPartName(newInput.getName());
});
inputWatcher.setInput(file);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
} }
package no.tobask.sb4e.editors;
import org.eclipse.core.runtime.IPath;
public interface IInputChangeListener {
public void inputRemoved();
public void inputContentChanged();
public void inputMoved(IPath newLocation);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment