diff --git a/app/src/processing/app/contrib/ContribProgress.java b/app/src/processing/app/contrib/ContribProgress.java index adb3e0396..d2e5b1fe0 100644 --- a/app/src/processing/app/contrib/ContribProgress.java +++ b/app/src/processing/app/contrib/ContribProgress.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2013-22 The Processing Foundation + Copyright (c) 2013-23 The Processing Foundation Copyright (c) 2011-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify @@ -109,8 +109,8 @@ public class ContribProgress { public void cancelAction() { } - public boolean isCanceled() { - return canceled; + public boolean notCanceled() { + return !canceled; } diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index f0ad7656e..419e42059 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -140,20 +140,20 @@ public class ContributionListing { } - protected void replaceContribution(Contribution oldLib, Contribution newLib) { - if (oldLib != null && newLib != null) { - if (oldLib.getImports() != null) { - for (String importName : oldLib.getImports()) { + protected void replaceContribution(Contribution oldContrib, Contribution newContrib) { + if (oldContrib != null && newContrib != null) { + if (oldContrib.getImports() != null) { + for (String importName : oldContrib.getImports()) { if (getLibrariesByImportHeader().containsKey(importName)) { - getLibrariesByImportHeader().put(importName, newLib); + getLibrariesByImportHeader().put(importName, newContrib); } } } - allContributions.remove(oldLib); - allContributions.add(newLib); + allContributions.remove(oldContrib); + allContributions.add(newContrib); for (ListPanel listener : listPanels) { - listener.contributionChanged(oldLib, newLib); + listener.contributionChanged(oldContrib, newContrib); } } } @@ -227,7 +227,7 @@ public class ContributionListing { } ContributionManager.download(url, makeContribsBlob(base), tempContribFile, progress); - if (!progress.isCanceled() && !progress.isException()) { + if (progress.notCanceled() && !progress.isException()) { if (listingFile.exists()) { listingFile.delete(); // may silently fail, but below may still work } diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index 6ab13a285..9257070c4 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -107,7 +107,7 @@ public class ContributionManager { int amount; if (progress != null) { int total = 0; - while (!progress.isCanceled() && (amount = in.read(b)) != -1) { + while (progress.notCanceled() && (amount = in.read(b)) != -1) { out.write(b, 0, amount); total += amount; progress.setProgress(total); @@ -154,7 +154,7 @@ public class ContributionManager { try { download(url, null, contribZip, downloadProgress); - if (!downloadProgress.isCanceled() && !downloadProgress.isException()) { + if (downloadProgress.notCanceled() && !downloadProgress.isException()) { installProgress.startTask(Language.text("contrib.progress.installing")); final LocalContribution contribution = ad.install(base, contribZip, false, status); diff --git a/app/src/processing/app/contrib/ContributionTab.java b/app/src/processing/app/contrib/ContributionTab.java index 56caf7f51..9f409dc61 100644 --- a/app/src/processing/app/contrib/ContributionTab.java +++ b/app/src/processing/app/contrib/ContributionTab.java @@ -324,6 +324,11 @@ public class ContributionTab extends JPanel { } + protected boolean filterHasFocus() { + return filterField != null && filterField.hasFocus(); + } + + /* // TODO Why is this entire set of code only running when Editor // is not null... And what's it doing anyway? Shouldn't it run @@ -369,13 +374,13 @@ public class ContributionTab extends JPanel { */ - public void updateStatusDetail(StatusDetail detail) { - statusPanel.updateDetail(detail); + protected StatusDetail createStatusDetail() { + return new StatusDetail(base, statusPanel); } - public boolean filterHasFocus() { - return filterField != null && filterField.hasFocus(); + protected void updateStatusDetail(StatusDetail detail) { + statusPanel.updateDetail(detail); } diff --git a/app/src/processing/app/contrib/ListPanel.java b/app/src/processing/app/contrib/ListPanel.java index 476d57536..e7dd0fbc8 100644 --- a/app/src/processing/app/contrib/ListPanel.java +++ b/app/src/processing/app/contrib/ListPanel.java @@ -22,6 +22,7 @@ package processing.app.contrib; import java.awt.*; +import java.awt.image.BufferedImage; import java.util.List; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -56,6 +57,7 @@ public class ListPanel extends JPanel implements Scrollable { protected ContributionTableModel model; // state icons appearing to the left side of the list + static final int ICON_SIZE = 16; Icon upToDateIcon; Icon updateAvailableIcon; Icon incompatibleIcon; @@ -197,17 +199,30 @@ public class ListPanel extends JPanel implements Scrollable { rowColor = Theme.getColor("manager.list.background.color"); table.setBackground(rowColor); - foundationIcon = Toolkit.renderIcon("manager/foundation", Theme.get("manager.list.foundation.color"), 16); + foundationIcon = Toolkit.renderIcon("manager/foundation", Theme.get("manager.list.foundation.color"), ICON_SIZE); - upToDateIcon = Toolkit.renderIcon("manager/list-up-to-date", Theme.get("manager.list.icon.color"), 16); - updateAvailableIcon = Toolkit.renderIcon("manager/list-update-available", Theme.get("manager.list.icon.color"), 16); - incompatibleIcon = Toolkit.renderIcon("manager/list-incompatible", Theme.get("manager.list.icon.color"), 16); - downloadingIcon = Toolkit.renderIcon("manager/list-downloading", Theme.get("manager.list.icon.color"), 16); + upToDateIcon = Toolkit.renderIcon("manager/list-up-to-date", Theme.get("manager.list.icon.color"), ICON_SIZE); + updateAvailableIcon = Toolkit.renderIcon("manager/list-update-available", Theme.get("manager.list.icon.color"), ICON_SIZE); + incompatibleIcon = Toolkit.renderIcon("manager/list-incompatible", Theme.get("manager.list.icon.color"), ICON_SIZE); + downloadingIcon = Toolkit.renderIcon("manager/list-downloading", Theme.get("manager.list.icon.color"), ICON_SIZE); ((PdeScrollBarUI) scrollPane.getVerticalScrollBar().getUI()).updateTheme(); } + Icon renderProgressIcon(float amount) { + final int scale = Toolkit.highResImages() ? 2 : 1; + final int dim = ICON_SIZE * scale; + Image image = new BufferedImage(dim, dim, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = (Graphics2D) image.getGraphics(); + g2.scale(scale, scale); + g2.setColor(Color.ORANGE); + g2.fillRect(0, 0, (int) (amount * ICON_SIZE), ICON_SIZE); + g2.dispose(); + return Toolkit.wrapIcon(image); + } + + // TODO remove this, yuck [fry 220313] protected int getScrollBarWidth() { return scrollPane.getVerticalScrollBar().getPreferredSize().width; @@ -304,9 +319,9 @@ public class ListPanel extends JPanel implements Scrollable { if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) { switch (sortKey.getSortOrder()) { case ASCENDING: - return " \u2193"; + return " ↓"; case DESCENDING: - return " \u2191"; + return " ↑"; } } // if not sorting on this column @@ -389,6 +404,8 @@ public class ListPanel extends JPanel implements Scrollable { if (detail != null && (detail.updateInProgress || detail.installInProgress)) { // Display "loading" icon if download/install in progress icon = downloadingIcon; +// float amount = detail.getProgressAmount(); +// icon = (amount == -1) ? downloadingIcon : renderProgressIcon(amount); } else if (contribution.isInstalled()) { if (!contribution.isCompatible(Base.getRevision())) { icon = incompatibleIcon; @@ -642,8 +659,7 @@ public class ListPanel extends JPanel implements Scrollable { // new Exception().printStackTrace(System.out); // long t1 = System.currentTimeMillis(); //StatusPanelDetail newPanel = new StatusPanelDetail(this); - StatusDetail newPanel = - new StatusDetail(contributionTab.base, contributionTab.statusPanel); + StatusDetail newPanel = contributionTab.createStatusDetail(); detailForContrib.put(contribution, newPanel); newPanel.setContrib(contribution); // add(newPanel); diff --git a/app/src/processing/app/contrib/StatusDetail.java b/app/src/processing/app/contrib/StatusDetail.java index d4e47810b..1566be959 100644 --- a/app/src/processing/app/contrib/StatusDetail.java +++ b/app/src/processing/app/contrib/StatusDetail.java @@ -30,6 +30,7 @@ import javax.swing.JProgressBar; import processing.app.*; import processing.app.laf.PdeProgressBarUI; +import processing.app.ui.Toolkit; /** @@ -78,11 +79,11 @@ class StatusDetail { } - private void installContribution(AvailableContribution info) { - if (info.link == null) { - statusPanel.setErrorMessage(Language.interpolate("contrib.unsupported_operating_system", info.getType())); + protected float getProgressAmount() { + if (progressBar.isIndeterminate()) { + return -1; } else { - installContribution(info, info.link); + return (float) progressBar.getValue() / progressBar.getMaximum(); } } @@ -141,12 +142,20 @@ class StatusDetail { protected void install() { - //clearStatusMessage(); statusPanel.clearMessage(); installInProgress = true; - if (contrib instanceof AvailableContribution) { - installContribution((AvailableContribution) contrib); - ContributionListing.getInstance().replaceContribution(contrib, contrib); + if (contrib instanceof AvailableContribution info) { + if (info.link == null) { + statusPanel.setErrorMessage(Language.interpolate("contrib.missing_link", info.getType())); + } else { + installContribution(info, info.link); + // NOTE As of 4.1.1 this was being called even if the error message + // above was getting called. Probably harmless, especially since + // the error may never happen, but still… weird. [fry 230114] + // TODO More importantly, why is this being called? Seems like this + // should be doing an actual replacement. [fry 230114] + ContributionListing.getInstance().replaceContribution(contrib, contrib); + } } } @@ -155,7 +164,6 @@ class StatusDetail { // of all things, calls install() in its finishedAction() method. // FFS this is gross. [fry 220311] protected void update() { - //clearStatusMessage(); statusPanel.clearMessage(); updateInProgress = true; @@ -163,19 +171,8 @@ class StatusDetail { // 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. - // TODO Not a good workaround [fry 220312] - // TODO This is really, really gross [fry 221104] - if (progressBar == null) { - // This was removed in 4.x and brought back for 4.0.2 because - // it broke the "Update All" option in the Contributions Manager. - // https://github.com/processing/processing4/issues/567 - progressBar = new JProgressBar(); - } else { - progressBar.setVisible(true); - progressBar.setIndeterminate(true); - } + progressBar.setVisible(true); + progressBar.setIndeterminate(true); ContribProgress progress = new ContribProgress(progressBar) { @Override diff --git a/app/src/processing/app/contrib/StatusPanel.java b/app/src/processing/app/contrib/StatusPanel.java index bfc07ad2d..d08696942 100644 --- a/app/src/processing/app/contrib/StatusPanel.java +++ b/app/src/processing/app/contrib/StatusPanel.java @@ -118,27 +118,7 @@ class StatusPanel extends JPanel { updateDetail(currentDetail); }); - progressBar = new JProgressBar(); - /* - progressBar = new JProgressBar() { - @Override - public void setBackground(Color c) { - new Exception("setting bg to " + c).printStackTrace(System.out); - super.setBackground(c); - } - }; - */ - progressBar.setStringPainted(true); - progressBar.setAlignmentX(Component.CENTER_ALIGNMENT); - //progressBar.setOpaque(true); - - resetProgressBar(); - - final int high = progressBar.getPreferredSize().height; - Dimension dim = new Dimension(BUTTON_WIDTH, high); - progressBar.setPreferredSize(dim); - progressBar.setMaximumSize(dim); - progressBar.setMinimumSize(dim); + buildProgressBar(); updateLabel = new JLabel(" "); // updateLabel.setFont(buttonFont); @@ -209,8 +189,6 @@ class StatusPanel extends JPanel { layout.linkSize(SwingConstants.HORIZONTAL, installButton, progressBar, updateButton, removeButton); - progressBar.setVisible(false); - installButton.setEnabled(false); updateButton.setEnabled(false); removeButton.setEnabled(false); @@ -223,6 +201,22 @@ class StatusPanel extends JPanel { } + protected void buildProgressBar() { + progressBar = new JProgressBar(); + progressBar.setStringPainted(true); + progressBar.setAlignmentX(Component.CENTER_ALIGNMENT); + //progressBar.setOpaque(true); + + resetProgressBar(); + + final int high = progressBar.getPreferredSize().height; + Dimension dim = new Dimension(BUTTON_WIDTH, high); + progressBar.setPreferredSize(dim); + progressBar.setMaximumSize(dim); + progressBar.setMinimumSize(dim); + } + + protected void resetProgressBar() { // TODO is this overkill for a reset? is this really only being used // when we mean to call setVisible(false)? [fry 220311] diff --git a/app/src/processing/app/contrib/UpdateContributionTab.java b/app/src/processing/app/contrib/UpdateContributionTab.java index ab19d0ae9..b92329c8c 100644 --- a/app/src/processing/app/contrib/UpdateContributionTab.java +++ b/app/src/processing/app/contrib/UpdateContributionTab.java @@ -28,8 +28,8 @@ public class UpdateContributionTab extends ContributionTab { public UpdateContributionTab(ManagerFrame dialog) { super(dialog); - // Filter to show only the contributions that have updates - // or are fake section header "contributions". + // Filter to show only the contributions with updates available, + // or are section headers (which are fake contributions). filter = contrib -> { if (contrib instanceof ListPanel.SectionHeaderContribution) { return true; @@ -86,10 +86,4 @@ public class UpdateContributionTab extends ContributionTab { //setBackground(Color.WHITE); } - - - @Override - public void updateStatusDetail(StatusDetail detail) { - // Do nothing - } } diff --git a/app/src/processing/app/contrib/UpdateStatusPanel.java b/app/src/processing/app/contrib/UpdateStatusPanel.java index f22ff5c98..509ab49f8 100644 --- a/app/src/processing/app/contrib/UpdateStatusPanel.java +++ b/app/src/processing/app/contrib/UpdateStatusPanel.java @@ -66,4 +66,10 @@ public class UpdateStatusPanel extends StatusPanel { protected void setUpdateEnabled(boolean updateEnabled) { updateButton.setEnabled(updateEnabled); } + + + @Override + protected void updateDetail(StatusDetail detail) { + detail.setProgressBar(progressBar); + } } \ No newline at end of file diff --git a/build/shared/lib/languages/PDE.properties b/build/shared/lib/languages/PDE.properties index c93dfa68e..fb2042f22 100644 --- a/build/shared/lib/languages/PDE.properties +++ b/build/shared/lib/languages/PDE.properties @@ -562,7 +562,7 @@ contrib.progress.installing = Installing contrib.progress.starting = Starting contrib.progress.downloading = Downloading contrib.download_error = An error occured while downloading the contribution. -contrib.unsupported_operating_system = Your operating system does not appear to be supported. You should visit the %s\’s library for more info. +contrib.missing_link = The download link for this %s is missing, please contact the author. contrib.category.3d = 3D contrib.category.animation = Animation contrib.category.data = Data diff --git a/todo.txt b/todo.txt index 4b77bde82..995bfeb5b 100755 --- a/todo.txt +++ b/todo.txt @@ -67,7 +67,15 @@ X should be using Base.getInstalledContribs() instead of rewriting its own X ManagerFrame.downloadAndUpdateContributionListing() X should not even be run, because the contribs load on startup X but ContributionTab.tryAgainButton needs to be able to request re-download +X removed old workaround for NPE +X https://github.com/processing/processing/issues/3667 +_ contrib categories are broken +_ showing 'all' on all tabs, not showing categories at all for libs + +_ updateDetail() call in StatusPanel.updateTheme() +_ move to ListPanel.updateTheme() (why wasn't it there?) +_ ugh, it's called twice, right nearby each other; yikes _ rip out JProgressBar from StatusPanel/StatusDetail _ show progress in the list instead