Reworking file detection code to be independent of Java's detection code

Added error handling if Base.loadFile throws an IOException (disable
listener)
Fully removed the old file change detection code

Added support for choosing not to reload the sketch by setting the
changed files to modifed, so that when saved they will be updated on
disk
Made the reload confirmation message more clear in specifying that
unsaved changes will be destroyed
This commit is contained in:
Patrick Vares
2015-01-17 22:42:00 -05:00
parent b6f212b047
commit 17ab30836b
4 changed files with 166 additions and 146 deletions
+1 -1
View File
@@ -6,7 +6,6 @@
<attribute name="ignore_optional_problems" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/processing-core"/>
<classpathentry kind="lib" path="lib/antlr.jar" sourcepath="lib/antlr-src.zip"/>
<classpathentry kind="lib" path="lib/jna.jar"/>
@@ -19,5 +18,6 @@
<classpathentry kind="lib" path="lib/jdi.jar" sourcepath="/processing-experimental/mode/jdi-src.zip"/>
<classpathentry kind="lib" path="lib/jdimodel.jar" sourcepath="/processing-experimental/mode/jdimodel-src.zip"/>
<classpathentry kind="lib" path="lib/org.eclipse.osgi_3.8.1.v20120830-144521.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+155
View File
@@ -0,0 +1,155 @@
package processing.app;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
public class ChangeDetector implements WindowFocusListener {
private Sketch sketch;
private Editor editor;
private boolean enabled = true;
private boolean skip = false;
public ChangeDetector(Sketch sketch, Editor editor) {
this.sketch = sketch;
this.editor = editor;
}
@Override
public void windowGainedFocus(WindowEvent e) {
//remove the detector from main if it is disabled during runtime (due to an error?)
if (!enabled || !Preferences.getBoolean("editor.watcher")) {
editor.removeWindowFocusListener(this);
return;
}
//if they selected no, skip the next focus event
if (skip) {
skip = false;
return;
}
checkFileChange();
}
private void checkFileChange() {
//check that the content of each of the files in sketch matches what is in memory
if (sketch == null) {
return;
}
//check file count first
File sketchFolder = sketch.getFolder();
int fileCount = sketchFolder.list(new FilenameFilter() {
//return true if the file is a code file for this mode
@Override
public boolean accept(File dir, String name) {
for (String s : editor.getMode().getExtensions()) {
if (name.endsWith(s)) {
return true;
}
}
return false;
}
}).length;
if (fileCount != sketch.getCodeCount()) {
reloadSketch(null);
if (fileCount < 1) {
Base.showWarning("Canceling Reload",
"You cannot delete the last code file in a sketch.");
//if they deleted the last file, re-save the SketchCode
try {
//make a blank file
sketch.getMainFile().createNewFile();
} catch (Exception e1) {
//if that didn't work, tell them it's un-recoverable
Base.showError("Reload failed", "The sketch contains no code files",
e1);
//don't try to reload again after the double fail
//this editor is probably trashed by this point, but a save-as might be possible
skip = true;
}
}
return;
}
SketchCode[] codes = sketch.getCode();
for (SketchCode sc : codes) {
String inMemory = sc.getProgram();
String onDisk = null;
File sketchFile = sc.getFile();
if (!sketchFile.exists()) {
//if a file in the sketch was not found, then it must have been deleted externally
//so reload the sketch
reloadSketch(sc);
return;
}
try {
onDisk = Base.loadFile(sketchFile);
} catch (IOException e1) {
Base
.showWarningTiered("File Change Detection Failed",
"Checking for changed files for this sketch has failed.",
"The file change detector will be disabled.", e1);
enabled = false;
return;
}
if (onDisk == null) {
//failed
} else if (!inMemory.equals(onDisk)) {
reloadSketch(sc);
return;
}
}
}
private void setSketchCodeModified(SketchCode sc) {
sc.setModified(true);
sketch.setModified(true);
}
//returns true if the files in the sketch have been reloaded
private void reloadSketch(SketchCode changed) {
int response = Base
.showYesNoQuestion(editor,
"File Modified",
"Your sketch has been modified externally.<br>Would you like to reload the sketch?",
"If you reload the sketch, any unsaved changes will be lost!");
if (response == 0) {
//reload the sketch
sketch.reload();
editor.header.rebuild();
} else {
//they said no, make it possible for them to stop the errors by saving
if (changed != null) {
//set it to be modified so that it will actually save to disk when the user saves from inside processing
setSketchCodeModified(changed);
} else {
//the number of files changed, so they may be working with a file that doesn't exist any more
//find the files that are missing, and mark them as modified
for (SketchCode sc : sketch.getCode()) {
if (!sc.getFile().exists()) {
setSketchCodeModified(sc);
}
}
//if files were simply added, then nothing needs done
}
editor.header.rebuild();
skip = true;
return;
}
return;
}
@Override
public void windowLostFocus(WindowEvent e) {
//shouldn't need to do anything here
}
}
+10 -143
View File
@@ -33,7 +33,8 @@ import java.awt.datatransfer.*;
import java.awt.event.*;
import java.awt.print.*;
import java.io.*;
import java.nio.file.*;
//REMOVE
//import java.nio.file.*;
import java.util.*;
import java.util.List;
import java.util.Timer;
@@ -286,6 +287,12 @@ public abstract class Editor extends JFrame implements RunnerListener {
if (!loaded) {
sketch = null;
}
//add a window listener to watch for changes to the files in the sketch
if (Preferences.getBoolean("editor.watcher")) {
addWindowFocusListener(new ChangeDetector(sketch, this));
}
}
@@ -2459,10 +2466,6 @@ public abstract class Editor extends JFrame implements RunnerListener {
Base.showWarning("Error", "Could not create the sketch.", e);
return false;
}
// Disabling for 3.0a4
if (false && Preferences.getBoolean("editor.watcher")) {
initFileChangeListener();
}
header.rebuild();
updateTitle();
@@ -2483,134 +2486,6 @@ public abstract class Editor extends JFrame implements RunnerListener {
// return false;
// }
}
//used to prevent the fileChangeListener from asking for reloads after internal changes
public void setWatcherSave() {
watcherSave = true;
}
//set to true when the sketch is saved from inside processing
private boolean watcherSave;
//the key which is being used to poll the fs for changes
private WatchKey watcherKey = null;
private void initFileChangeListener() {
try {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path sp = sketch.getFolder().toPath();
watcherKey = sp.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
} catch (IOException e) {
e.printStackTrace();
}
final WatchKey finKey = watcherKey;
//if the key is null for some reason, don't bother attaching a listener to it
if (finKey != null) {
// the key can now be polled for changes in the files
addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(WindowEvent arg0) {
//we switched locations (saveAs), ignore old things
if (watcherKey != finKey) {
return;
}
// check preference here for enabled or not?
//if the directory was deleted, then don't scan
if (finKey.isValid()) {
List<WatchEvent<?>> events = finKey.pollEvents();
if (!watcherSave) {
processFileEvents(events);
}
}
List<WatchEvent<?>> events = finKey.pollEvents();
if (!watcherSave)
processFileEvents(events);
}
@Override
public void windowLostFocus(WindowEvent arg0) {
//we switched locations (saveAs), ignore old things
if (watcherKey != finKey) {
return;
}
List<WatchEvent<?>> events = finKey.pollEvents();
//don't ask to reload a file we saved
if (!watcherSave) {
processFileEvents(events);
}
watcherSave = false;
}
});
}
}
/**
* Called when a file is changed.
* @param events the list of events that have occured in the sketch folder
*/
private void processFileEvents(List<WatchEvent<?>> events) {
for (WatchEvent<?> e : events) {
boolean sketchFile = false;
Path file = ((Path) e.context()).getFileName();
System.out.println(file);
for (String s : getMode().getExtensions()) {
// if it is a change to a file with a known extension
if (file.toString().endsWith(s)) {
sketchFile = true;
break;
}
}
//if the file is not a known type, then go the the next event
if (!sketchFile) {
continue;
}
int response =
Base.showYesNoQuestion(Editor.this,
"File Modified",
"Your sketch has been modified externally",
"Would you like to reload the sketch?");
if (response == 0) {
//grab the 'main' code in case this reload tries to delete everything
File sc = sketch.getMainFile();
//reload the sketch
try {
sketch.reload();
header.rebuild();
} catch (Exception f) {
if (sketch.getCodeCount() < 1) {
Base.showWarning("Canceling Reload",
"You cannot delete the last code file in a sketch.");
//if they deleted the last file, re-save the SketchCode
try {
//make a blank file
sc.createNewFile();
} catch (IOException e1) {
//if that didn't work, tell them it's un-recoverable
Base.showError("Reload failed", "The sketch contains no code files", e1);
//don't try to reload again after the double fail
//this editor is probably trashed by this point, but a save-as might be possible
break;
}
//don't ask for another reload after this save
watcherSave = true;
return;
}
}
//now that we've reloaded once, don't try to reload again
break;
}
}
watcherSave = false;
}
/**
* Set the title of the PDE window based on the current sketch, i.e.
@@ -2641,8 +2516,7 @@ public abstract class Editor extends JFrame implements RunnerListener {
*/
public boolean handleSave(boolean immediately) {
// handleStop(); // 0136
setWatcherSave();
if (sketch.isUntitled()) {
return handleSaveAs();
// need to get the name, user might also cancel here
@@ -2686,13 +2560,6 @@ public abstract class Editor extends JFrame implements RunnerListener {
statusNotice(Language.text("editor.status.saving"));
try {
if (sketch.saveAs()) {
// Disabling for 3.0a4
if (false && Preferences.getBoolean("editor.watcher")) {
// "Save As" moves where the files are, so a listener must be
// attached to the new location.
// TODO shouldn't this remove the old listener?
initFileChangeListener();
}
// statusNotice("Done Saving.");
// status is now printed from Sketch so that "Done Saving."
// is only printed after Save As when progress bar is shown.
@@ -3040,7 +2907,7 @@ public abstract class Editor extends JFrame implements RunnerListener {
cutItem.setEnabled(active);
copyItem.setEnabled(active);
discourseItem.setEnabled(active);
referenceItem.setEnabled(referenceCheck(false) != null);
super.show(component, x, y);
}
-2
View File
@@ -516,7 +516,6 @@ public class EditorHeader extends JComponent {
@Override
public void actionPerformed(ActionEvent e) {
editor.getSketch().handleNewCode();
editor.setWatcherSave();
}
};
mapKey = "editor.header.new_tab";
@@ -554,7 +553,6 @@ public class EditorHeader extends JComponent {
Language.text("editor.header.delete.warning.text"), null);
} else {
editor.getSketch().handleDeleteCode();
editor.setWatcherSave();
}
}
};