From 001959079ae0d3eaebb8b86a80d800b80aa57b80 Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sat, 17 Jan 2015 20:56:26 -0500 Subject: [PATCH 1/7] Revert "additional workaround for Canvas-only code" This reverts commit 234f310dc19eaef87981fb3d0477df659fdf9a60. --- core/src/processing/core/PSurfaceAWT.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/processing/core/PSurfaceAWT.java b/core/src/processing/core/PSurfaceAWT.java index d40e2bda3..b5594d450 100644 --- a/core/src/processing/core/PSurfaceAWT.java +++ b/core/src/processing/core/PSurfaceAWT.java @@ -831,13 +831,9 @@ public class PSurfaceAWT implements PSurface { // canvas.setSize(wide, high); // frame.setSize(wide, high); - if (frame != null) { // canvas only - setFrameSize(); //wide, high); - } + setFrameSize(); //wide, high); setCanvasSize(); - if (frame != null) { - frame.setLocationRelativeTo(null); - } + frame.setLocationRelativeTo(null); GraphicsConfiguration gc = canvas.getGraphicsConfiguration(); // If not realized (off-screen, i.e the Color Selector Tool), gc will be null. From 50377c65c9ea66eea8663e9215c210f2e149f392 Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sat, 17 Jan 2015 22:42:00 -0500 Subject: [PATCH 2/7] Commented out old change detection from Editor.java and EditorHeader.java Added a new ChangeDetector class which does not rely on Java 7's FileWatcher --- app/.classpath | 2 +- app/src/processing/app/ChangeDetector.java | 131 ++++++++++ app/src/processing/app/Editor.java | 284 +++++++++++---------- app/src/processing/app/EditorHeader.java | 6 +- 4 files changed, 285 insertions(+), 138 deletions(-) create mode 100644 app/src/processing/app/ChangeDetector.java diff --git a/app/.classpath b/app/.classpath index e12f72e7e..00411841a 100644 --- a/app/.classpath +++ b/app/.classpath @@ -6,7 +6,6 @@ - @@ -19,5 +18,6 @@ + diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java new file mode 100644 index 000000000..5d08695dc --- /dev/null +++ b/app/src/processing/app/ChangeDetector.java @@ -0,0 +1,131 @@ +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; + + public ChangeDetector(Sketch sketch, Editor editor) { + this.sketch = sketch; + this.editor = editor; + } + + @Override + public void windowGainedFocus(WindowEvent e) { + long start = System.currentTimeMillis(); + checkFileChange(); + long end = System.currentTimeMillis(); + long diff = end - start; + System.out.println("Full file change scan took " + (diff / 1000.) + + " seconds"); + } + + 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(); + if (sketchFolder.isDirectory()) { + 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()) { + System.out.println("filecount = " + fileCount + "\tsketchCount = " + + sketch.getCodeCount()); + if (reloadSketch()) { + return; + } + } + } + + SketchCode[] codes = sketch.getCode(); + for (SketchCode sc : codes) { + //REMOVE + System.out.println("Checking " + sc.getFileName()); + String inMemory = sc.getProgram(); + String onDisk = null; + File f = sc.getFile(); + if (!f.exists()) { + //if a file in the sketch was not found, then it must have been deleted externally + //so reload the sketch + if (reloadSketch()) { + break; + } + } + try { + onDisk = Base.loadFile(f); + } catch (Exception e1) { + // TODO couldn't access file for some reason + // deal with it + System.out.println("Loading file failed"); + e1.printStackTrace(); + } + if (onDisk == null) { + //failed + } else if (!inMemory.equals(onDisk)) { + if (reloadSketch()) { + break; + } + } + } + } + + //returns true if the files in the sketch have been reloaded + private boolean reloadSketch() { + int response = Base + .showYesNoQuestion(editor, "File Modified", + "Your sketch has been modified externally", + "Would you like to reload the sketch?"); + if (response == 0) { + File mainFile = sketch.getMainFile(); + //reload the sketch + try { + sketch.reload(); + editor.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 + mainFile.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 + return false; + } + } + } + } + return true; + } + + @Override + public void windowLostFocus(WindowEvent e) { + //shouldn't need to do anything here + } + +} diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 1b22f95cb..ee6f2b1fe 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -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; @@ -107,6 +108,8 @@ public abstract class Editor extends JFrame implements RunnerListener { // maintain caret position during undo operations private final Stack caretUndoStack = new Stack(); private final Stack caretRedoStack = new Stack(); + + private ChangeDetector changeDetector; private FindReplace find; JMenu toolsMenu; @@ -286,6 +289,13 @@ public abstract class Editor extends JFrame implements RunnerListener { if (!loaded) { sketch = null; } + + //TODO new implementation of file change listener here + if (Preferences.getBoolean("editor.watcher")) { + changeDetector = new ChangeDetector(sketch, this); + addWindowFocusListener(changeDetector); + } + } @@ -2459,10 +2469,11 @@ 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(); - } + //REMOVE delete +// // Disabling for 3.0a4 +// if (false && Preferences.getBoolean("editor.watcher")) { +// initFileChangeListener(); +// } header.rebuild(); updateTitle(); @@ -2484,133 +2495,134 @@ public abstract class Editor extends JFrame implements RunnerListener { // } } + //REMOVE delete //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> events = finKey.pollEvents(); - if (!watcherSave) { - processFileEvents(events); - } - } - - List> 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> events = finKey.pollEvents(); - //don't ask to reload a file we saved - if (!watcherSave) { - processFileEvents(events); - } - watcherSave = false; - } - }); - } - } +// 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> events = finKey.pollEvents(); +// if (!watcherSave) { +// processFileEvents(events); +// } +// } +// +// List> 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> 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> 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; - } +// private void processFileEvents(List> 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 +2653,9 @@ public abstract class Editor extends JFrame implements RunnerListener { */ public boolean handleSave(boolean immediately) { // handleStop(); // 0136 - - setWatcherSave(); + + //REMOVE + //setWatcherSave(); if (sketch.isUntitled()) { return handleSaveAs(); // need to get the name, user might also cancel here @@ -2686,13 +2699,14 @@ 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(); - } + //REMOVE +// // 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 +3054,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); } diff --git a/app/src/processing/app/EditorHeader.java b/app/src/processing/app/EditorHeader.java index 94e7c50f7..fc7a00dd3 100644 --- a/app/src/processing/app/EditorHeader.java +++ b/app/src/processing/app/EditorHeader.java @@ -516,7 +516,8 @@ public class EditorHeader extends JComponent { @Override public void actionPerformed(ActionEvent e) { editor.getSketch().handleNewCode(); - editor.setWatcherSave(); + //REMOVE + //editor.setWatcherSave(); } }; mapKey = "editor.header.new_tab"; @@ -554,7 +555,8 @@ public class EditorHeader extends JComponent { Language.text("editor.header.delete.warning.text"), null); } else { editor.getSketch().handleDeleteCode(); - editor.setWatcherSave(); + //REMOVE + //editor.setWatcherSave(); } } }; From c9838c89ddda2f080fbf4eaec6e1c4c5c35e417c Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sat, 17 Jan 2015 22:57:01 -0500 Subject: [PATCH 3/7] Added error handling if Base.loadFile throws an IOException (disable listener) Fully removed the old file change detection code --- app/src/processing/app/ChangeDetector.java | 37 +++-- app/src/processing/app/Editor.java | 151 +-------------------- app/src/processing/app/EditorHeader.java | 4 - 3 files changed, 20 insertions(+), 172 deletions(-) diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java index 5d08695dc..73ad3a6ce 100644 --- a/app/src/processing/app/ChangeDetector.java +++ b/app/src/processing/app/ChangeDetector.java @@ -11,6 +11,8 @@ public class ChangeDetector implements WindowFocusListener { private Editor editor; + private boolean enabled = true; + public ChangeDetector(Sketch sketch, Editor editor) { this.sketch = sketch; this.editor = editor; @@ -18,16 +20,15 @@ public class ChangeDetector implements WindowFocusListener { @Override public void windowGainedFocus(WindowEvent e) { - long start = System.currentTimeMillis(); + //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; + } checkFileChange(); - long end = System.currentTimeMillis(); - long diff = end - start; - System.out.println("Full file change scan took " + (diff / 1000.) - + " seconds"); } private void checkFileChange() { - //check that the content of each of the files in sketch matches what is in memory if (sketch == null) { return; @@ -49,8 +50,6 @@ public class ChangeDetector implements WindowFocusListener { } }).length; if (fileCount != sketch.getCodeCount()) { - System.out.println("filecount = " + fileCount + "\tsketchCount = " - + sketch.getCodeCount()); if (reloadSketch()) { return; } @@ -59,12 +58,10 @@ public class ChangeDetector implements WindowFocusListener { SketchCode[] codes = sketch.getCode(); for (SketchCode sc : codes) { - //REMOVE - System.out.println("Checking " + sc.getFileName()); String inMemory = sc.getProgram(); String onDisk = null; - File f = sc.getFile(); - if (!f.exists()) { + 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 if (reloadSketch()) { @@ -72,18 +69,20 @@ public class ChangeDetector implements WindowFocusListener { } } try { - onDisk = Base.loadFile(f); - } catch (Exception e1) { - // TODO couldn't access file for some reason - // deal with it - System.out.println("Loading file failed"); - e1.printStackTrace(); + 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)) { if (reloadSketch()) { - break; + return; } } } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index ee6f2b1fe..895df615b 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -108,8 +108,6 @@ public abstract class Editor extends JFrame implements RunnerListener { // maintain caret position during undo operations private final Stack caretUndoStack = new Stack(); private final Stack caretRedoStack = new Stack(); - - private ChangeDetector changeDetector; private FindReplace find; JMenu toolsMenu; @@ -290,10 +288,9 @@ public abstract class Editor extends JFrame implements RunnerListener { sketch = null; } - //TODO new implementation of file change listener here + //add a window listener to watch for changes to the files in the sketch if (Preferences.getBoolean("editor.watcher")) { - changeDetector = new ChangeDetector(sketch, this); - addWindowFocusListener(changeDetector); + addWindowFocusListener(new ChangeDetector(sketch, this)); } } @@ -2469,11 +2466,6 @@ public abstract class Editor extends JFrame implements RunnerListener { Base.showWarning("Error", "Could not create the sketch.", e); return false; } - //REMOVE delete -// // Disabling for 3.0a4 -// if (false && Preferences.getBoolean("editor.watcher")) { -// initFileChangeListener(); -// } header.rebuild(); updateTitle(); @@ -2494,135 +2486,6 @@ public abstract class Editor extends JFrame implements RunnerListener { // return false; // } } - - //REMOVE delete - //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> events = finKey.pollEvents(); -// if (!watcherSave) { -// processFileEvents(events); -// } -// } -// -// List> 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> 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> 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. @@ -2654,8 +2517,6 @@ public abstract class Editor extends JFrame implements RunnerListener { public boolean handleSave(boolean immediately) { // handleStop(); // 0136 - //REMOVE - //setWatcherSave(); if (sketch.isUntitled()) { return handleSaveAs(); // need to get the name, user might also cancel here @@ -2699,14 +2560,6 @@ public abstract class Editor extends JFrame implements RunnerListener { statusNotice(Language.text("editor.status.saving")); try { if (sketch.saveAs()) { - //REMOVE -// // 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. diff --git a/app/src/processing/app/EditorHeader.java b/app/src/processing/app/EditorHeader.java index fc7a00dd3..3b4183195 100644 --- a/app/src/processing/app/EditorHeader.java +++ b/app/src/processing/app/EditorHeader.java @@ -516,8 +516,6 @@ public class EditorHeader extends JComponent { @Override public void actionPerformed(ActionEvent e) { editor.getSketch().handleNewCode(); - //REMOVE - //editor.setWatcherSave(); } }; mapKey = "editor.header.new_tab"; @@ -555,8 +553,6 @@ public class EditorHeader extends JComponent { Language.text("editor.header.delete.warning.text"), null); } else { editor.getSketch().handleDeleteCode(); - //REMOVE - //editor.setWatcherSave(); } } }; From 117b5cc6b5e949ea010ecf0f6dd1c4e17246127c Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sun, 18 Jan 2015 00:19:23 -0500 Subject: [PATCH 4/7] 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 --- app/src/processing/app/ChangeDetector.java | 113 +++++++++++++-------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java index 73ad3a6ce..33f4eb567 100644 --- a/app/src/processing/app/ChangeDetector.java +++ b/app/src/processing/app/ChangeDetector.java @@ -13,6 +13,8 @@ public class ChangeDetector implements WindowFocusListener { private boolean enabled = true; + private boolean skip = false; + public ChangeDetector(Sketch sketch, Editor editor) { this.sketch = sketch; this.editor = editor; @@ -25,6 +27,11 @@ public class ChangeDetector implements WindowFocusListener { editor.removeWindowFocusListener(this); return; } + //if they selected no, skip the next focus event + if (skip) { + skip = false; + return; + } checkFileChange(); } @@ -36,24 +43,38 @@ public class ChangeDetector implements WindowFocusListener { //check file count first File sketchFolder = sketch.getFolder(); - if (sketchFolder.isDirectory()) { - 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; - } + 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()) { - if (reloadSketch()) { - return; + 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(); @@ -64,9 +85,8 @@ public class ChangeDetector implements WindowFocusListener { if (!sketchFile.exists()) { //if a file in the sketch was not found, then it must have been deleted externally //so reload the sketch - if (reloadSketch()) { - break; - } + reloadSketch(sc); + return; } try { onDisk = Base.loadFile(sketchFile); @@ -81,45 +101,50 @@ public class ChangeDetector implements WindowFocusListener { if (onDisk == null) { //failed } else if (!inMemory.equals(onDisk)) { - if (reloadSketch()) { - return; - } + 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 boolean reloadSketch() { + private void reloadSketch(SketchCode changed) { int response = Base - .showYesNoQuestion(editor, "File Modified", - "Your sketch has been modified externally", - "Would you like to reload the sketch?"); + .showYesNoQuestion(editor, + "File Modified", + "Your sketch has been modified externally.
Would you like to reload the sketch?", + "If you reload the sketch, any unsaved changes will be lost!"); if (response == 0) { - File mainFile = sketch.getMainFile(); //reload the sketch - try { - sketch.reload(); - editor.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 - mainFile.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 - return false; + + 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 true; + return; } @Override From 7450b4a887cdd2625a63c430295475e5eb19cc91 Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sun, 18 Jan 2015 00:37:53 -0500 Subject: [PATCH 5/7] More support for attempting to reload after deleting the entire sketch Changed ChangeDetector's constructor's signature to only require an Editor --- app/src/processing/app/ChangeDetector.java | 20 +++++++++++++------- app/src/processing/app/Editor.java | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java index 33f4eb567..0d18574d4 100644 --- a/app/src/processing/app/ChangeDetector.java +++ b/app/src/processing/app/ChangeDetector.java @@ -15,8 +15,8 @@ public class ChangeDetector implements WindowFocusListener { private boolean skip = false; - public ChangeDetector(Sketch sketch, Editor editor) { - this.sketch = sketch; + public ChangeDetector(Editor editor) { + this.sketch = editor.sketch; this.editor = editor; } @@ -55,24 +55,30 @@ public class ChangeDetector implements WindowFocusListener { 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", + 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; } + //it's okay to do this without confirmation, because they already confirmed to deleting the unsaved changes above + sketch.reload(); + editor.header.rebuild(); + Base + .showWarning("Modified Reload", + "You cannot delete the last code file in a sketch.\n" + + "A new blank sketch file has been generated for you."); + } return; } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 895df615b..9b706a202 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -290,7 +290,7 @@ public abstract class Editor extends JFrame implements RunnerListener { //add a window listener to watch for changes to the files in the sketch if (Preferences.getBoolean("editor.watcher")) { - addWindowFocusListener(new ChangeDetector(sketch, this)); + addWindowFocusListener(new ChangeDetector(this)); } } From 17ab30836b99e4495cb130a1b931a9baa4dc08b2 Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sat, 17 Jan 2015 22:42:00 -0500 Subject: [PATCH 6/7] 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 --- app/.classpath | 2 +- app/src/processing/app/ChangeDetector.java | 155 +++++++++++++++++++++ app/src/processing/app/Editor.java | 153 ++------------------ app/src/processing/app/EditorHeader.java | 2 - 4 files changed, 166 insertions(+), 146 deletions(-) create mode 100644 app/src/processing/app/ChangeDetector.java diff --git a/app/.classpath b/app/.classpath index e12f72e7e..00411841a 100644 --- a/app/.classpath +++ b/app/.classpath @@ -6,7 +6,6 @@
- @@ -19,5 +18,6 @@ + diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java new file mode 100644 index 000000000..33f4eb567 --- /dev/null +++ b/app/src/processing/app/ChangeDetector.java @@ -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.
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 + } + +} diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 1b22f95cb..895df615b 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -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> events = finKey.pollEvents(); - if (!watcherSave) { - processFileEvents(events); - } - } - - List> 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> 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> 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); } diff --git a/app/src/processing/app/EditorHeader.java b/app/src/processing/app/EditorHeader.java index 94e7c50f7..3b4183195 100644 --- a/app/src/processing/app/EditorHeader.java +++ b/app/src/processing/app/EditorHeader.java @@ -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(); } } }; From 46a712f97b562d5893b8d5e98916cc58bfde6ff8 Mon Sep 17 00:00:00 2001 From: Patrick Vares Date: Sun, 18 Jan 2015 00:37:53 -0500 Subject: [PATCH 7/7] Moved all file change detection code out to ChangeDetector.java No longer relies on Java's FileWatcher Dialogs are more clear in emphasizing that changes will be lost on reload More support for choosing not to reload by setting the changed files to be modified --- app/src/processing/app/ChangeDetector.java | 20 +++++++++++++------- app/src/processing/app/Editor.java | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java index 33f4eb567..0d18574d4 100644 --- a/app/src/processing/app/ChangeDetector.java +++ b/app/src/processing/app/ChangeDetector.java @@ -15,8 +15,8 @@ public class ChangeDetector implements WindowFocusListener { private boolean skip = false; - public ChangeDetector(Sketch sketch, Editor editor) { - this.sketch = sketch; + public ChangeDetector(Editor editor) { + this.sketch = editor.sketch; this.editor = editor; } @@ -55,24 +55,30 @@ public class ChangeDetector implements WindowFocusListener { 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", + 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; } + //it's okay to do this without confirmation, because they already confirmed to deleting the unsaved changes above + sketch.reload(); + editor.header.rebuild(); + Base + .showWarning("Modified Reload", + "You cannot delete the last code file in a sketch.\n" + + "A new blank sketch file has been generated for you."); + } return; } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 895df615b..9b706a202 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -290,7 +290,7 @@ public abstract class Editor extends JFrame implements RunnerListener { //add a window listener to watch for changes to the files in the sketch if (Preferences.getBoolean("editor.watcher")) { - addWindowFocusListener(new ChangeDetector(sketch, this)); + addWindowFocusListener(new ChangeDetector(this)); } }