From 92550dbc9457943c20a06d349b4501f9e2a5fc53 Mon Sep 17 00:00:00 2001 From: Ben Fry Date: Sat, 12 Mar 2022 20:35:32 -0500 Subject: [PATCH] allow update of the current Mode --- app/src/processing/app/Base.java | 15 +++- .../app/contrib/LocalContribution.java | 81 +++++++++++++------ .../app/contrib/StatusPanelDetail.java | 68 +++++++++++++++- todo.txt | 12 ++- 4 files changed, 140 insertions(+), 36 deletions(-) diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 17db45e6e..395ec55b7 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -1733,11 +1733,12 @@ public class Base { /** * Close a sketch as specified by its editor window. * @param editor Editor object of the sketch to be closed. - * @param modeSwitch Whether this close is being done in the context of a - * mode switch. + * @param preventQuit For platforms that must have a window open, + * prevent a quit because a new window will be opened + * (i.e. when upgrading or changing the Mode) * @return true if succeeded in closing, false if canceled. */ - public boolean handleClose(Editor editor, boolean modeSwitch) { + public boolean handleClose(Editor editor, boolean preventQuit) { if (!editor.checkModified()) { return false; } @@ -1775,14 +1776,20 @@ public class Base { } } + /* + // wow, this is wrong (should only be called after the last window) + // but also outdated, because it's instance_server.* not server.* + // and Preferences.save() is also about restoring sketches. + Preferences.unset("server.port"); //$NON-NLS-1$ Preferences.unset("server.key"); //$NON-NLS-1$ // Save out the current prefs state Preferences.save(); + */ if (defaultFileMenu == null) { - if (modeSwitch) { + if (preventQuit) { // need to close this editor, ever so temporarily editor.setVisible(false); editor.dispose(); diff --git a/app/src/processing/app/contrib/LocalContribution.java b/app/src/processing/app/contrib/LocalContribution.java index 3c40abb26..579c590ed 100644 --- a/app/src/processing/app/contrib/LocalContribution.java +++ b/app/src/processing/app/contrib/LocalContribution.java @@ -298,41 +298,70 @@ public abstract class LocalContribution extends Contribution { /** * Non-blocking call to remove a contribution in a new thread. */ - void removeContribution(final Base base, - final ContribProgress pm, - final StatusPanel status) { + protected void removeContribution(Base base, + ContribProgress pm, + StatusPanel status, + boolean updating) { // TODO: replace with SwingWorker [jv] - new Thread(() -> remove(base, pm, status, ContributionListing.getInstance()), "Contribution Uninstaller").start(); + new Thread(() -> remove(base, pm, status, updating), "Contribution Uninstaller").start(); } - void remove(final Base base, - final ContribProgress pm, - final StatusPanel status, - final ContributionListing contribListing) { + private void remove(Base base, ContribProgress pm, StatusPanel status, boolean updating) { pm.startTask("Removing"); boolean doBackup = Preferences.getBoolean("contribution.backup.on_remove"); if (getType() == ContributionType.MODE) { - boolean isModeActive = false; + //Set sketches = new HashSet<>(); + List editors = new ArrayList<>(); // might be nice to be in order ModeContribution m = (ModeContribution) this; - for (Editor e : base.getEditors()) { - if (e.getMode().equals(m.getMode())) { - isModeActive = true; - break; + for (Editor editor : base.getEditors()) { + if (editor.getMode().equals(m.getMode())) { + Sketch sketch = editor.getSketch(); + if (sketch.isModified()) { + pm.cancel(); + editor.toFront(); + Messages.showMessage("Save Sketch", + "Please first save “" + sketch.getName() + "”."); + return; + } else { + // Keep track of open Editor windows using this Mode + //sketchMainList.add(sketch.getMainPath()); + //sketches.add(sketch); + editors.add(editor); + } } } - if (!isModeActive) { - m.clearClassLoader(base); - } else { + // Close any open Editor windows that were using this Mode, + // and if updating, build up a list of paths for the sketches + // so that we can dispose of the Editor objects. + //StringList sketchPathList = new StringList(); + for (Editor editor : editors) { + //sketchPathList.append(editor.getSketch().getMainPath()); + StatusPanelDetail.storeSketchPath(editor.getSketch().getMainPath()); + base.handleClose(editor, true); + } + editors.clear(); + m.clearClassLoader(base); + //StatusPanelDetail.storeSketches(sketchPathList); + + /* pm.cancel(); Messages.showMessage("Mode Manager", "Please save your Sketch and change the Mode of all Editor\n" + "windows that have " + name + " as the active Mode."); return; + */ + + if (!updating) { + // Notify the Base in case this is the current Mode + base.modeRemoved(m.getMode()); + // If that was the last Editor window, and we deleted its Mode, + // open a fresh window using the default Mode. + if (base.getEditors().size() == 0) { + base.handleNew(); + } } - // Notify the Base in case this is the current Mode - base.modeRemoved(m.getMode()); } if (getType() == ContributionType.TOOL) { @@ -357,16 +386,18 @@ public abstract class LocalContribution extends Contribution { try { // TODO: run this in SwingWorker done() [jv] EventQueue.invokeAndWait(() -> { + ContributionListing cl = ContributionListing.getInstance(); + Contribution advertisedVersion = - contribListing.getAvailableContribution(LocalContribution.this); + cl.getAvailableContribution(LocalContribution.this); if (advertisedVersion == null) { - contribListing.removeContribution(LocalContribution.this); + cl.removeContribution(LocalContribution.this); } else { - contribListing.replaceContribution(LocalContribution.this, advertisedVersion); + cl.replaceContribution(LocalContribution.this, advertisedVersion); } base.refreshContribs(LocalContribution.this.getType()); - base.setUpdatesAvailable(contribListing.countUpdates(base)); + base.setUpdatesAvailable(cl.countUpdates(base)); }); } catch (InterruptedException e) { e.printStackTrace(); @@ -386,10 +417,11 @@ public abstract class LocalContribution extends Contribution { try { // TODO: run this in SwingWorker done() [jv] EventQueue.invokeAndWait(() -> { - contribListing.replaceContribution(LocalContribution.this, + ContributionListing cl = ContributionListing.getInstance(); + cl.replaceContribution(LocalContribution.this, LocalContribution.this); base.refreshContribs(LocalContribution.this.getType()); - base.setUpdatesAvailable(contribListing.countUpdates(base)); + base.setUpdatesAvailable(cl.countUpdates(base)); }); } catch (InterruptedException e) { e.printStackTrace(); @@ -436,7 +468,6 @@ public abstract class LocalContribution extends Contribution { * @return String[] packageNames (without wildcards) or null if none are specified */ public StringList getImports() { - //return imports != null ? imports.toArray(new String[0]) : null; return imports; } diff --git a/app/src/processing/app/contrib/StatusPanelDetail.java b/app/src/processing/app/contrib/StatusPanelDetail.java index 81d3c3366..dc15dc10f 100644 --- a/app/src/processing/app/contrib/StatusPanelDetail.java +++ b/app/src/processing/app/contrib/StatusPanelDetail.java @@ -25,6 +25,8 @@ import java.awt.Component; import java.awt.Dimension; import java.net.MalformedURLException; import java.net.URL; +import java.util.AbstractQueue; +import java.util.concurrent.ConcurrentLinkedQueue; import javax.swing.JProgressBar; @@ -38,7 +40,6 @@ import processing.app.ui.Toolkit; */ class StatusPanelDetail { private final ListPanel listPanel; - private final ContributionListing contribListing = ContributionListing.getInstance(); static private final int BUTTON_WIDTH = Toolkit.zoom(100); @@ -148,6 +149,9 @@ class StatusPanelDetail { ContribProgress installProgress = new ContribProgress(progressBar) { public void finishedAction() { finishInstall(isException()); + + // if it was a Mode, restore any sketches + restoreSketches(); } public void cancelAction() { @@ -171,7 +175,7 @@ class StatusPanelDetail { installInProgress = true; if (contrib instanceof AvailableContribution) { installContribution((AvailableContribution) contrib); - contribListing.replaceContribution(contrib, contrib); + ContributionListing.getInstance().replaceContribution(contrib, contrib); } } @@ -182,6 +186,10 @@ class StatusPanelDetail { public void update() { clearStatusMessage(); updateInProgress = true; + + ContributionListing contribListing = ContributionListing.getInstance(); + + // TODO not really a 'restart' anymore, just requires care [fry 220312] if (contrib.getType().requiresRestart()) { // For the special "Updates" tab in the manager, there are no progress // bars, so if that's what we're doing, this will create a dummy bar. @@ -198,7 +206,12 @@ class StatusPanelDetail { resetProgressBar(); AvailableContribution ad = contribListing.getAvailableContribution(contrib); + // install the new version of the Mode (or Tool) installContribution(ad, ad.link); + // if it was a Mode, restore any sketches + //if (contrib.getType() == ContributionType.MODE) { + //restoreSketches(); + //} } @Override @@ -213,7 +226,7 @@ class StatusPanelDetail { } } }; - getLocalContrib().removeContribution(getBase(), progress, getStatusPanel()); + getLocalContrib().removeContribution(getBase(), progress, getStatusPanel(), true); } else { AvailableContribution ad = @@ -243,7 +256,7 @@ class StatusPanelDetail { removeInProgress = false; } }; - getLocalContrib().removeContribution(getBase(), progress, getStatusPanel()); + getLocalContrib().removeContribution(getBase(), progress, getStatusPanel(), false); } } @@ -251,6 +264,53 @@ class StatusPanelDetail { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + static AbstractQueue restoreQueue = new ConcurrentLinkedQueue<>(); + + + static protected void storeSketchPath(String path) { + restoreQueue.add(path); + } + + + protected void restoreSketches() { + while (!restoreQueue.isEmpty()) { + String path = restoreQueue.remove(); + getBase().handleOpen(path); + } + } + + + /* + static final String SAVED_COUNT = "mode.update.sketch.count"; + + + static protected void storeSketches(StringList sketchPathList) { + Preferences.setInteger(SAVED_COUNT, sketchPathList.size()); + int index = 0; + for (String path : sketchPathList) { + Preferences.set("mode.update.sketch." + index, path); + index++; + } + } + + + protected void restoreSketches() { + if (Preferences.get(SAVED_COUNT) != null) { + int count = Preferences.getInteger(SAVED_COUNT); + for (int i = 0; i < count; i++) { + String key = "mode.update.sketch." + i; + String path = Preferences.get(key); + getBase().handleOpen(path); // re-open this sketch + Preferences.unset(key); + } + } + } + */ + + + // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + + // Can't be called from the constructor because the path isn't set all the // way down. However, Base does not change over time. More importantly, // though, is that the functions being called in Base are somewhat suspect diff --git a/todo.txt b/todo.txt index 7205bb49c..b203e75ea 100755 --- a/todo.txt +++ b/todo.txt @@ -49,10 +49,14 @@ X this was hiding the issue that was causing contribs to be added several time X because the add() was inside the category loop of that code X remove unused 'restart' flagging code, setRestartFlag() never used X all that was left was maintenance of when that's been set, so... +X allow update of the current Mode +X if doing an update (not just delete), close sketches and re-open +X if doing a delete, require sketches to be closed + + +_ after failed update of Mode, the button is no longer available +_ have to click something else, then click back on the Mode line again -_ allow update of the current Mode -_ if doing an update (not just delete), close sketches and re-open -_ if doing a delete, require sketches to be closed _ lots of rewriting to use SwingWorker _ https://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html _ when starting in p5jsMode, going to the contrib manager shows no green checks for libs @@ -60,6 +64,8 @@ _ tricky because those aren't gonna show up in 'Add Library' _ which is another case for a separate 'contrib manager' thing _ maybe that's the update button? when no updates, it's still a button? +_ remove updateInProgress from StatusPanelDetail + _ CJKV fonts are hosed b/c they're not actually monospace _ https://github.com/processing/processing4/issues/447