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..0d18574d4 --- /dev/null +++ b/app/src/processing/app/ChangeDetector.java @@ -0,0 +1,161 @@ +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(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; + } + 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) { + 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; + } + //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; + } + + 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..9b706a202 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(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(); } } }; diff --git a/core/src/processing/core/PSurfaceAWT.java b/core/src/processing/core/PSurfaceAWT.java index 35c405b07..c5c6c0c23 100644 --- a/core/src/processing/core/PSurfaceAWT.java +++ b/core/src/processing/core/PSurfaceAWT.java @@ -831,10 +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(); + frame.setLocationRelativeTo(null); // if (frame != null) { // frame.setLocationRelativeTo(null); // }