From 8df4ba3871317212db16c7a00317d13415cda5b1 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 19 Dec 2018 05:19:29 +0400 Subject: [PATCH] UI: added images re-download mode and improved dialog UX; --- .../client/dialog/DownloadImagesDialog.form | 50 +++++- .../client/dialog/DownloadImagesDialog.java | 56 ++++++- .../card/images/DownloadPicturesService.java | 158 +++++++++++------- 3 files changed, 189 insertions(+), 75 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.form index da0aaa4ea08..a4d0a72b564 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.form @@ -128,7 +128,7 @@ - + @@ -349,22 +349,56 @@ - + + - + - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java index 130a5799fa4..1f9b55f8829 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/DownloadImagesDialog.java @@ -8,6 +8,8 @@ import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.Map; /** * @author JayDi85 @@ -19,6 +21,7 @@ public class DownloadImagesDialog extends MageDialog { private Dimension sizeModeMessageOnly; private Dimension sizeModeMessageAndControls; + private Map actionsControlStates = new HashMap<>(); /** @@ -28,6 +31,9 @@ public class DownloadImagesDialog extends MageDialog { initComponents(); this.setModal(true); + // fix for panelInfo (it's resets aligmentX after netbeans designer opened) + panelInfo.setAlignmentX(CENTER_ALIGNMENT); + // save default sizes // this.sizeModeMessageAndControls = new Dimension(580, 330); // dialog -> properties -> designer size @@ -105,11 +111,36 @@ public class DownloadImagesDialog extends MageDialog { return this.progress; } + public JCheckBox getRedownloadCheckbox() { + return this.checkboxRedownload; + } + public void showLanguagesSupport(boolean haveSupport) { labelLanguage.setEnabled(haveSupport); comboLanguage.setEnabled(haveSupport); } + private void enableActionControl(boolean enable, Component comp) { + if (enable) { + // restore last enable state + comp.setEnabled(actionsControlStates.getOrDefault(comp, true)); + } else { + // save enable state and disable it + actionsControlStates.putIfAbsent(comp, comp.isEnabled()); + comp.setEnabled(false); + } + } + + public void enableActionControls(boolean enable) { + // restrict user actions while downloading/processing (all buttons, comboboxes and edits) + enableActionControl(enable, tabsList); + enableActionControl(enable, comboSource); + enableActionControl(enable, comboSets); + enableActionControl(enable, buttonSearchSet); + enableActionControl(enable, comboLanguage); + enableActionControl(enable, checkboxRedownload); + } + private void setTabTitle(int tabIndex, String title, String iconResourceName) { // tab caption with left sided icon // https://stackoverflow.com/questions/1782224/jtabbedpane-icon-on-left-side-of-tabs @@ -186,7 +217,9 @@ public class DownloadImagesDialog extends MageDialog { comboSets = new javax.swing.JComboBox<>(); fillerMode1 = new javax.swing.Box.Filler(new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 32767)); buttonSearchSet = new javax.swing.JButton(); - fillerMode = new javax.swing.Box.Filler(new java.awt.Dimension(130, 0), new java.awt.Dimension(130, 0), new java.awt.Dimension(130, 32767)); + panelRedownload = new javax.swing.JPanel(); + checkboxRedownload = new javax.swing.JCheckBox(); + filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 5), new java.awt.Dimension(0, 3), new java.awt.Dimension(32767, 5)); fillerMain1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 10), new java.awt.Dimension(0, 10), new java.awt.Dimension(32767, 10)); panelProgress = new javax.swing.JPanel(); fillerProgress1 = new javax.swing.Box.Filler(new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 32767)); @@ -216,10 +249,11 @@ public class DownloadImagesDialog extends MageDialog { tabMain.setLayout(new javax.swing.BoxLayout(tabMain, javax.swing.BoxLayout.Y_AXIS)); + panelInfo.setAlignmentX(0.5F); panelInfo.setLayout(new javax.swing.BoxLayout(panelInfo, javax.swing.BoxLayout.Y_AXIS)); panelInfo.add(fillerInfo1); - labelInfo.setText("Missing: 12345 card images / 789 token images"); + labelInfo.setText("Missing stats: 12345 card images / 789 token images"); labelInfo.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 7, 0, 0)); panelInfo.add(labelInfo); panelInfo.add(fillerInfo2); @@ -297,7 +331,19 @@ public class DownloadImagesDialog extends MageDialog { panelModeInner.add(panelModeSelect); panelMode.add(panelModeInner); - panelMode.add(fillerMode); + + panelRedownload.setAlignmentX(0.0F); + panelRedownload.setMaximumSize(new java.awt.Dimension(130, 32767)); + panelRedownload.setMinimumSize(new java.awt.Dimension(130, 30)); + panelRedownload.setPreferredSize(new java.awt.Dimension(130, 100)); + panelRedownload.setLayout(new java.awt.BorderLayout()); + + checkboxRedownload.setText("Re-download selected images"); + checkboxRedownload.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM); + panelRedownload.add(checkboxRedownload, java.awt.BorderLayout.CENTER); + panelRedownload.add(filler1, java.awt.BorderLayout.PAGE_END); + + panelMode.add(panelRedownload); tabMain.add(panelMode); tabMain.add(fillerMain1); @@ -363,15 +409,16 @@ public class DownloadImagesDialog extends MageDialog { private javax.swing.JButton buttonCancel; private javax.swing.JButton buttonOK; private javax.swing.JButton buttonSearchSet; + private javax.swing.JCheckBox checkboxRedownload; private javax.swing.JComboBox comboLanguage; private javax.swing.JComboBox comboSets; private javax.swing.JComboBox comboSource; + private javax.swing.Box.Filler filler1; private javax.swing.Box.Filler fillerGlobal1; private javax.swing.Box.Filler fillerInfo1; private javax.swing.Box.Filler fillerInfo2; private javax.swing.Box.Filler fillerMain1; private javax.swing.Box.Filler fillerMain2; - private javax.swing.Box.Filler fillerMode; private javax.swing.Box.Filler fillerMode1; private javax.swing.Box.Filler fillerProgress1; private javax.swing.Box.Filler fillerProgress2; @@ -388,6 +435,7 @@ public class DownloadImagesDialog extends MageDialog { private javax.swing.JPanel panelModeInner; private javax.swing.JPanel panelModeSelect; private javax.swing.JPanel panelProgress; + private javax.swing.JPanel panelRedownload; private javax.swing.JPanel panelSource; private javax.swing.JPanel panelSourceLeft; private javax.swing.JPanel panelSourceRight; diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java index 47b669ef2a6..a173e1845e0 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java @@ -22,10 +22,12 @@ import org.mage.plugins.card.properties.SettingsManager; import org.mage.plugins.card.utils.CardImageUtils; import javax.swing.*; +import java.awt.*; import java.awt.event.ItemEvent; import java.io.*; import java.net.*; import java.nio.file.AccessDeniedException; +import java.util.List; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -34,6 +36,9 @@ import java.util.stream.Collectors; import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; +/** + * @author JayDi85 + */ public class DownloadPicturesService extends DefaultBoundedRangeModel implements Runnable { // don't forget to remove new sets from ignore.urls to download (properties file in resources) @@ -49,8 +54,9 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements private boolean needCancel; private int cardIndex; + private List cardsAll; private List cardsMissing; - private List cardsToDownload; + private List cardsDownloadQueue; private int missingCardsCount = 0; private int missingTokensCount = 0; @@ -120,7 +126,9 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements public DownloadPicturesService(JFrame frame) { // init service and dialog - cardsToDownload = new ArrayList<>(); + cardsAll = Collections.synchronizedList(new ArrayList<>()); + cardsMissing = Collections.synchronizedList(new ArrayList<>()); + cardsDownloadQueue = Collections.synchronizedList(new ArrayList<>()); uiDialog = new DownloadImagesDialog(); // MESSAGE @@ -136,11 +144,16 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } }); - // TODO: LANGUAGES + // LANGUAGES uiDialog.getLaunguagesCombo().setModel(new DefaultComboBoxModel(CardLanguage.values())); uiDialog.getLaunguagesCombo().setSelectedItem(PreferencesDialog.getPrefImagesLanguage()); reloadLanguagesForSelectedSource(); + // REDOWNLOAD + uiDialog.getRedownloadCheckbox().setSelected(false); + uiDialog.getRedownloadCheckbox().addItemListener(this::checkboxRedowloadChanged); + + // SETS (fills after source and language select) //uiDialog.getSetsCombo().setModel(new DefaultComboBoxModel(DownloadSources.values())); uiDialog.getSetsCombo().addItemListener((ItemEvent event) -> { @@ -159,8 +172,9 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } // run - new Thread(DownloadPicturesService.this).start(); + uiDialog.enableActionControls(false); uiDialog.getStartButton().setEnabled(false); + new Thread(DownloadPicturesService.this).start(); }); // BUTTON CANCEL @@ -172,14 +186,19 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements uiDialog.showDownloadControls(false); } - public void setAllMissingCards() { + public void findMissingCards() { + updateAndViewMessage("Loading..."); + this.cardsAll.clear(); + this.cardsMissing.clear(); + this.cardsDownloadQueue.clear(); + updateAndViewMessage("Loading cards list..."); - List cards = CardRepository.instance.findCards(new CardCriteria()); + this.cardsAll = Collections.synchronizedList(CardRepository.instance.findCards(new CardCriteria())); - updateAndViewMessage("Find missing images..."); - this.cardsMissing = getNeededCards(cards); + updateAndViewMessage("Finding missing images..."); + this.cardsMissing = prepareMissingCards(this.cardsAll, uiDialog.getRedownloadCheckbox().isSelected()); - updateAndViewMessage("Find available sets from selected source..."); + updateAndViewMessage("Finding available sets from selected source..."); this.uiDialog.getSetsCombo().setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource())); reloadCardsToDownload(this.uiDialog.getSetsCombo().getSelectedItem().toString()); @@ -304,14 +323,14 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } // find missing cards to download - cardsToDownload.clear(); + cardsDownloadQueue.clear(); int numberTokenImagesAvailable = 0; int numberCardImagesAvailable = 0; for (CardDownloadData data : cardsMissing) { if (data.isToken()) { if (selectedSource.isTokenSource() && selectedSource.isImageProvided(data.getSet(), data.getName())) { numberTokenImagesAvailable++; - cardsToDownload.add(data); + cardsDownloadQueue.add(data); } else { //logger.warn("Source do not support token (set " + data.getSet() + ", token " + data.getName() + ")"); } @@ -319,7 +338,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements if (selectedSets != null && selectedSets.contains(data.getSet())) { if (selectedSource.isSetSupportedComplete(data.getSet()) || selectedSource.isImageProvided(data.getSet(), data.getName())) { numberCardImagesAvailable++; - cardsToDownload.add(data); + cardsDownloadQueue.add(data); } } } @@ -332,6 +351,17 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements reloadCardsToDownload(event.getItem().toString()); } + private void checkboxRedowloadChanged(ItemEvent event) { + MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR)); + try { + this.cardsMissing.clear(); + this.cardsMissing = prepareMissingCards(this.cardsAll, uiDialog.getRedownloadCheckbox().isSelected()); + reloadCardsToDownload(uiDialog.getSetsCombo().getSelectedItem().toString()); + } finally { + MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + } + } + private void updateProgressText(int cardCount, int tokenCount) { missingTokensCount = 0; for (CardDownloadData card : cardsMissing) { @@ -340,6 +370,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } } missingCardsCount = cardsMissing.size() - missingTokensCount; + uiDialog.setCurrentInfo("Missing: " + missingCardsCount + " card images / " + missingTokensCount + " token images"); int imageSum = cardCount + tokenCount; float mb = (imageSum * selectedSource.getAverageSize()) / 1024; @@ -356,16 +387,10 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements return className.substring(className.lastIndexOf('.') + 1); } - private static List getNeededCards(List allCards) { - - /** - * read all card names and urls - */ + private static List prepareMissingCards(List allCards, boolean redownloadMode) { HashSet ignoreUrls = SettingsManager.getIntance().getIgnoreUrls(); - /** - * get filter for Standard Type 2 cards - */ + // get filter for Standard Type 2 cards Set type2SetsFilter = new HashSet<>(); List constructedFormats = ConstructedFormats.getSetsByFormat(ConstructedFormats.STANDARD); if (constructedFormats != null && !constructedFormats.isEmpty()) { @@ -374,6 +399,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements logger.warn("No formats defined. Try connecting to a server first!"); } + // prepare checking list List allCardsUrls = Collections.synchronizedList(new ArrayList<>()); try { allCards.parallelStream().forEach(card -> { @@ -423,26 +449,28 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements logger.info("Card was not selected: " + card.getName()); } }); + allCardsUrls.addAll(getTokenCardUrls()); } catch (Exception e) { logger.error(e); } - /** - * check to see which cards we already have - */ + // find missing files List cardsToDownload = Collections.synchronizedList(new ArrayList<>()); allCardsUrls.parallelStream().forEach(card -> { - File file = new TFile(CardImageUtils.buildImagePathToCard(card)); - logger.debug(card.getName() + " (is_token=" + card.isToken() + "). Image is here:" + file.getAbsolutePath() + " (exists=" + file.exists() + ')'); - if (!file.exists()) { - logger.debug("Missing: " + file.getAbsolutePath()); - // logger.info("Missing image: " + (card.isToken() ? "TOKEN " : "CARD ") + card.getSet() + "/" + card.getName() + " type: " + card.getType()); + if (redownloadMode) { + // need all cards cardsToDownload.add(card); + } else { + // need missing cards + File file = new TFile(CardImageUtils.buildImagePathToCard(card)); + if (!file.exists()) { + cardsToDownload.add(card); + } } }); - return new ArrayList<>(cardsToDownload); + return Collections.synchronizedList(new ArrayList<>(cardsToDownload)); } public static ArrayList getTokenCardUrls() throws RuntimeException { @@ -555,22 +583,20 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements if (p != null) { HashSet ignoreUrls = SettingsManager.getIntance().getIgnoreUrls(); - update(0, cardsToDownload.size()); - logger.info("Started download of " + cardsToDownload.size() - + " images from source: " + selectedSource.getSourceName() + update(0, cardsDownloadQueue.size()); + logger.info("Started download of " + cardsDownloadQueue.size() + " images" + + " from source: " + selectedSource.getSourceName() + ", language: " + selectedSource.getCurrentLanguage().getCode()); int numberOfThreads = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_THREADS, "10")); ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); - for (int i = 0; i < cardsToDownload.size() && !this.getNeedCancel(); i++) { + for (int i = 0; i < cardsDownloadQueue.size() && !this.getNeedCancel(); i++) { try { - - CardDownloadData card = cardsToDownload.get(i); + CardDownloadData card = cardsDownloadQueue.get(i); logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')'); CardImageUrls urls; - if (ignoreUrls.contains(card.getSet()) || card.isToken()) { if (!"0".equals(card.getCollectorId())) { continue; @@ -594,18 +620,18 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } else if (selectedSource.getTotalImages() == -1) { logger.info("Image not available on " + selectedSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')'); synchronized (sync) { - update(cardIndex + 1, cardsToDownload.size()); + update(cardIndex + 1, cardsDownloadQueue.size()); } } } else { - Runnable task = new DownloadTask(card, urls, cardsToDownload.size()); + Runnable task = new DownloadTask(card, urls, cardsDownloadQueue.size()); executor.execute(task); } - } catch (Exception ex) { logger.error(ex, ex); } } + executor.shutdown(); while (!executor.isTerminated()) { try { @@ -614,6 +640,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } } } + try { TVFS.umount(); } catch (FsSyncException e) { @@ -622,6 +649,8 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } finally { // } + + // stop reloadCardsToDownload(uiDialog.getSetsCombo().getSelectedItem().toString()); } @@ -638,10 +667,6 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements private final String actualFilename; private final boolean useSpecifiedPaths; - DownloadTask(CardDownloadData card, String baseUrl, int count) { - this(card, new CardImageUrls(baseUrl, null), count); - } - DownloadTask(CardDownloadData card, CardImageUrls urls, int count) { this.card = card; this.urls = urls; @@ -704,20 +729,23 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } // FILE already exists (in zip or in dir) + // don't use, images can be re-downloaded + /* if (destFile.exists()) { synchronized (sync) { update(cardIndex + 1, count); } return; } + */ - // zip can't be read + // check zip access TFile testArchive = destFile.getTopLevelArchive(); if (testArchive != null && testArchive.exists()) { try { testArchive.list(); } catch (Exception e) { - logger.error("Error reading archive, may be it was corrapted. Try to delete it: " + testArchive.toString()); + logger.error("Error reading archive, it's can be corrupted. Try to delete it: " + testArchive.toString()); synchronized (sync) { update(cardIndex + 1, count); @@ -849,34 +877,38 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements } } - private void update(int card, int count) { - this.cardIndex = card; + private void update(int lastCardIndex, int needDownloadCount) { + this.cardIndex = lastCardIndex; - if (cardIndex < count) { - float mb = ((count - card) * selectedSource.getAverageSize()) / 1024; + if (cardIndex < needDownloadCount) { + // downloading + float mb = ((needDownloadCount - lastCardIndex) * selectedSource.getAverageSize()) / 1024; uiDialog.getProgressBar().setString(String.format("%d of %d image downloads finished! Please wait! [%.1f Mb]", - card, count, mb)); + lastCardIndex, needDownloadCount, mb)); } else { - List remainingCards = Collections.synchronizedList(new ArrayList<>()); + // finished + List downloadedCards = Collections.synchronizedList(new ArrayList<>()); DownloadPicturesService.this.cardsMissing.parallelStream().forEach(cardDownloadData -> { TFile file = new TFile(CardImageUtils.buildImagePathToCard(cardDownloadData)); - if (!file.exists()) { - remainingCards.add(cardDownloadData); + if (file.exists()) { + downloadedCards.add(cardDownloadData); } }); - // remove the cards not downloaded to get the siccessfull downloaded cards - DownloadPicturesService.this.cardsToDownload.removeAll(remainingCards); - DownloadPicturesService.this.cardsMissing.removeAll(DownloadPicturesService.this.cardsToDownload); + // remove all downloaded cards, missing must be remains + this.cardsDownloadQueue.removeAll(downloadedCards); + this.cardsMissing.removeAll(downloadedCards); - count = remainingCards.size(); - - if (count == 0) { - uiDialog.getProgressBar().setString("0 images remaining! Please close!"); + if (this.cardsDownloadQueue.size() == 0) { + // stop download + uiDialog.getProgressBar().setString("0 images remaining. Please close."); } else { - //bar.setString(String.format("%d cards remaining! Please choose another source!", count)); - uiDialog.getStartButton().setEnabled(true); + // try download again } + + this.uiDialog.getRedownloadCheckbox().setSelected(false); + uiDialog.enableActionControls(true); + uiDialog.getStartButton().setEnabled(true); } } @@ -894,7 +926,7 @@ class LoadMissingCardDataNew implements Runnable { @Override public void run() { - downloadPicturesService.setAllMissingCards(); + downloadPicturesService.findMissingCards(); } public static void main() {