diff --git a/app/src/processing/app/contrib/AvailableContribution.java b/app/src/processing/app/contrib/AvailableContribution.java index de8add146..0eb741e76 100644 --- a/app/src/processing/app/contrib/AvailableContribution.java +++ b/app/src/processing/app/contrib/AvailableContribution.java @@ -26,7 +26,6 @@ import java.util.HashMap; import java.util.List; import processing.app.Base; -import processing.app.Editor; import processing.core.PApplet; @@ -70,9 +69,12 @@ class AvailableContribution extends Contribution { * @param confirmReplace * true to open a dialog asking the user to confirm removing/moving * the library when a library by the same name already exists + * @param status + * the StatusPanel. Pass null if this function is called for an + * install-on-startup * @return */ - public LocalContribution install(Editor editor, File contribArchive, + public LocalContribution install(Base base, File contribArchive, boolean confirmReplace, StatusPanel status) { // Unzip the file into the modes, tools, or libraries folder inside the // sketchbook. Unzipping to /tmp is problematic because it may be on @@ -83,7 +85,8 @@ class AvailableContribution extends Contribution { try { tempFolder = type.createTempFolder(); } catch (IOException e) { - status.setErrorMessage("Could not create a temporary folder to install."); + if (status != null) + status.setErrorMessage("Could not create a temporary folder to install."); return null; } Base.unzip(contribArchive, tempFolder); @@ -111,7 +114,8 @@ class AvailableContribution extends Contribution { tempFolder.renameTo(contribFolder); tempFolder = enclosingFolder; */ - status.setErrorMessage(getName() + " needs to be repackaged according to the " + type.getTitle() + " guidelines."); + if (status != null) + status.setErrorMessage(getName() + " needs to be repackaged according to the " + type.getTitle() + " guidelines."); //status.setErrorMessage("This " + type + " needs to be repackaged according to the guidelines."); return null; } @@ -123,14 +127,15 @@ class AvailableContribution extends Contribution { LocalContribution installedContrib = null; if (contribFolder == null) { - status.setErrorMessage("Could not find a " + type + " in the downloaded file."); + if (status != null) + status.setErrorMessage("Could not find a " + type + " in the downloaded file."); } else { File propFile = new File(contribFolder, type + ".properties"); if (writePropertiesFile(propFile)) { // 1. contribFolder now has a legit contribution, load it to get info. LocalContribution newContrib = - type.load(editor.getBase(), contribFolder); + type.load(base, contribFolder); // 1.1. get info we need to delete the newContrib folder later File newContribFolder = newContrib.getFolder(); @@ -138,7 +143,7 @@ class AvailableContribution extends Contribution { // 2. Check to make sure nothing has the same name already, // backup old if needed, then move things into place and reload. installedContrib = - newContrib.copyAndLoad(editor, confirmReplace, status); + newContrib.copyAndLoad(base, confirmReplace, status); // Restart no longer needed. Yay! // if (newContrib != null && type.requiresRestart()) { @@ -148,12 +153,10 @@ class AvailableContribution extends Contribution { // 3.1 Unlock all the jars if it is a mode or tool if (newContrib.getType() == ContributionType.MODE) { - ((ModeContribution)newContrib).clearClassLoader(editor.getBase()); - System.out.println("ismode"); + ((ModeContribution)newContrib).clearClassLoader(base); } else if (newContrib.getType() == ContributionType.TOOL) { - ((ToolContribution)newContrib).clearClassLoader(editor.getBase()); - System.out.println("istool"); + ((ToolContribution)newContrib).clearClassLoader(base); } // 3.2 Delete the newContrib, do a garbage collection, hope and pray @@ -176,7 +179,8 @@ class AvailableContribution extends Contribution { Base.removeDir(newContribFolder); } else { - status.setErrorMessage("Error overwriting .properties file."); + if (status != null) + status.setErrorMessage("Error overwriting .properties file."); } } @@ -188,90 +192,6 @@ class AvailableContribution extends Contribution { } - /** - * @param contribArchive - * a zip file containing the library to install - * @return - */ - public LocalContribution installOnStartup(Base base, File contribArchive, StatusPanel status) { - // Unzip the file into the modes, tools, or libraries folder inside the - // sketchbook. Unzipping to /tmp is problematic because it may be on - // another file system, so move/rename operations will break. - - File tempFolder = null; - - try { - tempFolder = type.createTempFolder(); - } catch (IOException e) { - status.setErrorMessage("Could not create a temporary folder to install."); - return null; - } - Base.unzip(contribArchive, tempFolder); - - // Now go looking for a legit contrib inside what's been unpacked. - File contribFolder = null; - - // Sometimes contrib authors place all their folders in the base directory - // of the .zip file instead of in single folder as the guidelines suggest. - if (type.isCandidate(tempFolder)) { - status.setErrorMessage(getName() + " needs to be repackaged according to the " + type.getTitle() + " guidelines."); - return null; - } - - contribFolder = type.findCandidate(tempFolder); - LocalContribution installedContrib = null; - - if (contribFolder == null) { - status.setErrorMessage("Could not find a " + type + " in the downloaded file."); - - } else { - File propFile = new File(contribFolder, type + ".properties"); - if (writePropertiesFile(propFile)) { - // 1. contribFolder now has a legit contribution, load it to get info. - LocalContribution newContrib = - type.load(base, contribFolder); - - // 1.1. get info we need to delete the newContrib folder later - File newContribFolder = newContrib.getFolder(); - - // 2. Check to make sure nothing has the same name already, - // backup old if needed, then move things into place and reload. - installedContrib = - newContrib.copyAndLoadOnStartup(base, status); - - // 3. Delete the newContrib, do a garbage collection, hope and pray - // that Java will unlock the temp folder on Windows now - newContrib = null; - System.gc(); - - - if (Base.isWindows()) { - // we'll even give it 2 seconds to finish up ... because file ops are - // just that flaky on Windows. - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - // 4. Okay, now actually delete that temp folder - Base.removeDir(newContribFolder); - - } else { - status.setErrorMessage("Error overwriting .properties file."); - } - } - - // Remove any remaining ickies - if (tempFolder.exists()) { - Base.removeDir(tempFolder); - } - return installedContrib; - } - - - public boolean isInstalled() { return false; } diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index 8e6c33c8a..ec1c799cd 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -23,11 +23,7 @@ package processing.app.contrib; import java.io.*; import java.net.*; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ArrayList; +import java.util.*; import processing.app.Base; import processing.app.Editor; @@ -43,8 +39,8 @@ public class ContributionManager { /** - * Blocks until the file is downloaded or an error occurs. - * Returns true if the file was successfully downloaded, false otherwise. + * Blocks until the file is downloaded or an error occurs. Returns true if the + * file was successfully downloaded, false otherwise. * * @param source * the URL of the file to download @@ -52,6 +48,9 @@ public class ContributionManager { * the file on the local system where the file will be written. This * must be a file (not a directory), and must already exist. * @param progress + * null if progress is irrelevant, such as when downloading for an + * install during startup, when the ProgressMonitor is useless since + * UI isn't setup yet. * @throws FileNotFoundException * if an error occurred downloading the file */ @@ -67,83 +66,48 @@ public class ContributionManager { conn.setRequestMethod("GET"); conn.connect(); - // TODO this is often -1, may need to set progress to indeterminate - int fileSize = conn.getContentLength(); + if (progress != null) { + // TODO this is often -1, may need to set progress to indeterminate + int fileSize = conn.getContentLength(); // System.out.println("file size is " + fileSize); - progress.startTask(Language.text("contributions.progress.downloading"), fileSize); + progress.startTask(Language.text("contributions.progress.downloading"), fileSize); + } InputStream in = conn.getInputStream(); FileOutputStream out = new FileOutputStream(dest); byte[] b = new byte[8192]; int amount; - int total = 0; - while (!progress.isCanceled() && (amount = in.read(b)) != -1) { - out.write(b, 0, amount); - total += amount; - progress.setProgress(total); + if (progress != null) { + int total = 0; + while (!progress.isCanceled() && (amount = in.read(b)) != -1) { + out.write(b, 0, amount); + total += amount; + progress.setProgress(total); + } + } else { + while ((amount = in.read(b)) != -1) { + out.write(b, 0, amount); + } } out.flush(); out.close(); success = true; } catch (SocketTimeoutException ste) { - progress.error(ste); - + if (progress != null) + progress.error(ste); } catch (IOException ioe) { - progress.error(ioe); + if (progress != null) + progress.error(ioe); ioe.printStackTrace(); } - progress.finished(); + if (progress != null) + progress.finished(); return success; } - - /** - * Blocks until the file is downloaded or an error occurs. - * Returns true if the file was successfully downloaded, false otherwise. - * Used at startup for automatically downloading and installing. - * - * @param source - * the URL of the file to download - * @param dest - * the file on the local system where the file will be written. This - * must be a file (not a directory), and must already exist. - */ - static boolean download(URL source, File dest) { - boolean success = false; - try { -// System.out.println("downloading file " + source); -// URLConnection conn = source.openConnection(); - HttpURLConnection conn = (HttpURLConnection) source.openConnection(); - HttpURLConnection.setFollowRedirects(true); - conn.setConnectTimeout(15 * 1000); - conn.setReadTimeout(60 * 1000); - conn.setRequestMethod("GET"); - conn.connect(); - InputStream in = conn.getInputStream(); - FileOutputStream out = new FileOutputStream(dest); - - byte[] b = new byte[8192]; - int amount; - while ((amount = in.read(b)) != -1) { - out.write(b, 0, amount); - } - out.flush(); - out.close(); - success = true; - - } catch (SocketTimeoutException ste) { - // When there's no internet... - // TODO: Will have to find a way to download later - } catch (IOException ioe) { - ioe.printStackTrace(); - } - return success; - } - - /** * Non-blocking call to download and install a contribution in a new thread. * @@ -176,7 +140,7 @@ public class ContributionManager { if (!downloadProgress.isCanceled() && !downloadProgress.isError()) { installProgress.startTask("Installing...", ProgressMonitor.UNKNOWN); LocalContribution contribution = - ad.install(editor, contribZip, false, status); + ad.install(editor.getBase(), contribZip, false, status); if (contribution != null) { contribListing.replaceContribution(ad, contribution); @@ -224,38 +188,27 @@ public class ContributionManager { contribZip.setWritable(true); // necessary? try { - download(url, contribZip); + download(url, contribZip, null); - StatusPanel status = new StatusPanel(); - LocalContribution contribution = ad.installOnStartup(base, - contribZip, - status); + LocalContribution contribution = ad.install(base, contribZip, + false, null); if (contribution != null) { contribListing.replaceContribution(ad, contribution); if (contribution.getType() == ContributionType.MODE) { ArrayList contribModes = base .getModeContribs(); - if (contribModes != null) { + if (contribModes != null && !contribModes.contains(contribution)) { contribModes.add((ModeContribution) contribution); - if (contribution.getType() == ContributionType.MODE - && base.getActiveEditor() != null) { - ArrayList contribModesList = base - .getModeContribs(); - if (!contribModesList.contains(contribution)) - contribModesList.add((ModeContribution) contribution); - } } } if (base.getActiveEditor() != null) refreshInstalled(base.getActiveEditor()); } - System.out.println(status.getText()); -// if (contribution != null) { -// contribListing.replaceContribution(ad, contribution); -// } contribZip.delete(); + + handleUpdateFailedMarkers(ad, filename.substring(0, filename.lastIndexOf('.'))); } catch (Exception e) { e.printStackTrace(); @@ -272,9 +225,34 @@ public class ContributionManager { } +/** + * After install, this function checks whether everything went properly or not. + * If not, it adds a marker file so that the next time Processing is started, installPreviouslyFailed() + * can install the contribution. + * @param ac + * The contribution just installed. + * @param filename + * The name of the folder in which the contribution is supposed to be stored. + */ + static private void handleUpdateFailedMarkers(final AvailableContribution ac, String filename) { + + File contribLocn = ac.getType().getSketchbookFolder(); + + try { + new File(contribLocn, ac.getName()).createNewFile(); + } catch (IOException e) { + // File already exists... + //e.printStackTrace(); + } + + } + + static public void refreshInstalled(Editor e) { - List editor = e.getBase().getEditors(); - for (Editor ed : editor) { + + Iterator iter = e.getBase().getEditors().iterator(); + while (iter.hasNext()) { + Editor ed = iter.next(); ed.getMode().rebuildImportMenu(); ed.getMode().resetExamples(); ed.rebuildToolMenu(); @@ -340,6 +318,7 @@ public class ContributionManager { * Also updates all entries previously marked for update. */ static public void cleanup(Base base) throws Exception { + deleteTemp(Base.getSketchbookModesFolder()); deleteTemp(Base.getSketchbookToolsFolder()); @@ -347,7 +326,9 @@ public class ContributionManager { deleteFlagged(Base.getSketchbookModesFolder()); deleteFlagged(Base.getSketchbookToolsFolder()); - updateFlagged(base, Base.getSketchbookLibrariesFolder()); + installPreviouslyFailed(base, Base.getSketchbookModesFolder()); + installPreviouslyFailed(base, Base.getSketchbookToolsFolder()); + updateFlagged(base, Base.getSketchbookModesFolder()); updateFlagged(base, Base.getSketchbookToolsFolder()); @@ -356,27 +337,40 @@ public class ContributionManager { } + /** + * Deletes the icky tmp folders that were left over from installs and updates + * in the previous run of Processing. Needed to be called only on the tools + * and modes sketchbook folders. + * + * @param root + */ static private void deleteTemp(File root) { - + LinkedList deleteList = new LinkedList(); - + for (File f : root.listFiles()) if (f.getName().matches(root.getName().substring(0, 4) + "\\d*" + "tmp")) deleteList.add(f); - + Iterator folderIter = deleteList.iterator(); - - while(folderIter.hasNext()) { + + while (folderIter.hasNext()) { Base.removeDir(folderIter.next()); } } - + + /** + * Deletes all the modes/tools/libs that are flagged for removal. + * + * @param root + * @throws Exception + */ static private void deleteFlagged(File root) throws Exception { File[] markedForDeletion = root.listFiles(new FileFilter() { public boolean accept(File folder) { - return (folder.isDirectory() && - LocalContribution.isDeletionFlagged(folder)); + return (folder.isDirectory() && LocalContribution + .isDeletionFlagged(folder)); } }); for (File folder : markedForDeletion) { @@ -385,38 +379,81 @@ public class ContributionManager { } + /** + * Installs all the modes/tools whose installation failed during an + * auto-update the previous time Processing was started up. + * + * @param base + * @param root + * @throws Exception + */ + static private void installPreviouslyFailed(Base base, File root) throws Exception { + File[] installList = root.listFiles(new FileFilter() { + public boolean accept(File folder) { + return (folder.isFile()); + } + }); + + for (File file : installList) { + Iterator iter = contribListing.advertisedContributions.iterator(); + while (iter.hasNext()) { + AvailableContribution availableContrib = iter.next(); + if (file.getName().equals(availableContrib.getName())) { + installOnStartUp(base, availableContrib); + contribListing + .replaceContribution(availableContrib, availableContrib); + } + } + } + } + + + /** + * Updates all the flagged modes/tools. + * + * @param base + * @param root + * @throws Exception + */ static private void updateFlagged(Base base, File root) throws Exception { File[] markedForUpdate = root.listFiles(new FileFilter() { public boolean accept(File folder) { - return (folder.isDirectory() && - LocalContribution.isUpdateFlagged(folder)); + return (folder.isDirectory() && LocalContribution + .isUpdateFlagged(folder)); } }); - + ArrayList updateContribsNames = new ArrayList(); LinkedList updateContribsList = new LinkedList(); - + String type = root.getName().substring(root.getName().lastIndexOf('/') + 1); String propFileName = null; - + if (type.equalsIgnoreCase("tools")) propFileName = "tool.properties"; else if (type.equalsIgnoreCase("modes")) propFileName = "mode.properties"; else if (type.equalsIgnoreCase("libraries")) //putting this here, just in case propFileName = "libraries.properties"; - + for (File folder : markedForUpdate) { - HashMap properties = Base.readSettings(new File(folder, propFileName)); + HashMap properties = Base + .readSettings(new File(folder, propFileName)); updateContribsNames.add(properties.get("name")); Base.removeDir(folder); } - for (AvailableContribution availableContribs : contribListing.advertisedContributions) { - if(updateContribsNames.contains(availableContribs.getName())) { + + Iterator iter = contribListing.advertisedContributions.iterator(); + while (iter.hasNext()) { + AvailableContribution availableContribs = iter.next(); + if (updateContribsNames.contains(availableContribs.getName())) { updateContribsList.add(availableContribs); } } - for (AvailableContribution contribToUpdate : updateContribsList) { + + Iterator iter2 = updateContribsList.iterator(); + while (iter2.hasNext()) { + AvailableContribution contribToUpdate = iter2.next(); installOnStartUp(base, contribToUpdate); contribListing.replaceContribution(contribToUpdate, contribToUpdate); } diff --git a/app/src/processing/app/contrib/ContributionManagerDialog.java b/app/src/processing/app/contrib/ContributionManagerDialog.java index 46e70598a..189c0152a 100644 --- a/app/src/processing/app/contrib/ContributionManagerDialog.java +++ b/app/src/processing/app/contrib/ContributionManagerDialog.java @@ -95,7 +95,9 @@ public class ContributionManagerDialog { @Override public void actionPerformed(ActionEvent arg0) { - for (Editor ed : editor.getBase().getEditors()) + Iterator iter = editor.getBase().getEditors().iterator(); + while (iter.hasNext()) { + Editor ed = iter.next(); if (ed.getSketch().isModified() || ed.getSketch().isUntitled()) { int option = Base .showYesNoQuestion(editor, title, @@ -107,6 +109,7 @@ public class ContributionManagerDialog { else break; } + } // Thanks to http://stackoverflow.com/a/4160543 StringBuilder cmd = new StringBuilder(); diff --git a/app/src/processing/app/contrib/ContributionPanel.java b/app/src/processing/app/contrib/ContributionPanel.java index f393066f4..3fbb79c6d 100644 --- a/app/src/processing/app/contrib/ContributionPanel.java +++ b/app/src/processing/app/contrib/ContributionPanel.java @@ -26,7 +26,7 @@ import java.awt.event.*; import java.io.File; import java.net.MalformedURLException; import java.net.URL; -import java.util.ArrayList; +import java.util.Iterator; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.Date; @@ -133,9 +133,10 @@ class ContributionPanel extends JPanel { LocalContribution installed = (LocalContribution) contrib; installed.setDeletionFlag(false); contribListing.replaceContribution(contrib, contrib); // ?? - ArrayList contribsList = contribListing.allContributions; + Iterator contribsListIter = contribListing.allContributions.iterator(); boolean toBeRestarted = false; - for(Contribution contribElement : contribsList) + while (contribsListIter.hasNext()) { + Contribution contribElement = contribsListIter.next(); if (contrib.getType().equals(contribElement.getType())) { if (contribElement.isDeletionFlagged() || contribElement.isUpdateFlagged()) { @@ -143,6 +144,7 @@ class ContributionPanel extends JPanel { break; } } + } listPanel.contribManager.restartButton.setVisible(toBeRestarted); } } @@ -399,6 +401,7 @@ class ContributionPanel extends JPanel { JPanel updateBox = new JPanel(); updateBox.setLayout(new BorderLayout()); + updateBox.setInheritsPopupMenu(true); updateBox.add(notificationBlock, BorderLayout.WEST); updateBox.setBorder(new EmptyBorder(4, 7, 7, 7)); updateBox.setOpaque(false); @@ -412,26 +415,24 @@ class ContributionPanel extends JPanel { add(rightPane, BorderLayout.EAST); - if (updateButton.isVisible() && !isRemoveInProgress && !contrib.isDeletionFlagged()) {//installRemoveButton.getText().equals("Remove") && + if (updateButton.isVisible() && !isRemoveInProgress && !contrib.isDeletionFlagged()) { JPanel updateRemovePanel = new JPanel(); updateRemovePanel.setLayout(new FlowLayout()); updateRemovePanel.setOpaque(false); updateRemovePanel.add(updateButton); + updateRemovePanel.setInheritsPopupMenu(true); updateRemovePanel.add(installRemoveButton); updateBox.add(updateRemovePanel, BorderLayout.EAST); JPanel barPane = new JPanel(); barPane.setOpaque(false); + barPane.setInheritsPopupMenu(true); barPane.add(installProgressBar); rightPane.add(barPane); if (isUpdateInProgress) ((CardLayout) barButtonCardPane.getLayout()).show(barButtonCardPane, PROGRESS_BAR_CONSTRAINT); - -// barButtonCardPane.removeAll(); -// barButtonCardPane.add(barPane, PROGRESS_BAR_CONSTRAINT); -// ((CardLayout) barButtonCardPane.getLayout()).show(barButtonCardPane, PROGRESS_BAR_CONSTRAINT); -// rightPane.add(barButtonCardPane); + } else { updateBox.add(updateButton, BorderLayout.EAST); @@ -439,10 +440,12 @@ class ContributionPanel extends JPanel { JPanel barPane = new JPanel(); barPane.setOpaque(false); + barPane.setInheritsPopupMenu(true); barPane.add(installProgressBar); JPanel buttonPane = new JPanel(); buttonPane.setOpaque(false); + buttonPane.setInheritsPopupMenu(true); buttonPane.add(installRemoveButton); barButtonCardPane.add(buttonPane, BUTTON_CONSTRAINT); @@ -624,8 +627,6 @@ class ContributionPanel extends JPanel { installRemoveButton.setText(Language.text("contributions.install")); } -// reorganizePaneComponents(); - contextMenu.removeAll(); if (contrib.isInstalled()) { diff --git a/app/src/processing/app/contrib/LocalContribution.java b/app/src/processing/app/contrib/LocalContribution.java index 3a3e5528d..70dce887d 100644 --- a/app/src/processing/app/contrib/LocalContribution.java +++ b/app/src/processing/app/contrib/LocalContribution.java @@ -195,104 +195,90 @@ public abstract class LocalContribution extends Contribution { // } - LocalContribution copyAndLoad(Editor editor, + LocalContribution copyAndLoad(Base base, boolean confirmReplace, StatusPanel status) { - ArrayList oldContribs = - getType().listContributions(editor); +// NOTE: null status => function is called on startup when Editor objects, et al. aren't ready String contribFolderName = getFolder().getName(); File contribTypeFolder = getType().getSketchbookFolder(); File contribFolder = new File(contribTypeFolder, contribFolderName); + + if (status != null) { // when status != null, install is not occurring on startup + + Editor editor = base.getActiveEditor(); + + ArrayList oldContribs = + getType().listContributions(editor); + + // In case an update marker exists, and the user wants to install, delete the update marker + if (contribFolder.exists() && !contribFolder.isDirectory()) { + contribFolder.delete(); + contribFolder = new File(contribTypeFolder, contribFolderName); + } - for (LocalContribution oldContrib : oldContribs) { - if ((oldContrib.getFolder().exists() && oldContrib.getFolder().equals(contribFolder)) || - (oldContrib.getId() != null && oldContrib.getId().equals(getId()))) { + for (LocalContribution oldContrib : oldContribs) { + if ((oldContrib.getFolder().exists() && oldContrib.getFolder().equals(contribFolder)) || + (oldContrib.getId() != null && oldContrib.getId().equals(getId()))) { - if (oldContrib.getType().requiresRestart()) { - // XXX: We can't replace stuff, soooooo.... do something different - if (!oldContrib.backup(editor, false, status)) { - return null; - } - } else { - int result = 0; - boolean doBackup = Preferences.getBoolean("contribution.backup.on_install"); - if (confirmReplace) { - if (doBackup) { - result = Base.showYesNoQuestion(editor, "Replace", - "Replace pre-existing \"" + oldContrib.getName() + "\" library?", - "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ - "has been found in your sketchbook. Clicking “Yes”
"+ - "will move the existing library to a backup folder
" + - "in libraries/old before replacing it."); - if (result != JOptionPane.YES_OPTION || !oldContrib.backup(editor, true, status)) { - return null; - } - } else { - result = Base.showYesNoQuestion(editor, "Replace", - "Replace pre-existing \"" + oldContrib.getName() + "\" library?", - "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ - "has been found in your sketchbook. Clicking “Yes”
"+ - "will permanently delete this library and all of its contents
"+ - "before replacing it."); - if (result != JOptionPane.YES_OPTION || !oldContrib.getFolder().delete()) { - return null; - } + if (oldContrib.getType().requiresRestart()) { + // XXX: We can't replace stuff, soooooo.... do something different + if (!oldContrib.backup(editor, false, status)) { + return null; } } else { - if ((doBackup && !oldContrib.backup(editor, true, status)) || - (!doBackup && !oldContrib.getFolder().delete())) { - return null; + int result = 0; + boolean doBackup = Preferences.getBoolean("contribution.backup.on_install"); + if (confirmReplace) { + if (doBackup) { + result = Base.showYesNoQuestion(editor, "Replace", + "Replace pre-existing \"" + oldContrib.getName() + "\" library?", + "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ + "has been found in your sketchbook. Clicking “Yes”
"+ + "will move the existing library to a backup folder
" + + "in libraries/old before replacing it."); + if (result != JOptionPane.YES_OPTION || !oldContrib.backup(editor, true, status)) { + return null; + } + } else { + result = Base.showYesNoQuestion(editor, "Replace", + "Replace pre-existing \"" + oldContrib.getName() + "\" library?", + "A pre-existing copy of the \"" + oldContrib.getName() + "\" library
"+ + "has been found in your sketchbook. Clicking “Yes”
"+ + "will permanently delete this library and all of its contents
"+ + "before replacing it."); + if (result != JOptionPane.YES_OPTION || !oldContrib.getFolder().delete()) { + return null; + } + } + } else { + if ((doBackup && !oldContrib.backup(editor, true, status)) || + (!doBackup && !oldContrib.getFolder().delete())) { + return null; + } } } } } - } - // At this point it should be safe to replace this fella - if (contribFolder.exists()) { - Base.removeDir(contribFolder); + // At this point it should be safe to replace this fella + if (contribFolder.exists()) { + Base.removeDir(contribFolder); + } + + } + else { + // This if should ideally never happen, since this function is to be called only when restarting on update + if (contribFolder.exists() && contribFolder.isDirectory()) { + Base.removeDir(contribFolder); + } + else if (contribFolder.exists()) { + contribFolder.delete(); + contribFolder = new File(contribTypeFolder, contribFolderName); + } } - - File oldFolder = getFolder(); - - try { - Base.copyDir(oldFolder, contribFolder); - } catch (IOException e) { - status.setErrorMessage("Could not copy " + getTypeName() + - " \"" + getName() + "\" to the sketchbook."); - e.printStackTrace(); - return null; - } - - - /* - if (!getFolder().renameTo(contribFolder)) { - status.setErrorMessage("Could not move " + getTypeName() + - " \"" + getName() + "\" to the sketchbook."); - return null; - } - */ - - return getType().load(editor.getBase(), contribFolder); - } - - - LocalContribution copyAndLoadOnStartup(Base base, StatusPanel status) { - - String contribFolderName = getFolder().getName(); - - File contribTypeFolder = getType().getSketchbookFolder(); - File contribFolder = new File(contribTypeFolder, contribFolderName); - - // This if should ideally never happen, since this function is to be called only when restarting on update - if (contribFolder.exists()) { - Base.removeDir(contribFolder); - } - - File oldFolder = getFolder(); try { @@ -317,7 +303,6 @@ public abstract class LocalContribution extends Contribution { } - /** * Moves the given contribution to a backup folder. * @param deleteOriginal @@ -367,8 +352,11 @@ public abstract class LocalContribution extends Contribution { }).start(); } - void remove(final Editor editor, final ProgressMonitor pm, - final StatusPanel status, final ContributionListing contribListing) { + + void remove(final Editor editor, + final ProgressMonitor pm, + final StatusPanel status, + final ContributionListing contribListing) { pm.startTask("Removing", ProgressMonitor.UNKNOWN); boolean doBackup = Preferences.getBoolean("contribution.backup.on_remove"); @@ -383,11 +371,14 @@ public abstract class LocalContribution extends Contribution { if (getType() == ContributionType.MODE) { boolean isModeActive = false; ModeContribution m = (ModeContribution) this; - for (Editor e : editor.getBase().getEditors()) + Iterator iter = editor.getBase().getEditors().iterator(); + while (iter.hasNext()) { + Editor e = iter.next(); if (e.getMode().equals(m.getMode())) { isModeActive = true; break; } + } if (!isModeActive) m.clearClassLoader(editor.getBase()); else { @@ -401,8 +392,11 @@ public abstract class LocalContribution extends Contribution { } if (getType() == ContributionType.TOOL) { ToolContribution t = (ToolContribution) this; - for (Editor ed : editor.getBase().getEditors()) + Iterator iter = editor.getBase().getEditors().iterator(); + while (iter.hasNext()) { + Editor ed = iter.next(); ed.clearToolMenu(); + } t.clearClassLoader(editor.getBase()); } if (doBackup) { diff --git a/app/src/processing/app/contrib/ModeContribution.java b/app/src/processing/app/contrib/ModeContribution.java index 7312b5309..d80c074a0 100644 --- a/app/src/processing/app/contrib/ModeContribution.java +++ b/app/src/processing/app/contrib/ModeContribution.java @@ -21,17 +21,13 @@ */ package processing.app.contrib; -import java.awt.Component; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.URLClassLoader; import java.util.*; -import javax.swing.JRadioButtonMenuItem; - import processing.app.Base; -import processing.app.Editor; import processing.app.Mode; @@ -77,6 +73,7 @@ public class ModeContribution extends LocalContribution { private ModeContribution(Base base, File folder, String className) throws Exception { super(folder); + className = initLoader(className); if (className != null) { Class modeClass = loader.loadClass(className); @@ -90,26 +87,16 @@ public class ModeContribution extends LocalContribution { } /** - * Method to close the ClassLoader so that the archives are no longer "locked" and - * a mode can be removed without restart. + * Method to close the ClassLoader so that the archives are no longer "locked" + * and a mode can be removed without restart. */ public void clearClassLoader(Base base) { + ArrayList contribModes = base.getModeContribs(); int botherToRemove = contribModes.indexOf(this); if (botherToRemove != -1) { // The poor thing isn't even loaded, and we're trying to remove it... contribModes.remove(botherToRemove); - /* List editorList = base.getEditors(); - for (Editor editor : editorList) { - Component[] j = editor.getModeMenu().getPopupMenu().getComponents(); - for (Component component : j) { - JRadioButtonMenuItem cbmi = null; - if (component instanceof JRadioButtonMenuItem) { - cbmi = (JRadioButtonMenuItem) component; - if (cbmi.getText().equals(mode.toString())) - editor.getModeMenu().getPopupMenu().remove(component); - } - } - } */ + try { ((URLClassLoader) loader).close(); // The typecast should be safe, since the only case when loader is not of diff --git a/app/src/processing/app/contrib/ToolContribution.java b/app/src/processing/app/contrib/ToolContribution.java index 85befb838..2769bf820 100644 --- a/app/src/processing/app/contrib/ToolContribution.java +++ b/app/src/processing/app/contrib/ToolContribution.java @@ -76,8 +76,9 @@ public class ToolContribution extends LocalContribution implements Tool { } catch (IOException e1) { e1.printStackTrace(); } - List editors = base.getEditors(); - for (Editor editor : editors) { + Iterator editorIter = base.getEditors().iterator(); + while (editorIter.hasNext()) { + Editor editor = editorIter.next(); ArrayList contribTools = editor.contribTools; for (ToolContribution toolContrib : contribTools) if (toolContrib.getName().equals(this.name)) {