Add new DownloadImagesScreen with related components and updates, pre-implementation.
parent
aaf7fa131b
commit
64040dec28
|
@ -1,51 +1,24 @@
|
|||
package magic.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import magic.model.MagicCardDefinition;
|
||||
import magic.utility.MagicFileSystem;
|
||||
|
||||
public class CardImageFile extends DownloadableFile {
|
||||
|
||||
private final File file;
|
||||
private final URL url;
|
||||
private final String cardName;
|
||||
|
||||
public CardImageFile(final MagicCardDefinition cdef) throws MalformedURLException {
|
||||
file = MagicFileSystem.getCardImageFile(cdef);
|
||||
url = new URL(cdef.getImageURL());
|
||||
cardName = cdef.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLocalFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(final Proxy proxy) throws IOException {
|
||||
final File tempFile = new File(file.getParent(), "~" + file.getName());
|
||||
DownloadableFile.downloadToFile(proxy, url, tempFile);
|
||||
Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
super(
|
||||
MagicFileSystem.getCardImageFile(cdef),
|
||||
new URL(cdef.getImageURL())
|
||||
);
|
||||
cardName = cdef.getDistinctName();
|
||||
}
|
||||
|
||||
public String getCardName() {
|
||||
return cardName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getDownloadUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,18 +8,37 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
public abstract class DownloadableFile {
|
||||
public abstract void download(final Proxy proxy) throws IOException;
|
||||
public class DownloadableFile {
|
||||
|
||||
public abstract String getFilename();
|
||||
private final File localFile;
|
||||
private final URL remoteFile;
|
||||
|
||||
public abstract File getLocalFile();
|
||||
public DownloadableFile(File aFile, URL aUrl) {
|
||||
this.localFile = aFile;
|
||||
this.remoteFile = aUrl;
|
||||
}
|
||||
|
||||
public abstract URL getDownloadUrl();
|
||||
public File getLocalFile() {
|
||||
return localFile;
|
||||
}
|
||||
|
||||
protected String filenamePrefix = "";
|
||||
public URL getUrl() {
|
||||
return remoteFile;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return localFile.getName();
|
||||
}
|
||||
|
||||
public void doDownload(final Proxy proxy) throws IOException {
|
||||
final File tempFile = new File(localFile.getParent(), "~" + localFile.getName());
|
||||
downloadToFile(proxy, remoteFile, tempFile);
|
||||
Files.move(tempFile.toPath(), localFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
public static void downloadToFile(final Proxy proxy, final URL url, final File file) throws IOException {
|
||||
if (proxy != Proxy.NO_PROXY && proxy.type() != Proxy.Type.DIRECT) {
|
||||
|
@ -33,22 +52,18 @@ public abstract class DownloadableFile {
|
|||
try (
|
||||
final OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
|
||||
final InputStream inputStream = url.openConnection(proxy).getInputStream()) {
|
||||
final byte[] buffer=new byte[65536];
|
||||
while (true) {
|
||||
final int len=inputStream.read(buffer);
|
||||
if (len<0) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buffer,0,len);
|
||||
final byte[] buffer = new byte[65536];
|
||||
while (true) {
|
||||
final int len = inputStream.read(buffer);
|
||||
if (len < 0) {
|
||||
break;
|
||||
}
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
file.delete();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public void setFilenamePrefix(final String prefix) {
|
||||
filenamePrefix = prefix;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.text.ParseException;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import magic.ui.CardTextLanguage;
|
||||
import magic.ui.duel.animation.AnimationFx;
|
||||
import magic.ui.message.MessageStyle;
|
||||
import magic.utility.MagicFileSystem;
|
||||
|
@ -73,6 +74,7 @@ public class GeneralConfig {
|
|||
private static final String LOG_MESSAGE_STYLE = "logMessageStyle";
|
||||
private static final String ANIMATION_FLAGS = "animationFlags";
|
||||
private static final String PREF_IMAGE_SIZE = "preferredImageSize";
|
||||
private static final String CARD_TEXT_LANG = "cardTextLanguage";
|
||||
|
||||
private static final int DEFAULT_LEFT=-1;
|
||||
private static final int DEFAULT_TOP=0;
|
||||
|
@ -160,6 +162,7 @@ public class GeneralConfig {
|
|||
private String translation = DEFAULT_TRANSLATION;
|
||||
private MessageStyle logMessageStyle = MessageStyle.PLAIN;
|
||||
private String preferredImageSize = DEFAULT_PREF_IMAGE_SIZE;
|
||||
private CardTextLanguage cardTextLanguage = CardTextLanguage.ENGLISH;
|
||||
|
||||
private GeneralConfig() { }
|
||||
|
||||
|
@ -630,6 +633,7 @@ public class GeneralConfig {
|
|||
logMessageStyle = MessageStyle.valueOf(properties.getProperty(LOG_MESSAGE_STYLE, MessageStyle.PLAIN.name()));
|
||||
AnimationFx.setFlags(Integer.parseInt(properties.getProperty(ANIMATION_FLAGS, "" + AnimationFx.getFlags())));
|
||||
preferredImageSize = properties.getProperty(PREF_IMAGE_SIZE, DEFAULT_PREF_IMAGE_SIZE);
|
||||
cardTextLanguage = CardTextLanguage.valueOf(properties.getProperty(CARD_TEXT_LANG, CardTextLanguage.ENGLISH.name()));
|
||||
}
|
||||
|
||||
public void load() {
|
||||
|
@ -682,6 +686,7 @@ public class GeneralConfig {
|
|||
properties.setProperty(LOG_MESSAGE_STYLE, logMessageStyle.name());
|
||||
properties.setProperty(ANIMATION_FLAGS, String.valueOf(AnimationFx.getFlags()));
|
||||
properties.setProperty(PREF_IMAGE_SIZE, preferredImageSize);
|
||||
properties.setProperty(CARD_TEXT_LANG, cardTextLanguage.name());
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
@ -714,4 +719,12 @@ public class GeneralConfig {
|
|||
this.preferredImageSize = s;
|
||||
}
|
||||
|
||||
public CardTextLanguage getCardTextLanguage() {
|
||||
return cardTextLanguage;
|
||||
}
|
||||
|
||||
public void setCardTextLanguage(CardTextLanguage aLang) {
|
||||
this.cardTextLanguage = aLang;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,42 +5,13 @@ import java.io.File;
|
|||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import magic.data.DownloadableFile;
|
||||
|
||||
public class DownloadableJsonFile extends DownloadableFile {
|
||||
|
||||
private final URL url;
|
||||
private final File file;
|
||||
|
||||
public DownloadableJsonFile(final String url, final File jsonFile) throws MalformedURLException {
|
||||
this.url = new URL(url);
|
||||
file = jsonFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Proxy proxy) throws IOException {
|
||||
final File tempFile = new File(file.getParent(), "~" + file.getName());
|
||||
DownloadableFile.downloadToFile(proxy, url, tempFile);
|
||||
Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLocalFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getDownloadUrl() {
|
||||
return url;
|
||||
super(jsonFile, new URL(url));
|
||||
}
|
||||
|
||||
public static String getJsonString(final File jsonFile) throws IOException {
|
||||
|
@ -56,5 +27,4 @@ public class DownloadableJsonFile extends DownloadableFile {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public final class NewVersionJsonParser {
|
|||
private static boolean downloadJsonToFile(final File jsonFile) {
|
||||
try {
|
||||
final DownloadableJsonFile downloadFile = new DownloadableJsonFile("https://magarena.github.io/current.json", jsonFile);
|
||||
downloadFile.download(GeneralConfig.getInstance().getProxy());
|
||||
downloadFile.doDownload(GeneralConfig.getInstance().getProxy());
|
||||
if (jsonFile.length() == 0) {
|
||||
System.err.println("new version json file is empty!");
|
||||
return false;
|
||||
|
|
|
@ -30,7 +30,7 @@ public final class FiremindJsonReader {
|
|||
try {
|
||||
final DownloadableJsonFile downloadFile =
|
||||
new DownloadableJsonFile("https://www.firemind.ch/decks/top.json", jsonFile);
|
||||
downloadFile.download(GeneralConfig.getInstance().getProxy());
|
||||
downloadFile.doDownload(GeneralConfig.getInstance().getProxy());
|
||||
} catch (IOException ex) {
|
||||
System.err.println("Download of json file failed : " + ex.getMessage());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package magic.ui;
|
||||
|
||||
public enum CardTextLanguage {
|
||||
|
||||
ENGLISH("English", "en"),
|
||||
// Magarena translations
|
||||
PORTUGUESE("Português", "pt"),
|
||||
RUSSIAN("Русский", "ru"),
|
||||
// other languages offered by magiccards.info.
|
||||
GERMAN("Deutsch", "de"),
|
||||
SPANISH("Español", "es"),
|
||||
FRENCH("Français", "fr"),
|
||||
ITALIAN("Italiano", "it"),
|
||||
CHINESE("简体中文", "cn"),
|
||||
JAPANESE("日本語", "jp"),
|
||||
TAIWANESE("繁體中文", "tw")
|
||||
;
|
||||
|
||||
private final String caption;
|
||||
private final String mcardsLangCode; // language code used by magiccards.info in image urls.
|
||||
|
||||
private CardTextLanguage(String aCaption, String aLangCode) {
|
||||
this.caption = aCaption;
|
||||
this.mcardsLangCode = aLangCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return caption;
|
||||
}
|
||||
|
||||
public String getMagicCardsCode() {
|
||||
return mcardsLangCode;
|
||||
}
|
||||
|
||||
public boolean isEnglish() {
|
||||
return this == ENGLISH;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package magic.ui;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import magic.utility.MagicFileSystem;
|
||||
|
||||
public class MagicLogFile {
|
||||
|
||||
private boolean isError = false;
|
||||
private final File logFile;
|
||||
|
||||
public MagicLogFile(String aLogName) {
|
||||
final Path logsPath = MagicFileSystem.getDataPath(MagicFileSystem.DataPath.LOGS);
|
||||
logFile = logsPath.resolve(aLogName + ".log").toFile();
|
||||
}
|
||||
|
||||
public void deleteLogFileIfExists() throws IOException {
|
||||
if (!isError) {
|
||||
Files.deleteIfExists(logFile.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
public void log(String msg) throws IOException {
|
||||
if (!isError) {
|
||||
try (
|
||||
FileWriter fw = new FileWriter(logFile, true);
|
||||
BufferedWriter bw = new BufferedWriter(fw);
|
||||
PrintWriter out = new PrintWriter(bw)) {
|
||||
out.println(msg);
|
||||
} catch (IOException ex) {
|
||||
isError = true;
|
||||
throw new IOException("Logging switched off (restart Magarena to enable) : " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void log(String msg, CardTextLanguage lang, URL cardUrl) throws IOException {
|
||||
log(String.format("%s %s %s",
|
||||
lang != CardTextLanguage.ENGLISH ? lang.getMagicCardsCode() : " ",
|
||||
msg,
|
||||
cardUrl != null ? "(" + cardUrl.toExternalForm() + ")" : ""));
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import magic.ui.screen.DeckEditorScreen;
|
|||
import magic.ui.screen.DeckTiledCardsScreen;
|
||||
import magic.ui.screen.DeckViewScreen;
|
||||
import magic.ui.screen.DecksScreen;
|
||||
import magic.ui.screen.DownloadImagesScreen;
|
||||
import magic.ui.screen.DuelDecksScreen;
|
||||
import magic.ui.screen.DuelGameScreen;
|
||||
import magic.ui.screen.GameLogScreen;
|
||||
|
@ -55,6 +56,7 @@ public final class ScreenController {
|
|||
|
||||
private static MagicFrame mainFrame = null;
|
||||
private static final Stack<AbstractScreen> screens = new Stack<>();
|
||||
private static AbstractScreen hiddenScreen;
|
||||
|
||||
public static MagicFrame getMainFrame() {
|
||||
if (mainFrame == null && java.awt.GraphicsEnvironment.isHeadless() == false) {
|
||||
|
@ -190,6 +192,10 @@ public final class ScreenController {
|
|||
new AboutDialog(getMainFrame());
|
||||
}
|
||||
|
||||
public static void showDownloadImagesScreen() {
|
||||
showScreen(new DownloadImagesScreen());
|
||||
}
|
||||
|
||||
public static void showPreferencesDialog() {
|
||||
final PreferencesDialog dialog = new PreferencesDialog(getMainFrame(), isDuelActive());
|
||||
if (dialog.isRestartRequired()) {
|
||||
|
@ -225,9 +231,14 @@ public final class ScreenController {
|
|||
}
|
||||
}
|
||||
|
||||
private static void showScreen(final AbstractScreen screen) {
|
||||
private static void showScreen(AbstractScreen screen) {
|
||||
if (hiddenScreen != null && hiddenScreen.getClass().getName().equals(screen.getClass().getName())) {
|
||||
screen = hiddenScreen;
|
||||
hiddenScreen = null;
|
||||
}
|
||||
setMainFrameScreen(screen);
|
||||
screens.push(screen);
|
||||
screen.setVisible(true);
|
||||
screen.requestFocus();
|
||||
}
|
||||
|
||||
|
@ -274,4 +285,14 @@ public final class ScreenController {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static void hideActiveScreen() {
|
||||
if (hiddenScreen == null) {
|
||||
hiddenScreen = screens.pop();
|
||||
hiddenScreen.setVisible(false);
|
||||
showScreen(screens.pop());
|
||||
} else {
|
||||
throw new RuntimeException("A screen is already hidden - only one allowed at a time!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@ package magic.ui;
|
|||
|
||||
import java.awt.Desktop;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
public class URLUtils {
|
||||
|
||||
|
@ -35,4 +38,37 @@ public class URLUtils {
|
|||
use.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code magiccards.info} image url for a given non-English language code.
|
||||
*/
|
||||
public static URL getAlternateMagicCardsImageUrl(URL defaultUrl, CardTextLanguage lang) throws MalformedURLException {
|
||||
final String BASE = "/magiccards.info/scans/";
|
||||
final String TARGET = BASE + "en/";
|
||||
final String s = defaultUrl.toExternalForm();
|
||||
return s.contains(TARGET)
|
||||
? new URL(s.replaceAll(TARGET, BASE + lang.getMagicCardsCode() + "/"))
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quickly checks to see whether URL is reachable.
|
||||
*/
|
||||
public static boolean isUrlValid(final URL url) {
|
||||
|
||||
if (url == null)
|
||||
return false;
|
||||
|
||||
try {
|
||||
final HttpURLConnection huc = (HttpURLConnection) url.openConnection();
|
||||
huc.setRequestMethod("HEAD");
|
||||
huc.setConnectTimeout(2000);
|
||||
final int responseCode = huc.getResponseCode();
|
||||
return responseCode == HttpURLConnection.HTTP_OK;
|
||||
|
||||
} catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package magic.ui.dialog.button;
|
||||
|
||||
import magic.data.MagicIcon;
|
||||
import magic.ui.MagicImages;
|
||||
import magic.translate.UiString;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class CloseButton extends MagicDialogButton {
|
||||
|
||||
// translatable strings
|
||||
private static final String _S1 = "Close";
|
||||
|
||||
public CloseButton() {
|
||||
super(UiString.get(_S1));
|
||||
setIcon(MagicImages.getIcon(MagicIcon.BANNED_ICON));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import magic.data.MagicIcon;
|
||||
import magic.translate.UiString;
|
||||
import magic.ui.MagicImages;
|
||||
import magic.ui.URLUtils;
|
||||
import magic.ui.dialog.button.CloseButton;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class DialogMainPanel extends JPanel implements PropertyChangeListener {
|
||||
|
||||
static final String CP_CLOSE = "b0fbb0a7";
|
||||
static final String CP_RUN_BACKGROUND = "a88997e6";
|
||||
|
||||
// translatable strings
|
||||
private static final String _S3 = "Run in background...";
|
||||
|
||||
private final JButton backgroundButton;
|
||||
private final OptionsPanel optionsPanel;
|
||||
private final List<DownloadPanel> downloadPanels = new ArrayList<>();
|
||||
private final ErrorPanel errorPanel = new ErrorPanel();
|
||||
private final ButtonsPanel buttonsPanel;
|
||||
|
||||
DialogMainPanel() {
|
||||
|
||||
optionsPanel = new OptionsPanel();
|
||||
optionsPanel.setEnabled(false);
|
||||
optionsPanel.addPropertyChangeListener(OptionsPanel.CP_OPTIONS_CHANGED,
|
||||
(e) -> { refreshDownloadList(); }
|
||||
);
|
||||
|
||||
backgroundButton = new JButton(UiString.get(_S3));
|
||||
backgroundButton.setFocusable(false);
|
||||
backgroundButton.addActionListener((a) -> { doRunInBackground(); });
|
||||
|
||||
buttonsPanel = new ButtonsPanel();
|
||||
|
||||
setDownloadPanels();
|
||||
setLookAndFeel();
|
||||
refreshLayout();
|
||||
}
|
||||
|
||||
private void doRunInBackground() {
|
||||
firePropertyChange(CP_RUN_BACKGROUND, true, false);
|
||||
}
|
||||
|
||||
private void refreshDownloadList() {
|
||||
final DownloadMode mode = optionsPanel.getDownloadMode();
|
||||
for (DownloadPanel panel : downloadPanels) {
|
||||
panel.refreshDownloadList(mode);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDownloadPanels() {
|
||||
downloadPanels.add(getPlayableDownloaderPanel());
|
||||
downloadPanels.add(getUnimplementedDownloaderPanel());
|
||||
}
|
||||
|
||||
private void refreshLayout() {
|
||||
setLayout(new MigLayout("flowy, gap 0, insets 2 6 6 6"));
|
||||
add(optionsPanel, "w 100%, gapbottom 6");
|
||||
add(getDownloadPanel(), "w 100%");
|
||||
add(errorPanel, "w 100%, h 100%");
|
||||
add(buttonsPanel, "w 100%, h 30!, pushy, aligny bottom");
|
||||
}
|
||||
|
||||
private void setLookAndFeel() {
|
||||
backgroundButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private JPanel getDownloadPanel() {
|
||||
final JPanel panel = new JPanel(new MigLayout("flowy, insets 6 0 6 0, gapy 0"));
|
||||
panel.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0, Color.GRAY));
|
||||
for (DownloadPanel downloadPanel : downloadPanels) {
|
||||
panel.add(downloadPanel, "w 100%");
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
private DownloadPanel getPlayableDownloaderPanel() {
|
||||
final DownloadPanel playableDownloaderPanel = new PlayablePanel(
|
||||
optionsPanel.getDownloadMode(),
|
||||
optionsPanel.getCardTextLanguage(),
|
||||
this
|
||||
);
|
||||
playableDownloaderPanel.addPropertyChangeListener(DownloadPanel.CP_STATE_CHANGED, this);
|
||||
return playableDownloaderPanel;
|
||||
}
|
||||
|
||||
private DownloadPanel getUnimplementedDownloaderPanel() {
|
||||
final DownloadPanel unimplementedDownloaderPanel = new UnimplementedPanel(
|
||||
optionsPanel.getDownloadMode(),
|
||||
optionsPanel.getCardTextLanguage(),
|
||||
this
|
||||
);
|
||||
unimplementedDownloaderPanel.addPropertyChangeListener(DownloadPanel.CP_STATE_CHANGED, this);
|
||||
return unimplementedDownloaderPanel;
|
||||
}
|
||||
|
||||
private void updateComponentState() {
|
||||
boolean isScanning = false;
|
||||
boolean isDownloading = false;
|
||||
for (DownloadPanel panel : downloadPanels) {
|
||||
isScanning |= panel.getState() == DownloadState.SCANNING;
|
||||
isDownloading |= panel.getState() == DownloadState.DOWNLOADING;
|
||||
}
|
||||
optionsPanel.setEnabled(!isScanning && !isDownloading);
|
||||
buttonsPanel.setIsDownloading(isDownloading);
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
for (DownloadPanel panel : downloadPanels) {
|
||||
if (panel.getState() == DownloadState.SCANNING ||
|
||||
panel.getState() == DownloadState.DOWNLOADING) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class ButtonsPanel extends JPanel {
|
||||
|
||||
private final JButton closeButton;
|
||||
|
||||
ButtonsPanel() {
|
||||
|
||||
closeButton = new CloseButton();
|
||||
closeButton.setFocusable(false);
|
||||
closeButton.addActionListener(getCancelAction());
|
||||
|
||||
final JButton helpButton = new JButton();
|
||||
helpButton.setIcon(MagicImages.getIcon(MagicIcon.MISSING_ICON));
|
||||
helpButton.setFocusable(false);
|
||||
helpButton.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
URLUtils.openURL(URLUtils.URL_WIKI + "UIDownloadImages");
|
||||
}
|
||||
});
|
||||
|
||||
setLayout(new MigLayout("insets 0, alignx right, aligny bottom"));
|
||||
add(helpButton);
|
||||
add(backgroundButton, "w 100%");
|
||||
add(closeButton);
|
||||
}
|
||||
|
||||
void setIsDownloading(boolean b) {
|
||||
closeButton.setEnabled(b == false);
|
||||
backgroundButton.setEnabled(b);
|
||||
}
|
||||
}
|
||||
|
||||
private void doCancelAndClose() {
|
||||
for (DownloadPanel panel : downloadPanels) {
|
||||
panel.doCancel();
|
||||
}
|
||||
firePropertyChange(CP_CLOSE, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals(DownloadPanel.CP_STATE_CHANGED)) {
|
||||
updateComponentState();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void setMessage(final String message) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
errorPanel.setErrorText(message + "\n");
|
||||
if (!errorPanel.isVisible()) {
|
||||
errorPanel.setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AbstractAction getCancelAction() {
|
||||
return new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (!backgroundButton.isEnabled()) {
|
||||
doCancelAndClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import magic.ui.ScreenController;
|
||||
import magic.ui.theme.Theme;
|
||||
import magic.ui.utility.MagicStyle;
|
||||
import magic.ui.widget.FontsAndBorders;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DownloadDialogPanel extends JPanel implements PropertyChangeListener {
|
||||
|
||||
private final DialogMainPanel mainPanel;
|
||||
|
||||
public DownloadDialogPanel() {
|
||||
|
||||
setPreferredSize(new Dimension(440, 460));
|
||||
setBorder(
|
||||
BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(Color.DARK_GRAY),
|
||||
BorderFactory.createMatteBorder(0, 8, 8, 8, MagicStyle.getTheme().getColor(Theme.COLOR_TITLE_BACKGROUND)))
|
||||
);
|
||||
|
||||
mainPanel = getMainPanel();
|
||||
|
||||
setLayout(new MigLayout("flowy, insets 0"));
|
||||
add(getDialogCaptionLabel(), "w 100%, h 26!");
|
||||
add(mainPanel, "w 100%, h 100%");
|
||||
}
|
||||
|
||||
private DialogMainPanel getMainPanel() {
|
||||
final DialogMainPanel panel = new DialogMainPanel();
|
||||
panel.addPropertyChangeListener(DialogMainPanel.CP_CLOSE, this);
|
||||
panel.addPropertyChangeListener(DialogMainPanel.CP_RUN_BACKGROUND, this);
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JLabel getDialogCaptionLabel() {
|
||||
final JLabel lbl = new JLabel("Download card images");
|
||||
lbl.setOpaque(true);
|
||||
lbl.setBackground(MagicStyle.getTheme().getColor(Theme.COLOR_TITLE_BACKGROUND));
|
||||
lbl.setForeground(MagicStyle.getTheme().getColor(Theme.COLOR_TITLE_FOREGROUND));
|
||||
lbl.setFont(FontsAndBorders.FONT1.deriveFont(14f));
|
||||
lbl.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private void doCloseScreen() {
|
||||
ScreenController.closeActiveScreen(false);
|
||||
}
|
||||
|
||||
private void doHideScreen() {
|
||||
ScreenController.hideActiveScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
switch (evt.getPropertyName()) {
|
||||
case DialogMainPanel.CP_CLOSE:
|
||||
doCloseScreen();
|
||||
break;
|
||||
case DialogMainPanel.CP_RUN_BACKGROUND:
|
||||
doHideScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBusy() {
|
||||
return mainPanel.isBusy();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
enum DownloadMode {
|
||||
|
||||
MISSING("Missing images"),
|
||||
ALL("All images");
|
||||
|
||||
private final String desc;
|
||||
|
||||
private DownloadMode(String aDesc) {
|
||||
this.desc = aDesc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return desc;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import magic.data.CardImageFile;
|
||||
import magic.data.DownloadableFile;
|
||||
import magic.data.ImagesDownloadList;
|
||||
import magic.data.GeneralConfig;
|
||||
import magic.data.MagicIcon;
|
||||
import magic.model.MagicCardDefinition;
|
||||
import magic.ui.MagicImages;
|
||||
import magic.translate.UiString;
|
||||
import magic.ui.CardTextLanguage;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
abstract class DownloadPanel extends JPanel implements IScanListener, IDownloadListener {
|
||||
|
||||
static final String CP_STATE_CHANGED = "7b40b0dd";
|
||||
|
||||
// translatable strings
|
||||
private static final String _S1 = "Cancel";
|
||||
|
||||
protected final GeneralConfig CONFIG = GeneralConfig.getInstance();
|
||||
|
||||
private final MigLayout migLayout = new MigLayout();
|
||||
protected final JLabel captionLabel = getCaptionLabel(getProgressCaption());
|
||||
protected final JButton downloadButton = new JButton();
|
||||
private final JButton cancelButton = new JButton(UiString.get(_S1));
|
||||
protected final JProgressBar progressBar = new JProgressBar();
|
||||
|
||||
private ImagesDownloadList files;
|
||||
private boolean isCancelled = false;
|
||||
private SwingWorker<Void, Integer> imagesDownloader;
|
||||
private ScanWorker imagesScanner;
|
||||
private DownloadState downloaderState = DownloadState.STOPPED;
|
||||
private DownloadMode downloadMode = DownloadMode.MISSING;
|
||||
private CardTextLanguage textLanguage = CardTextLanguage.ENGLISH;
|
||||
private final DialogMainPanel mainPanel;
|
||||
|
||||
protected abstract String getProgressCaption();
|
||||
protected abstract String getDownloadButtonCaption();
|
||||
|
||||
DownloadPanel(DownloadMode aMode, CardTextLanguage aLang, DialogMainPanel aPanel) {
|
||||
this.downloadMode = aMode;
|
||||
this.textLanguage = aLang;
|
||||
this.mainPanel = aPanel;
|
||||
setLookAndFeel();
|
||||
refreshLayout();
|
||||
setActions();
|
||||
buildDownloadImagesList();
|
||||
}
|
||||
|
||||
DownloadState getState() {
|
||||
return downloaderState;
|
||||
}
|
||||
|
||||
private void doRunImageDownloadWorker() {
|
||||
setDownloadingState();
|
||||
imagesDownloader = new DownloadWorker(this, files, textLanguage, downloadMode);
|
||||
imagesDownloader.execute();
|
||||
notifyStatusChanged(DownloadState.DOWNLOADING);
|
||||
}
|
||||
|
||||
private void setActions() {
|
||||
downloadButton.addActionListener(action -> { doRunImageDownloadWorker(); });
|
||||
cancelButton.addActionListener(action -> { doCancelImageDownloadWorker(); });
|
||||
}
|
||||
|
||||
void doCancel() {
|
||||
isCancelled = true;
|
||||
doCancelImageDownloadWorker();
|
||||
doCancelImagesScanner();
|
||||
}
|
||||
|
||||
private void doCancelImageDownloadWorker() {
|
||||
if (imagesDownloader != null && !imagesDownloader.isCancelled() && !imagesDownloader.isDone()) {
|
||||
imagesDownloader.cancel(true);
|
||||
setButtonState(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void doCancelImagesScanner() {
|
||||
if (imagesScanner != null && !imagesScanner.isCancelled() && !imagesScanner.isDone()) {
|
||||
imagesScanner.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void buildDownloadImagesList() {
|
||||
if (imagesScanner != null && !imagesScanner.isDone()) {
|
||||
System.out.println("cancelling images scanner");
|
||||
imagesScanner.cancel(true);
|
||||
}
|
||||
if (!isCancelled) {
|
||||
captionLabel.setIcon(MagicImages.getIcon(MagicIcon.BUSY16));
|
||||
captionLabel.setText(getProgressCaption());
|
||||
imagesScanner = new ScanWorker(this, downloadMode);
|
||||
imagesScanner.execute();
|
||||
downloadButton.setEnabled(false);
|
||||
notifyStatusChanged(DownloadState.SCANNING);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetProgressBar() {
|
||||
progressBar.setMinimum(0);
|
||||
progressBar.setMaximum(files.size());
|
||||
progressBar.setValue(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setButtonState(final boolean isDownloading) {
|
||||
downloadButton.setVisible(!isDownloading);
|
||||
cancelButton.setVisible(isDownloading);
|
||||
refreshLayout();
|
||||
}
|
||||
|
||||
private void setDownloadingState() {
|
||||
setButtonState(true);
|
||||
resetProgressBar();
|
||||
captionLabel.setIcon(MagicImages.getIcon(MagicIcon.BUSY16));
|
||||
}
|
||||
|
||||
private void refreshLayout() {
|
||||
removeAll();
|
||||
migLayout.setLayoutConstraints("flowx, insets 0, gap 0");
|
||||
add(progressBar, "w 100%, h 26!");
|
||||
add(downloadButton.isVisible() ? downloadButton : cancelButton, "w 100!");
|
||||
}
|
||||
|
||||
private void setLookAndFeel() {
|
||||
// Layout manager.
|
||||
setLayout(migLayout);
|
||||
// download button
|
||||
downloadButton.setEnabled(false);
|
||||
downloadButton.setText(getDownloadButtonCaption());
|
||||
// progress bar
|
||||
progressBar.setLayout(new MigLayout("insets 0 8 0 0, aligny center"));
|
||||
progressBar.add(captionLabel, "w 100%, h 100%");
|
||||
progressBar.setStringPainted(false);
|
||||
}
|
||||
|
||||
private JLabel getCaptionLabel(final String text) {
|
||||
final ImageIcon ii = MagicImages.getIcon(MagicIcon.BUSY16);
|
||||
final JLabel lbl = new JLabel(ii);
|
||||
lbl.setText(text);
|
||||
lbl.setHorizontalAlignment(SwingConstants.LEFT);
|
||||
lbl.setHorizontalTextPosition(SwingConstants.LEADING);
|
||||
lbl.setOpaque(false);
|
||||
lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
|
||||
return lbl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyStatusChanged(final DownloadState newState) {
|
||||
assert SwingUtilities.isEventDispatchThread();
|
||||
final DownloadState oldState = downloaderState;
|
||||
downloaderState = newState;
|
||||
firePropertyChange(CP_STATE_CHANGED, oldState, newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doScannerFinished(final ImagesDownloadList aList) {
|
||||
files = aList;
|
||||
downloadButton.setEnabled(files != null && files.size() > 0);
|
||||
captionLabel.setIcon(null);
|
||||
captionLabel.setText(String.format("%s = %d",
|
||||
getProgressCaption(),
|
||||
files == null ? 0 : files.size())
|
||||
);
|
||||
}
|
||||
|
||||
void refreshDownloadList(DownloadMode aType) {
|
||||
this.downloadMode = aType;
|
||||
buildDownloadImagesList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(final String message) {
|
||||
mainPanel.setMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showProgress(int count, int total) {
|
||||
progressBar.setValue(count);
|
||||
captionLabel.setText(String.format("%s = %d",
|
||||
getProgressCaption(),
|
||||
total - getCustomCount(count))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetProgress() {
|
||||
progressBar.setValue(0);
|
||||
progressBar.setString(null);
|
||||
}
|
||||
|
||||
protected String doFileDownloadAndGetName(final DownloadableFile file, final Proxy proxy) throws IOException {
|
||||
file.doDownload(proxy);
|
||||
if (file instanceof CardImageFile) {
|
||||
return ((CardImageFile) file).getCardName();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
protected int getCustomCount(int countInteger) {
|
||||
return countInteger;
|
||||
}
|
||||
|
||||
protected static Stream<MagicCardDefinition> getCards(Collection<MagicCardDefinition> cards, Date aDate) {
|
||||
return cards.stream()
|
||||
.filter(MagicCardDefinition::hasImageUrl)
|
||||
.filter(card -> card.isImageUpdatedAfter(aDate) || card.isImageFileMissing());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
enum DownloadState {
|
||||
STOPPED,
|
||||
SCANNING,
|
||||
DOWNLOADING;
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import magic.ui.CardTextLanguage;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import magic.data.CardImageFile;
|
||||
import magic.data.DownloadableFile;
|
||||
import magic.data.GeneralConfig;
|
||||
import magic.data.ImagesDownloadList;
|
||||
import magic.ui.URLUtils;
|
||||
import magic.ui.MagicLogFile;
|
||||
import magic.utility.MagicSystem;
|
||||
|
||||
class DownloadWorker extends SwingWorker<Void, Integer> {
|
||||
|
||||
private static final MagicLogFile missingLog = new MagicLogFile("downloaded-images");
|
||||
|
||||
private final Proxy proxy;
|
||||
private final ImagesDownloadList downloadList;
|
||||
private final IDownloadListener listener;
|
||||
private final CardTextLanguage textLanguage;
|
||||
private final DownloadMode downloadMode;
|
||||
private boolean updateDownloadDate = true;
|
||||
private boolean isLogging = true;
|
||||
|
||||
DownloadWorker(
|
||||
IDownloadListener aListener,
|
||||
ImagesDownloadList downloadList,
|
||||
CardTextLanguage aLanguage,
|
||||
DownloadMode aDownloadMode) {
|
||||
|
||||
this.listener = aListener;
|
||||
this.downloadList = downloadList;
|
||||
this.textLanguage = aLanguage;
|
||||
this.downloadMode = aDownloadMode;
|
||||
this.proxy = GeneralConfig.getInstance().getProxy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
doDownloadImages(textLanguage);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
get();
|
||||
} catch (ExecutionException | InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (CancellationException ex) {
|
||||
System.err.println("ImageDownloadWorker cancelled.");
|
||||
}
|
||||
listener.setButtonState(false);
|
||||
resetProgressBar();
|
||||
|
||||
magic.ui.CachedImagesProvider.getInstance().clearCache();
|
||||
listener.buildDownloadImagesList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process(List<Integer> chunks) {
|
||||
final int countInteger = chunks.get(chunks.size() - 1);
|
||||
if (!isCancelled()) {
|
||||
listener.showProgress(countInteger, downloadList.size());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLoggingOn() {
|
||||
return isLogging && MagicSystem.isDevMode() && downloadMode == DownloadMode.MISSING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Experimental, currently only available in devMode and missing mode.
|
||||
*/
|
||||
private void doLog(String cardName, CardTextLanguage aLang, URL cardUrl) {
|
||||
if (isLoggingOn()) {
|
||||
synchronized (missingLog) {
|
||||
try {
|
||||
missingLog.log(cardName, aLang, cardUrl);
|
||||
} catch (IOException ex) {
|
||||
System.err.println("doLog: " + ex);
|
||||
isLogging = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes log files for capturing useful info (DEVMODE ONLY!).
|
||||
*/
|
||||
private void initializeLogFiles() {
|
||||
if (isLoggingOn()) {
|
||||
synchronized (missingLog) {
|
||||
try {
|
||||
missingLog.deleteLogFileIfExists();
|
||||
missingLog.log("# " + LocalDateTime.now());
|
||||
missingLog.log("#");
|
||||
} catch (IOException ex) {
|
||||
System.err.println("initializeLogFiles: " + ex);
|
||||
isLogging = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets alternate magiccards.info image url for given card text language.
|
||||
* If it fails then displays reason in error log panel and returns null url.
|
||||
*/
|
||||
private URL getAlternateUrl(DownloadableFile aFile, CardTextLanguage textLang) {
|
||||
try {
|
||||
return URLUtils.getAlternateMagicCardsImageUrl(aFile.getUrl(), textLang);
|
||||
} catch (MalformedURLException ex) {
|
||||
listener.setMessage(String.format("%s [%s]", ex.toString(), aFile.getUrl()));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to download alternate card image.
|
||||
* If it fails displays reason in error log panel.
|
||||
*/
|
||||
private boolean tryAlternateDownload(final DownloadableFile aFile, CardTextLanguage aLang) {
|
||||
try {
|
||||
aFile.doDownload(proxy);
|
||||
} catch (IOException ex) {
|
||||
listener.setMessage(String.format("%s [%s (%s)]", ex.toString(), aFile.getFilename(), aLang));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean downloadAlternateCardImage(final CardImageFile aFile, CardTextLanguage aLang) {
|
||||
final URL altUrl = getAlternateUrl(aFile, aLang);
|
||||
if (URLUtils.isUrlValid(altUrl)) {
|
||||
if (tryAlternateDownload(new DownloadableFile(aFile.getLocalFile(), altUrl), aLang)) {
|
||||
doLog(aFile.getCardName(), aLang, altUrl);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to download default card image.
|
||||
* If it fails displays reason in error log panel.
|
||||
*/
|
||||
private boolean tryDefaultDownload(final DownloadableFile aFile) {
|
||||
try {
|
||||
aFile.doDownload(proxy);
|
||||
} catch (IOException ex) {
|
||||
listener.setMessage(String.format("%s [%s]", ex.toString(), aFile.getFilename()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void downloadDefaultCardImage(final CardImageFile aFile) {
|
||||
if (tryDefaultDownload(aFile)) {
|
||||
doLog(aFile.getCardName(), CardTextLanguage.ENGLISH, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void doDownloadImages(CardTextLanguage textLang) {
|
||||
|
||||
initializeLogFiles();
|
||||
int fileCount = 0;
|
||||
|
||||
for (DownloadableFile dFile : downloadList) {
|
||||
|
||||
final CardImageFile imageFile = (CardImageFile) dFile;
|
||||
|
||||
if (deleteLocalImageFileIfMissing(imageFile) == false)
|
||||
continue; // with next DownloadableFile.
|
||||
|
||||
if (textLang.isEnglish() || !downloadAlternateCardImage(imageFile, textLang)) {
|
||||
downloadDefaultCardImage(imageFile);
|
||||
}
|
||||
|
||||
fileCount++;
|
||||
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
} else {
|
||||
publish(new Integer(fileCount));
|
||||
}
|
||||
}
|
||||
|
||||
if (updateDownloadDate) {
|
||||
listener.doCustomActionAfterDownload();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If downloading missing images but local image file already exists then download was
|
||||
* triggered by 'image_updated'. If unable to delete image file so that it becomes
|
||||
* "missing" do not update 'imageDownloaderRunDate' so that image update remains pending.
|
||||
*/
|
||||
private boolean deleteLocalImageFileIfMissing(DownloadableFile dFile) {
|
||||
if (downloadMode == DownloadMode.MISSING) {
|
||||
try {
|
||||
Files.deleteIfExists(dFile.getLocalFile().toPath());
|
||||
} catch (IOException ex) {
|
||||
updateDownloadDate = false;
|
||||
listener.setMessage(String.format("%s [%s]", ex.toString(), dFile.getFilename()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void resetProgressBar() {
|
||||
assert SwingUtilities.isEventDispatchThread();
|
||||
listener.resetProgress();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import magic.translate.UiString;
|
||||
import magic.ui.ScreenController;
|
||||
import magic.ui.widget.FontsAndBorders;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class ErrorPanel extends JPanel {
|
||||
|
||||
private static final String _S4 = "Copy to clipboard";
|
||||
private static final String _S5 = "Error details have been copied to the clipboard.";
|
||||
|
||||
private final JTextArea textArea;
|
||||
|
||||
ErrorPanel() {
|
||||
|
||||
final JButton copyButton = new JButton(UiString.get(_S4));
|
||||
copyButton.setEnabled(false);
|
||||
|
||||
textArea = new JTextArea();
|
||||
textArea.setEditable(false);
|
||||
textArea.setForeground(Color.red);
|
||||
textArea.setFont(FontsAndBorders.FONT_README.deriveFont(11.0f));
|
||||
textArea.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
copyButton.setEnabled(!isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
copyButton.setEnabled(!isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
copyButton.setEnabled(!isEmpty());
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return textArea.getText().trim().isEmpty();
|
||||
}
|
||||
});
|
||||
|
||||
final JScrollPane scrollPane = new JScrollPane(textArea);
|
||||
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
scrollPane.setBorder(FontsAndBorders.BLACK_BORDER);
|
||||
|
||||
copyButton.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
final StringSelection textSelection = new StringSelection(textArea.getText());
|
||||
clip.setContents(textSelection, null);
|
||||
ScreenController.showInfoMessage(UiString.get(_S5));
|
||||
}
|
||||
});
|
||||
|
||||
setVisible(false);
|
||||
setLayout(new MigLayout("flowy, gap 0, insets 10 0 10 0"));
|
||||
add(scrollPane, "w 100%, h 100%");
|
||||
add(copyButton, "w 100%");
|
||||
}
|
||||
|
||||
void setErrorText(String text) {
|
||||
textArea.append(text);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
interface IDownloadListener {
|
||||
void setMessage(final String message);
|
||||
void setButtonState(final boolean isDownloading);
|
||||
void doCustomActionAfterDownload();
|
||||
void buildDownloadImagesList();
|
||||
void showProgress(int count, int total);
|
||||
void resetProgress();
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import magic.data.ImagesDownloadList;
|
||||
import magic.model.MagicCardDefinition;
|
||||
|
||||
interface IScanListener {
|
||||
Stream<MagicCardDefinition> getCards(final DownloadMode aType);
|
||||
void doScannerFinished(final ImagesDownloadList aList);
|
||||
void notifyStatusChanged(final DownloadState newState);
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.awt.event.ItemEvent;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import magic.data.GeneralConfig;
|
||||
import magic.translate.UiString;
|
||||
import magic.ui.CardTextLanguage;
|
||||
import magic.ui.widget.DirectoryChooser;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class OptionsPanel extends JPanel {
|
||||
|
||||
static final String CP_OPTIONS_CHANGED = "1b7206a7";
|
||||
|
||||
private final GeneralConfig CONFIG = GeneralConfig.getInstance();
|
||||
|
||||
private final JComboBox<CardTextLanguage> cboCardText = new JComboBox<>();
|
||||
private final JComboBox<DownloadMode> cboDownloadMode = new JComboBox<>();
|
||||
private final DirectoryChooser imagesFolderChooser;
|
||||
|
||||
OptionsPanel() {
|
||||
|
||||
imagesFolderChooser = getImagesFolderChooser();
|
||||
setDownloadModeCombo();
|
||||
|
||||
setLayout(new MigLayout("wrap 2, insets 0", "[right][]"));
|
||||
add(new JLabel(UiString.get("Images folder:")));
|
||||
add(imagesFolderChooser, "w 100%");
|
||||
add(new JLabel(UiString.get("Card text:")));
|
||||
add(getCardTextCombo());
|
||||
add(getBoldLabel(UiString.get("Download mode:")));
|
||||
add(cboDownloadMode);
|
||||
}
|
||||
|
||||
private JLabel getBoldLabel(String text) {
|
||||
final JLabel lbl = new JLabel(text);
|
||||
lbl.setFont(lbl.getFont().deriveFont(Font.BOLD));
|
||||
return lbl;
|
||||
}
|
||||
|
||||
private void saveSettings() {
|
||||
CONFIG.setCardTextLanguage(getCardTextLanguage());
|
||||
CONFIG.setCardImagesPath(imagesFolderChooser.getPath());
|
||||
CONFIG.save();
|
||||
}
|
||||
|
||||
private void doImageFolderChanged() {
|
||||
setEnabled(false);
|
||||
saveSettings();
|
||||
firePropertyChange(CP_OPTIONS_CHANGED, true, false);
|
||||
}
|
||||
|
||||
private DirectoryChooser getImagesFolderChooser() {
|
||||
final DirectoryChooser chooser = new DirectoryChooser(CONFIG.getCardImagesPath());
|
||||
chooser.setFocusable(false);
|
||||
chooser.addPropertyChangeListener(DirectoryChooser.CP_FOLDER_CHANGED, (e) -> {
|
||||
doImageFolderChanged();
|
||||
});
|
||||
return chooser;
|
||||
}
|
||||
|
||||
private void doTextLanguageChanged() {
|
||||
cboCardText.setToolTipText(getCardTextLanguage().isEnglish()
|
||||
? ""
|
||||
: "<html><b>Preferred Language</b><br>Downloads selected translation of card if available.</html>"
|
||||
);
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
private JComboBox<CardTextLanguage> getCardTextCombo() {
|
||||
cboCardText.setModel(new DefaultComboBoxModel<>(CardTextLanguage.values()));
|
||||
cboCardText.setSelectedItem(CONFIG.getCardTextLanguage());
|
||||
cboCardText.addItemListener((ItemEvent ev) -> {
|
||||
if (ev.getStateChange() == ItemEvent.SELECTED) {
|
||||
doTextLanguageChanged();
|
||||
}
|
||||
});
|
||||
return cboCardText;
|
||||
}
|
||||
|
||||
private void doDownloadModeChanged() {
|
||||
setEnabled(false);
|
||||
firePropertyChange(CP_OPTIONS_CHANGED, true, false);
|
||||
}
|
||||
|
||||
private void setDownloadModeCombo() {
|
||||
cboDownloadMode.setFont(cboDownloadMode.getFont().deriveFont(Font.BOLD));
|
||||
cboDownloadMode.setModel(new DefaultComboBoxModel<>(DownloadMode.values()));
|
||||
cboDownloadMode.getModel().setSelectedItem(cboDownloadMode.getItemAt(0));
|
||||
cboDownloadMode.addItemListener((final ItemEvent e) -> {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
doDownloadModeChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean b) {
|
||||
super.setEnabled(b);
|
||||
for (Component c : getComponents()) {
|
||||
c.setEnabled(b);
|
||||
}
|
||||
}
|
||||
|
||||
JComboBox<DownloadMode> getDownloadTypeCombo() {
|
||||
return cboDownloadMode;
|
||||
}
|
||||
|
||||
DownloadMode getDownloadMode() {
|
||||
return (DownloadMode) cboDownloadMode.getSelectedItem();
|
||||
}
|
||||
|
||||
CardTextLanguage getCardTextLanguage() {
|
||||
return (CardTextLanguage) cboCardText.getSelectedItem();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.stream.Stream;
|
||||
import magic.data.CardDefinitions;
|
||||
import magic.data.GeneralConfig;
|
||||
import magic.model.MagicCardDefinition;
|
||||
import magic.translate.UiString;
|
||||
import magic.ui.CardTextLanguage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class PlayablePanel extends DownloadPanel {
|
||||
|
||||
// translatable strings
|
||||
private static final String _S1 = "Playable";
|
||||
private static final String _S2 = "Download";
|
||||
|
||||
PlayablePanel(DownloadMode aMode, CardTextLanguage aLang, DialogMainPanel aPanel) {
|
||||
super(aMode, aLang, aPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProgressCaption() {
|
||||
return UiString.get(_S1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<MagicCardDefinition> getCards(final DownloadMode aType) {
|
||||
return aType == DownloadMode.ALL
|
||||
? CardDefinitions.getAllPlayableCardDefs().stream()
|
||||
: getCards(
|
||||
CardDefinitions.getAllPlayableCardDefs(),
|
||||
GeneralConfig.getInstance().getPlayableImagesDownloadDate()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDownloadButtonCaption() {
|
||||
return UiString.get(_S2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCustomActionAfterDownload() {
|
||||
GeneralConfig.getInstance().setPlayableImagesDownloadDate(new Date());
|
||||
GeneralConfig.getInstance().save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.swing.SwingWorker;
|
||||
import magic.data.ImagesDownloadList;
|
||||
|
||||
class ScanWorker extends SwingWorker<ImagesDownloadList, Void> {
|
||||
|
||||
private final IScanListener listener;
|
||||
private final DownloadMode downloadType;
|
||||
private ImagesDownloadList downloadList;
|
||||
|
||||
ScanWorker(final IScanListener aListener, final DownloadMode aType) {
|
||||
this.listener = aListener;
|
||||
this.downloadType = aType;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImagesDownloadList doInBackground() throws Exception {
|
||||
// Speeds up scanning by preventing multiple scanners
|
||||
// accessing filesystem at the same time.
|
||||
synchronized (ScanWorker.class) {
|
||||
return new ImagesDownloadList(listener.getCards(downloadType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
try {
|
||||
downloadList = get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (CancellationException ex) {
|
||||
System.out.println("ImagesScanner cancelled by user!");
|
||||
}
|
||||
if (!isCancelled()) {
|
||||
listener.doScannerFinished(downloadList);
|
||||
}
|
||||
listener.notifyStatusChanged(DownloadState.STOPPED);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package magic.ui.image.download;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.stream.Stream;
|
||||
import magic.data.CardDefinitions;
|
||||
import magic.data.GeneralConfig;
|
||||
import magic.model.MagicCardDefinition;
|
||||
import magic.translate.UiString;
|
||||
import magic.ui.CardTextLanguage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class UnimplementedPanel extends DownloadPanel {
|
||||
|
||||
// translatable strings
|
||||
private static final String _S1 = "Unimplemented";
|
||||
private static final String _S2 = "Download";
|
||||
|
||||
UnimplementedPanel(DownloadMode aMode, CardTextLanguage aLang, DialogMainPanel aPanel) {
|
||||
super(aMode, aLang, aPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProgressCaption() {
|
||||
return UiString.get(_S1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<MagicCardDefinition> getCards(final DownloadMode aType) {
|
||||
return aType == DownloadMode.ALL
|
||||
? CardDefinitions.getMissingCards().stream()
|
||||
: getCards(
|
||||
CardDefinitions.getMissingCards(),
|
||||
GeneralConfig.getInstance().getMissingImagesDownloadDate()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDownloadButtonCaption() {
|
||||
return UiString.get(_S2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doCustomActionAfterDownload() {
|
||||
GeneralConfig.getInstance().setMissingImagesDownloadDate(new Date());
|
||||
GeneralConfig.getInstance().save();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package magic.ui.screen;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import magic.ui.image.download.DownloadDialogPanel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DownloadImagesScreen extends AbstractScreen {
|
||||
|
||||
private final DownloadDialogPanel dialogPanel;
|
||||
|
||||
public DownloadImagesScreen() {
|
||||
dialogPanel = new DownloadDialogPanel();
|
||||
setContent(getContentPanel());
|
||||
}
|
||||
|
||||
private JPanel getContentPanel() {
|
||||
final JPanel panel = new JPanel(new MigLayout("alignx center, aligny center"));
|
||||
panel.setOpaque(false);
|
||||
panel.add(dialogPanel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isScreenReadyToClose(final AbstractScreen nextScreen) {
|
||||
return dialogPanel.isBusy() == false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package magic.ui.widget;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
@ -23,6 +25,8 @@ import net.miginfocom.swing.MigLayout;
|
|||
@SuppressWarnings("serial")
|
||||
public class DirectoryChooser extends JPanel implements MouseListener {
|
||||
|
||||
public static final String CP_FOLDER_CHANGED = "imageFolderUChanged";
|
||||
|
||||
// translatable strings
|
||||
private static final String _S1 = "Choose directory...";
|
||||
private static final String _S3 = "Not enough free space!";
|
||||
|
@ -34,7 +38,7 @@ public class DirectoryChooser extends JPanel implements MouseListener {
|
|||
private final MigLayout layout = new MigLayout();
|
||||
private final JTextField textField = new JTextField();
|
||||
private final JButton selectButton = new JButton();
|
||||
private final Path defaultPath;
|
||||
private Path defaultPath;
|
||||
|
||||
public DirectoryChooser(final Path defaultPath) {
|
||||
|
||||
|
@ -46,19 +50,29 @@ public class DirectoryChooser extends JPanel implements MouseListener {
|
|||
selectButton.addActionListener(new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final JFileChooser fileChooser = new ImagesDirectoryChooser(defaultPath.toString());
|
||||
final JFileChooser fileChooser = new ImagesDirectoryChooser(getDefaultPath().toString());
|
||||
final int action = fileChooser.showOpenDialog(ScreenController.getMainFrame());
|
||||
if (action == JFileChooser.APPROVE_OPTION) {
|
||||
final File f = fileChooser.getSelectedFile();
|
||||
if (MagicFileSystem.directoryContains(MagicFileSystem.INSTALL_PATH, f.toPath())) {
|
||||
textField.setText(MagicFileSystem.getDataPath(DataPath.IMAGES).toString());
|
||||
} else {
|
||||
textField.setText(f.toString());
|
||||
}
|
||||
setImagesPath(fileChooser.getSelectedFile());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private File getDefaultPath() {
|
||||
return defaultPath.toFile();
|
||||
}
|
||||
|
||||
private void setImagesPath(final File f) {
|
||||
if (!f.equals(getDefaultPath())) {
|
||||
if (MagicFileSystem.directoryContains(MagicFileSystem.INSTALL_PATH, f.toPath())) {
|
||||
textField.setText(MagicFileSystem.getDataPath(DataPath.IMAGES).toString());
|
||||
} else {
|
||||
textField.setText(f.toString());
|
||||
}
|
||||
firePropertyChange(CP_FOLDER_CHANGED, defaultPath.toFile(), f);
|
||||
defaultPath = f.toPath();
|
||||
}
|
||||
}
|
||||
|
||||
private void setLookAndFeel() {
|
||||
|
@ -67,6 +81,7 @@ public class DirectoryChooser extends JPanel implements MouseListener {
|
|||
textField.setText(defaultPath.toString());
|
||||
textField.setEditable(false);
|
||||
textField.addMouseListener(this);
|
||||
textField.setToolTipText("<html><b>Images folder</b><br>Double-click to open in the default file manager.");
|
||||
// JButton
|
||||
selectButton.setText("...");
|
||||
selectButton.setFont(FontsAndBorders.FONT1);
|
||||
|
@ -122,20 +137,24 @@ public class DirectoryChooser extends JPanel implements MouseListener {
|
|||
public void mousePressed(MouseEvent e) { }
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (e.getSource() == textField && e.getButton() == MouseEvent.BUTTON3) {
|
||||
if (e.getSource() == textField && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
try {
|
||||
DesktopUtils.openDirectory(textField.getText());
|
||||
} catch (IOException | IllegalArgumentException e1) {
|
||||
} catch (IOException | IllegalArgumentException e1) {
|
||||
ScreenController.showWarningMessage(e1.getMessage());
|
||||
}
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
dispatchEvent(e);
|
||||
}
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
|
||||
dispatchEvent(e);
|
||||
}
|
||||
|
||||
|
@ -147,4 +166,18 @@ public class DirectoryChooser extends JPanel implements MouseListener {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocusable(boolean b) {
|
||||
super.setFocusable(b);
|
||||
textField.setFocusable(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean b) {
|
||||
super.setEnabled(b);
|
||||
for (Component c : getComponents()) {
|
||||
c.setEnabled(b);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ public abstract class MissingImagesDownloadPanel extends ImageDownloadPanel {
|
|||
|
||||
@Override
|
||||
protected String doFileDownloadAndGetName(final DownloadableFile file, final Proxy proxy) throws IOException {
|
||||
file.download(proxy);
|
||||
file.doDownload(proxy);
|
||||
if (file instanceof CardImageFile) {
|
||||
return ((CardImageFile) file).getCardName();
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue