From d0abe3fd41e106c84a4b468beae30e720692cf32 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Fri, 24 Apr 2015 15:21:01 -0400 Subject: [PATCH] working on change detection --- app/src/processing/app/ChangeDetector.java | 269 +++++++++++---------- app/src/processing/app/Editor.java | 7 +- todo.txt | 21 +- 3 files changed, 158 insertions(+), 139 deletions(-) diff --git a/app/src/processing/app/ChangeDetector.java b/app/src/processing/app/ChangeDetector.java index 8b5abfebd..024c7a10b 100644 --- a/app/src/processing/app/ChangeDetector.java +++ b/app/src/processing/app/ChangeDetector.java @@ -8,95 +8,22 @@ import java.io.File; import java.io.FilenameFilter; import java.lang.reflect.InvocationTargetException; +import javax.swing.JOptionPane; + + public class ChangeDetector implements WindowFocusListener { private Sketch sketch; - private Editor editor; -// private boolean enabled = true; - private boolean enabled = true; // broken on OS X (possibly fixed? tested and it seems to work) - + private boolean enabled = true; private boolean skip = false; + public ChangeDetector(Editor editor) { this.sketch = editor.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; - } - checkFileChangeAsync(); - } - - private void checkFileChangeAsync() { - Thread th = new Thread(new Runnable() { - @Override - public void run() { - checkFileChange(); - } - }); - th.start(); - } - - private void showErrorAsync(final String title, final String message, - final Exception e) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - Base.showError(title, message, e); - } - }); - } - - private void showWarningAsync(final String title, final String message) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - Base.showWarning(title, message); - } - }); - } - - private int showYesNoQuestionAsync(final Frame editor, final String title, - final String message1, - final String message2) { - final int[] res = { -1 }; - try { - //have to wait for a response on this one - EventQueue.invokeAndWait(new Runnable() { - @Override - public void run() { - res[0] = Base.showYesNoQuestion(editor, title, message1, message2); - } - }); - } catch (InvocationTargetException e) { - //occurs if Base.showYesNoQuestion throws an error, so, shouldn't happen - e.printStackTrace(); - } catch (InterruptedException e) { - //occurs if the EDT is interrupted, so, shouldn't happen - e.printStackTrace(); - } - return res[0]; - } - - private void rebuildHeaderAsync() { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - editor.header.rebuild(); - } - }); - } private void checkFileChange() { //check that the content of each of the files in sketch matches what is in memory @@ -104,13 +31,13 @@ public class ChangeDetector implements WindowFocusListener { return; } - //make sure the sketch folder exists at all. if it does not, it will be re-saved, and no changes will be detected - // + // make sure the sketch folder exists at all. + // if it does not, it will be re-saved, and no changes will be detected sketch.ensureExistence(); - //check file count first + + // 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 + File[] sketchFiles = sketchFolder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { for (String s : editor.getMode().getExtensions()) { @@ -120,18 +47,18 @@ public class ChangeDetector implements WindowFocusListener { } return false; } - }).length; + }); + int fileCount = sketchFiles.length; if (fileCount != sketch.getCodeCount()) { - //if they chose to reload and there aren't any files left + // if they chose to reload and there aren't any files left if (reloadSketch(null) && fileCount < 1) { try { //make a blank file sketch.getMainFile().createNewFile(); } catch (Exception e1) { //if that didn't work, tell them it's un-recoverable - showErrorAsync("Reload failed", "The sketch contains no code files.", - e1); + showErrorEDT("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; @@ -139,9 +66,9 @@ public class ChangeDetector implements WindowFocusListener { } //it's okay to do this without confirmation, because they already confirmed to deleting the unsaved changes above sketch.reload(); - showWarningAsync("Modified Reload", - "You cannot delete the last code file in a sketch.\n" - + "A new blank sketch file has been generated for you."); + showWarningEDT("Modified Reload", + "You cannot delete the last code file in a sketch.\n" + + "A new blank sketch file has been generated for you."); } return; @@ -150,62 +77,152 @@ public class ChangeDetector implements WindowFocusListener { SketchCode[] codes = sketch.getCode(); for (SketchCode sc : codes) { 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; - } - //if a file's tab was saved before the file was - if (sketchFile.lastModified() > sc.lastModified()) { + if (sketchFile.exists()) { + long diff = sketchFile.lastModified() - sc.lastModified(); + if (diff != 0) { + if (Base.isMacOS() && diff == 1000L) { + // Mac OS X has a one second difference. Not sure if it's a Java bug + // or something else about how OS X is writing files. + continue; + } + System.out.println(sketchFile.getName() + " " + diff); + reloadSketch(sc); + return; + } + } else { + // If a file in the sketch was not found, then it must have been + // deleted externally, so reload the sketch. 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(SketchCode changed) { - //new Exception().printStackTrace(System.out); - int response = showYesNoQuestionAsync(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 + /** + * @param changed The file that was known to be modified + * @return true if the files in the sketch have been reloaded + */ + private boolean reloadSketch(SketchCode changed) { + int response = blockingYesNoPrompt(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 == JOptionPane.YES_OPTION) { sketch.reload(); - rebuildHeaderAsync(); + rebuildHeaderEDT(); return true; - } 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 - } - rebuildHeaderAsync(); - skip = true; - return false; } + + // they said no (or canceled), make it possible to stop the msgs 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 { + // Because the number of files changed, they may be working with a file + // that doesn't exist any more. So find the files that are missing, + // and mark them as modified so that the next "Save" will write them. + for (SketchCode sc : sketch.getCode()) { + if (!sc.getFile().exists()) { + setSketchCodeModified(sc); + } + } + // If files were simply added, then nothing needs done + } + rebuildHeaderEDT(); + skip = true; + return false; } + @Override public void windowLostFocus(WindowEvent e) { //shouldn't need to do anything here } + + @Override + public void windowGainedFocus(WindowEvent e) { + if (enabled) { + //remove the detector from main if it is disabled during runtime (due to an error?) + //if (!enabled || !Preferences.getBoolean("editor.watcher")) { + //editor.removeWindowFocusListener(this); + //} else if (skip) { + + // if they selected no, skip the next focus event + if (skip) { + skip = false; + + } else { + new Thread(new Runnable() { + @Override + public void run() { + checkFileChange(); + } + }).start(); + } + } + } + + + private void showErrorEDT(final String title, final String message, + final Exception e) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + Base.showError(title, message, e); + } + }); + } + + + private void showWarningEDT(final String title, final String message) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + Base.showWarning(title, message); + } + }); + } + + + private int blockingYesNoPrompt(final Frame editor, final String title, + final String message1, + final String message2) { + final int[] result = { -1 }; // yuck + try { + //have to wait for a response on this one + EventQueue.invokeAndWait(new Runnable() { + @Override + public void run() { + result[0] = Base.showYesNoQuestion(editor, title, message1, message2); + } + }); + } catch (InvocationTargetException e) { + //occurs if Base.showYesNoQuestion throws an error, so, shouldn't happen + e.getTargetException().printStackTrace(); + } catch (InterruptedException e) { + //occurs if the EDT is interrupted, so, shouldn't happen + e.printStackTrace(); + } + return result[0]; + } + + + private void rebuildHeaderEDT() { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + editor.header.rebuild(); + } + }); + } } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 535d9dd4d..0653b575d 100755 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -360,11 +360,8 @@ public abstract class Editor extends JFrame implements RunnerListener { sketch = null; } - //add a window listener to watch for changes to the files in the sketch - if (Preferences.getBoolean("editor.watcher")) { - addWindowFocusListener(new ChangeDetector(this)); - } - + // Add a window listener to watch for changes to the files in the sketch + addWindowFocusListener(new ChangeDetector(this)); } diff --git a/todo.txt b/todo.txt index 38cc2af24..a60a9c4e7 100644 --- a/todo.txt +++ b/todo.txt @@ -30,9 +30,9 @@ X the .macosx, .linux, etc prefs should be stripped X only use them on first load, and merge into preferences.txt X auto-insert after antlr @SuppressWarnings({ "unused", "unchecked", "cast" }) +_ editor window draws in stages (at least on OS X) on first view +_ "sketch modified" message still happening _ is debug turned on? lots of "export.txt" complaints -_ sketchbook window is completely empty w/ no sketches -_ requires restart of p5 before it updates _ install/remove buttons not working in the managers _ https://github.com/processing/processing/issues/3172 _ search the source for 'applet' references (i.e. SVG docs) @@ -215,8 +215,6 @@ _ https://github.com/processing/processing/issues/2953 _ don't return here, allow contrib types to fail: _ https://github.com/processing/processing/blob/master/app/src/processing/app/contrib/ContributionListing.java#L509 -_ need to handle the 2.x to 3.x sketchbook transition - _ Linux throwing IllegalStateException: Buffers have not been created _ in render() (called from blit) PSurfaceAWT:301 @@ -301,8 +299,6 @@ _ might be something with libraries (native or otherwise) _ move the language stuff to the settings folder _ that way people can modify and test w/o recompiling _ https://github.com/processing/processing/issues/2938 -_ sketchbook window doesn't update when sketches are added, renamed, etc -_ https://github.com/processing/processing/issues/2944 pending @@ -318,6 +314,17 @@ _ clean out the repo _ https://github.com/processing/processing/issues/1898 +sketchbook +_ need to handle the 2.x to 3.x sketchbook transition +_ "Save As" is reloading the whole sketchbook, argh +_ sketchbook window is completely empty w/ no sketches +_ requires restart of p5 before it updates +_ sketchbook window doesn't update when sketches are added, renamed, etc +_ https://github.com/processing/processing/issues/2944 +_ improve start time by populating sketchbook/libraries on threads +_ https://github.com/processing/processing/issues/2945 + + help me _ "String index out of range" error _ https://github.com/processing/processing/issues/1940 @@ -334,8 +341,6 @@ _ http://stackoverflow.com/questions/4933677/detecting-windows-ie-proxy-settin _ http://www.java2s.com/Code/Java/Network-Protocol/DetectProxySettingsforInternetConnection.htm _ problems with non-US keyboards and some shortcuts _ https://github.com/processing/processing/issues/2199 -_ improve start time by populating sketchbook/libraries on threads -_ https://github.com/processing/processing/issues/2945 _ clean up 'ant doc' target to remove warnings _ https://github.com/processing/processing/issues/1492 _ fix encodings, line endings, and mime types in the repo