mirror of
https://github.com/processing/processing4.git
synced 2026-06-16 04:26:26 +02:00
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:
+1
-1
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user