From b42117f163c9c0dd96424d43730fb95fb088b4be Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 5 Mar 2017 13:18:30 +0100 Subject: [PATCH] Version 1.4.0 - Remember the word count and the date of creation of Fanfix stories - UI: option to show the word count instead of the author below the book title - CBZ: do not include the first page twice anymore for no-cover websites - UI: update version check (we now check for new versions) --- VERSION | 2 +- changelog.md | 3 +- ...es.jar => nikiroo-utils-1.3.4-sources.jar} | Bin 49423 -> 49880 bytes src/be/nikiroo/fanfix/Instance.java | 95 ++++++++---- src/be/nikiroo/fanfix/Main.java | 22 +++ src/be/nikiroo/fanfix/VersionCheck.java | 140 ++++++++++++++++++ src/be/nikiroo/fanfix/bundles/Config.java | 42 +++--- .../nikiroo/fanfix/bundles/config.properties | 46 +++--- src/be/nikiroo/fanfix/reader/LocalReader.java | 62 ++++++++ 9 files changed, 340 insertions(+), 72 deletions(-) rename libs/{nikiroo-utils-1.3.3-sources.jar => nikiroo-utils-1.3.4-sources.jar} (74%) create mode 100644 src/be/nikiroo/fanfix/VersionCheck.java diff --git a/VERSION b/VERSION index 3a3cd8c..88c5fb8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.1 +1.4.0 diff --git a/changelog.md b/changelog.md index 20b5870..1e638e3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,11 @@ # Fanfix -## Version (in progress) +## Version 1.4.0 - Remember the word count and the date of creation of Fanfix stories - UI: option to show the word count instead of the author below the book title - CBZ: do not include the first page twice anymore for no-cover websites +- UI: update version check (we now check for new versions) ## Version 1.3.1 diff --git a/libs/nikiroo-utils-1.3.3-sources.jar b/libs/nikiroo-utils-1.3.4-sources.jar similarity index 74% rename from libs/nikiroo-utils-1.3.3-sources.jar rename to libs/nikiroo-utils-1.3.4-sources.jar index a7c9ff1ee16247db7dc91a0088ee1b5b11a15ca5..f4b5883d12bbb6a81149ad8861d192e62f89412d 100644 GIT binary patch delta 4863 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 IS~KeJe;RZm6aWAK delta 4485 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 diff --git a/src/be/nikiroo/fanfix/Instance.java b/src/be/nikiroo/fanfix/Instance.java index fdd73b8..1266ac7 100644 --- a/src/be/nikiroo/fanfix/Instance.java +++ b/src/be/nikiroo/fanfix/Instance.java @@ -2,6 +2,7 @@ package be.nikiroo.fanfix; import java.io.File; import java.io.IOException; +import java.util.Date; import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.bundles.ConfigBundle; @@ -9,6 +10,7 @@ import be.nikiroo.fanfix.bundles.StringIdBundle; import be.nikiroo.fanfix.bundles.UiConfig; import be.nikiroo.fanfix.bundles.UiConfigBundle; import be.nikiroo.fanfix.output.BasicOutput.OutputType; +import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.resources.Bundles; /** @@ -25,49 +27,49 @@ public class Instance { private static boolean debug; private static File coverDir; private static File readerTmp; + private static String configDir; static { // Most of the rest is dependent upon this: config = new ConfigBundle(); - String configDir = System.getProperty("CONFIG_DIR"); + configDir = System.getProperty("CONFIG_DIR"); if (configDir == null) { configDir = System.getenv("CONFIG_DIR"); } + if (configDir == null) { configDir = new File(System.getProperty("user.home"), ".fanfix") .getPath(); } - if (configDir != null) { - if (!new File(configDir).exists()) { - new File(configDir).mkdirs(); - } else { - Bundles.setDirectory(configDir); - } - - try { - config = new ConfigBundle(); - config.updateFile(configDir); - } catch (IOException e) { - syserr(e); - } - try { - uiconfig = new UiConfigBundle(); - uiconfig.updateFile(configDir); - } catch (IOException e) { - syserr(e); - } - try { - trans = new StringIdBundle(getLang()); - trans.updateFile(configDir); - } catch (IOException e) { - syserr(e); - } - + if (!new File(configDir).exists()) { + new File(configDir).mkdirs(); + } else { Bundles.setDirectory(configDir); } + try { + config = new ConfigBundle(); + config.updateFile(configDir); + } catch (IOException e) { + syserr(e); + } + try { + uiconfig = new UiConfigBundle(); + uiconfig.updateFile(configDir); + } catch (IOException e) { + syserr(e); + } + try { + trans = new StringIdBundle(getLang()); + trans.updateFile(configDir); + } catch (IOException e) { + syserr(e); + } + + Bundles.setDirectory(configDir); + uiconfig = new UiConfigBundle(); trans = new StringIdBundle(getLang()); try { @@ -183,6 +185,45 @@ public class Instance { return readerTmp; } + /** + * Check if we need to check that a new version of Fanfix is available. + * + * @return TRUE if we need to + */ + public static boolean isVersionCheckNeeded() { + try { + long wait = config.getInteger(Config.UPDATE_INTERVAL, 1) * 24 * 60 * 60; + if (wait >= 0) { + String lastUpString = IOUtils.readSmallFile(new File(configDir, + "LAST_UPDATE")); + long delay = new Date().getTime() + - Long.parseLong(lastUpString); + if (delay > wait) { + return true; + } + } else { + return false; + } + } catch (Exception e) { + // No file or bad file: + return true; + } + + return false; + } + + /** + * Notify that we checked for a new version of Fanfix. + */ + public static void setVersionChecked() { + try { + IOUtils.writeSmallFile(new File(configDir), "LAST_UPDATE", + Long.toString(new Date().getTime())); + } catch (IOException e) { + syserr(e); + } + } + /** * Report an error to the user * diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index 06287f1..85d7492 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -189,17 +189,38 @@ public class Main { Progress pg = new Progress(); mainProgress.addProgress(pg, mainProgress.getMax()); + VersionCheck updates = VersionCheck.check(); + if (updates.isNewVersionAvailable()) { + // Sent to syserr so not to cause problem if one tries to capture a + // story content in text mode + System.err + .println("A new version of the program is available at https://github.com/nikiroo/fanfix/releases"); + System.err.println(""); + for (Version v : updates.getNewer()) { + System.err.println("\tVersion " + v); + System.err.println("\t-------------"); + System.err.println(""); + for (String item : updates.getChanges().get(v)) { + System.err.println("\t- " + item); + } + System.err.println(""); + } + } + if (exitCode != 255) { switch (action) { case IMPORT: exitCode = imprt(urlString, pg); + updates.ok(); // we consider it read break; case EXPORT: exitCode = export(luid, typeString, target, pg); + updates.ok(); // we consider it read break; case CONVERT: exitCode = convert(urlString, typeString, target, plusInfo == null ? false : plusInfo, pg); + updates.ok(); // we consider it read break; case LIST: exitCode = list(typeString); @@ -222,6 +243,7 @@ public class Main { + "\nhttps://github.com/nikiroo/fanfix/" + "\n\tWritten by Nikiroo", Version.getCurrentVersion())); + updates.ok(); // we consider it read break; case START: UIUtils.setLookAndFeel(); diff --git a/src/be/nikiroo/fanfix/VersionCheck.java b/src/be/nikiroo/fanfix/VersionCheck.java new file mode 100644 index 0000000..3359bac --- /dev/null +++ b/src/be/nikiroo/fanfix/VersionCheck.java @@ -0,0 +1,140 @@ +package be.nikiroo.fanfix; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import be.nikiroo.utils.Version; + +public class VersionCheck { + private static final String url = "https://github.com/nikiroo/fanfix/raw/master/changelog.md"; + + private Version current; + private List newer; + private Map> changes; + + /** + * Create a new {@link VersionCheck}. + * + * @param current + * the current version of the program + * @param newer + * the list of available {@link Version}s newer the current one + * @param changes + * the list of changes + */ + private VersionCheck(Version current, List newer, + Map> changes) { + this.current = current; + this.newer = newer; + this.changes = changes; + } + + /** + * Check if there are more recent {@link Version}s of this program + * available. + * + * @return TRUE if there is at least one + */ + public boolean isNewVersionAvailable() { + return !newer.isEmpty(); + } + + /** + * The current {@link Version} of the program. + * + * @return the current {@link Version} + */ + public Version getCurrentVersion() { + return current; + } + + /** + * The list of available {@link Version}s newer than the current one. + * + * @return the newer {@link Version}s + */ + public List getNewer() { + return newer; + } + + /** + * The list of changes for each available {@link Version} newer than the + * current one. + * + * @return the list of changes + */ + public Map> getChanges() { + return changes; + } + + /** + * Ignore the check result. + */ + public void ignore() { + + } + + /** + * Accept the information, and do not check again until the minimum wait + * time has elapsed. + */ + public void ok() { + Instance.setVersionChecked(); + } + + /** + * Check if there are available {@link Version}s of this program more recent + * than the current one. + * + * @return a {@link VersionCheck} + */ + public static VersionCheck check() { + Version current = Version.getCurrentVersion(); + List newer = new ArrayList(); + Map> changes = new HashMap>(); + + if (Instance.isVersionCheckNeeded()) { + try { + InputStream in = Instance.getCache().openNoCache(new URL(url), + null); + BufferedReader reader = new BufferedReader( + new InputStreamReader(in, "UTF-8")); + try { + for (String line = reader.readLine(); line != null; line = reader + .readLine()) { + if (line.startsWith("## Version ")) { + String v = line.substring("## Version ".length()); + Version version = new Version(v); + if (version.isNewerThan(current)) { + newer.add(version); + changes.put(version, new ArrayList()); + } + } else if (!newer.isEmpty() && !line.isEmpty()) { + Version version = newer.get(newer.size() - 1); + List ch = changes.get(version); + if (!ch.isEmpty() && !line.startsWith("- ")) { + int i = ch.size() - 1; + ch.set(i, ch.get(i) + " " + line.trim()); + } else { + ch.add(line.substring("- ".length()).trim()); + } + } + } + } finally { + reader.close(); + } + } catch (IOException e) { + Instance.syserr(e); + } + } + + return new VersionCheck(current, newer, changes); + } +} diff --git a/src/be/nikiroo/fanfix/bundles/Config.java b/src/be/nikiroo/fanfix/bundles/Config.java index b68265e..6a728c4 100644 --- a/src/be/nikiroo/fanfix/bundles/Config.java +++ b/src/be/nikiroo/fanfix/bundles/Config.java @@ -8,44 +8,46 @@ import be.nikiroo.utils.resources.Meta; * @author niki */ public enum Config { - @Meta(what = "language", where = "", format = "language (example: en-GB) or nothing for default system language", info = "Force the language (can be overwritten again with the env variable $LANG)") + @Meta(what = "language (example: en-GB, fr-BE...) or nothing for default system language", where = "", format = "Locale|''", info = "Force the language (can be overwritten again with the env variable $LANG)") LANG, // - @Meta(what = "reader type", where = "", format = "CLI or LOCAL", info = "Select the default reader to use to read stories (CLI = simple output to console, LOCAL = use local system file handler)") + @Meta(what = "reader type (CLI = simple output to console, LOCAL = use local system file handler)", where = "", format = "'CLI'|'LOCAL'", info = "Select the default reader to use to read stories") READER_TYPE, // - @Meta(what = "directory", where = "", format = "absolute path, $HOME variable supported, / is always accepted as dir separator", info = "The directory where to store temporary files, defaults to directory 'tmp' in the conig directory (usually $HOME/.fanfix)") + @Meta(what = "absolute path, $HOME variable supported, / is always accepted as dir separator", where = "", format = "Directory", info = "The directory where to store temporary files, defaults to directory 'tmp' in the conig directory (usually $HOME/.fanfix)") CACHE_DIR, // - @Meta(what = "delay in hours", where = "", format = "integer | 0: no cache | -1: infinite time cache which is default", info = "The delay after which a cached resource that is thought to change ~often is considered too old and triggers a refresh") + @Meta(what = "delay in hours, or 0 for no cache, or -1 for infinite time (default)", where = "", format = "int", info = "The delay after which a cached resource that is thought to change ~often is considered too old and triggers a refresh") CACHE_MAX_TIME_CHANGING, // - @Meta(what = "delay in hours", where = "", format = "integer | 0: no cache | -1: infinite time cache which is default", info = "The delay after which a cached resource that is thought to change rarely is considered too old and triggers a refresh") + @Meta(what = "delay in hours, or 0 for no cache, or -1 for infinite time (default)", where = "", format = "int", info = "The delay after which a cached resource that is thought to change rarely is considered too old and triggers a refresh") CACHE_MAX_TIME_STABLE, // - @Meta(what = "string", where = "", format = "", info = "The user-agent to use to download files") + @Meta(what = "string", where = "", format = "String", info = "The user-agent to use to download files") USER_AGENT, // - @Meta(what = "directory", where = "", format = "absolute path, $HOME variable supported, / is always accepted as dir separator", info = "The directory where to get the default story covers") + @Meta(what = "absolute path, $HOME variable supported, / is always accepted as dir separator", where = "", format = "Directory", info = "The directory where to get the default story covers") DEFAULT_COVERS_DIR, // - @Meta(what = "directory", where = "", format = "absolute path, $HOME variable supported, / is always accepted as dir separator", info = "The directory where to store the library") + @Meta(what = "absolute path, $HOME variable supported, / is always accepted as dir separator", where = "", format = "Directory", info = "The directory where to store the library") LIBRARY_DIR, // - @Meta(what = "boolean", where = "", format = "'true' or 'false'", info = "Show debug information on errors") + @Meta(what = "boolean", where = "", format = "'true'|'false'", info = "Show debug information on errors") DEBUG_ERR, // - @Meta(what = "image format", where = "", format = "PNG, JPG, BMP...", info = "Image format to use for cover images") + @Meta(what = "image format", where = "", format = "'PNG'|JPG'|'BMP'", info = "Image format to use for cover images") IMAGE_FORMAT_COVER, // - @Meta(what = "image format", where = "", format = "PNG, JPG, BMP...", info = "Image format to use for content images") + @Meta(what = "image format", where = "", format = "'PNG'|JPG'|'BMP'", info = "Image format to use for content images") IMAGE_FORMAT_CONTENT, // - @Meta(what = "", where = "", format = "not used", info = "This key is only present to allow access to suffixes") + // This key is only present to allow access to suffixes, so no Meta LATEX_LANG, // - @Meta(what = "LaTeX output language", where = "LaTeX", format = "", info = "LaTeX full name for English") + @Meta(what = "LaTeX output language", where = "LaTeX", format = "String", info = "LaTeX full name for English") LATEX_LANG_EN, // - @Meta(what = "LaTeX output language", where = "LaTeX", format = "", info = "LaTeX full name for French") + @Meta(what = "LaTeX output language", where = "LaTeX", format = "String", info = "LaTeX full name for French") LATEX_LANG_FR, // - @Meta(what = "other 'by' prefixes before author name", where = "", format = "comma-separated list", info = "used to identify the author") + @Meta(what = "other 'by' prefixes before author name", where = "", format = "comma-separated list|String", info = "used to identify the author") BYS, // - @Meta(what = "Chapter identification languages", where = "", format = "comma-separated list", info = "used to identify a starting chapter in text mode") + @Meta(what = "Chapter identification languages", where = "", format = "comma-separated list|String", info = "used to identify a starting chapter in text mode") CHAPTER, // - @Meta(what = "Chapter identification string", where = "", format = "", info = "used to identify a starting chapter in text mode") + @Meta(what = "Chapter identification string", where = "String", format = "", info = "used to identify a starting chapter in text mode") CHAPTER_EN, // - @Meta(what = "Chapter identification string", where = "", format = "", info = "used to identify a starting chapter in text mode") + @Meta(what = "Chapter identification string", where = "String", format = "", info = "used to identify a starting chapter in text mode") CHAPTER_FR, // - @Meta(what = "Login information", where = "", format = "", info = "used to login on YiffStar to have access to all the stories (should not be necessary anymore)") + @Meta(what = "Login information", where = "", format = "String", info = "used to login on YiffStar to have access to all the stories (should not be necessary anymore)") LOGIN_YIFFSTAR_USER, // - @Meta(what = "Login information", where = "", format = "", info = "used to login on YiffStar to have access to all the stories (should not be necessary anymore)") + @Meta(what = "Login information", where = "", format = "Password", info = "used to login on YiffStar to have access to all the stories (should not be necessary anymore)") LOGIN_YIFFSTAR_PASS, // + @Meta(what = "Minimum time between version update checks in days, or -1 for 'no checks' -- default is 1 day", where = "VersionCheck", format = "int", info = "If the last update check was done at least that many days, check for updates at startup") + UPDATE_INTERVAL, } diff --git a/src/be/nikiroo/fanfix/bundles/config.properties b/src/be/nikiroo/fanfix/bundles/config.properties index 3f8345f..2fa3d32 100644 --- a/src/be/nikiroo/fanfix/bundles/config.properties +++ b/src/be/nikiroo/fanfix/bundles/config.properties @@ -2,63 +2,63 @@ # -# (WHAT: language, FORMAT: language (example: en-GB) or nothing for default system language) +# (WHAT: language (example: en-GB, fr-BE...) or nothing for default system language, FORMAT: Locale|'') # Force the language (can be overwritten again with the env variable $LANG) LANG = -# (WHAT: reader type, FORMAT: CLI or LOCAL) -# Select the default reader to use to read stories (CLI = simple output to console, LOCAL = use local system file handler) +# (WHAT: reader type (CLI = simple output to console, LOCAL = use local system file handler), FORMAT: 'CLI'|'LOCAL') +# Select the default reader to use to read stories READER_TYPE = -# (WHAT: directory, FORMAT: absolute path, $HOME variable supported, / is always accepted as dir separator) +# (WHAT: absolute path, $HOME variable supported, / is always accepted as dir separator, FORMAT: Directory) # The directory where to store temporary files, defaults to directory 'tmp' in the conig directory (usually $HOME/.fanfix) CACHE_DIR = -# (WHAT: delay in hours, FORMAT: integer | 0: no cache | -1: infinite time cache which is default) +# (WHAT: delay in hours, or 0 for no cache, or -1 for infinite time (default), FORMAT: int) # The delay after which a cached resource that is thought to change ~often is considered too old and triggers a refresh CACHE_MAX_TIME_CHANGING = 24 -# (WHAT: delay in hours, FORMAT: integer | 0: no cache | -1: infinite time cache which is default) +# (WHAT: delay in hours, or 0 for no cache, or -1 for infinite time (default), FORMAT: int) # The delay after which a cached resource that is thought to change rarely is considered too old and triggers a refresh CACHE_MAX_TIME_STABLE = -# (WHAT: string) +# (WHAT: string, FORMAT: String) # The user-agent to use to download files USER_AGENT = Mozilla/5.0 (X11; Linux x86_64; rv:44.0) Gecko/20100101 Firefox/44.0 -- ELinks/0.9.3 (Linux 2.6.11 i686; 80x24) -# (WHAT: directory, FORMAT: absolute path, $HOME variable supported, / is always accepted as dir separator) +# (WHAT: absolute path, $HOME variable supported, / is always accepted as dir separator, FORMAT: Directory) # The directory where to get the default story covers DEFAULT_COVERS_DIR = $HOME/bin/epub/ -# (WHAT: directory, FORMAT: absolute path, $HOME variable supported, / is always accepted as dir separator) +# (WHAT: absolute path, $HOME variable supported, / is always accepted as dir separator, FORMAT: Directory) # The directory where to store the library LIBRARY_DIR = $HOME/Books -# (WHAT: boolean, FORMAT: 'true' or 'false') +# (WHAT: boolean, FORMAT: 'true'|'false') # Show debug information on errors DEBUG_ERR = false -# (WHAT: image format, FORMAT: PNG, JPG, BMP...) +# (WHAT: image format, FORMAT: 'PNG'|JPG'|'BMP') # Image format to use for cover images IMAGE_FORMAT_COVER = png -# (WHAT: image format, FORMAT: PNG, JPG, BMP...) +# (WHAT: image format, FORMAT: 'PNG'|JPG'|'BMP') # Image format to use for content images IMAGE_FORMAT_CONTENT = png -# (FORMAT: not used) -# This key is only present to allow access to suffixes -LATEX_LANG = -# (WHAT: LaTeX output language, WHERE: LaTeX) +# (WHAT: LaTeX output language, WHERE: LaTeX, FORMAT: String) # LaTeX full name for English LATEX_LANG_EN = english -# (WHAT: LaTeX output language, WHERE: LaTeX) +# (WHAT: LaTeX output language, WHERE: LaTeX, FORMAT: String) # LaTeX full name for French LATEX_LANG_FR = french -# (WHAT: other 'by' prefixes before author name, FORMAT: comma-separated list) +# (WHAT: other 'by' prefixes before author name, FORMAT: comma-separated list|String) # used to identify the author BYS = by,par,de,©,(c) -# (WHAT: Chapter identification languages, FORMAT: comma-separated list) +# (WHAT: Chapter identification languages, FORMAT: comma-separated list|String) # used to identify a starting chapter in text mode CHAPTER = EN,FR -# (WHAT: Chapter identification string) +# (WHAT: Chapter identification string, WHERE: String) # used to identify a starting chapter in text mode CHAPTER_EN = Chapter -# (WHAT: Chapter identification string) +# (WHAT: Chapter identification string, WHERE: String) # used to identify a starting chapter in text mode CHAPTER_FR = Chapitre -# (WHAT: Login information) +# (WHAT: Login information, FORMAT: String) # used to login on YiffStar to have access to all the stories (should not be necessary anymore) LOGIN_YIFFSTAR_USER = -# (WHAT: Login information) +# (WHAT: Login information, FORMAT: Password) # used to login on YiffStar to have access to all the stories (should not be necessary anymore) LOGIN_YIFFSTAR_PASS = +# (WHAT: Minimum time between version update checks in days, or -1 for 'no checks' -- default is 1 day, WHERE: VersionCheck, FORMAT: int) +# If the last update check was done at least that many days, check for updates at startup +UPDATE_INTERVAL = diff --git a/src/be/nikiroo/fanfix/reader/LocalReader.java b/src/be/nikiroo/fanfix/reader/LocalReader.java index 45fca8c..b031de8 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReader.java +++ b/src/be/nikiroo/fanfix/reader/LocalReader.java @@ -4,14 +4,23 @@ import java.awt.Desktop; import java.awt.EventQueue; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; + +import javax.swing.JEditorPane; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.Library; +import be.nikiroo.fanfix.VersionCheck; import be.nikiroo.fanfix.bundles.UiConfig; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.utils.Progress; +import be.nikiroo.utils.Version; class LocalReader extends BasicReader { private Library lib; @@ -140,9 +149,62 @@ class LocalReader extends BasicReader { @Override public void start(String type) { + // TODO: improve presentation of update message + final VersionCheck updates = VersionCheck.check(); + StringBuilder builder = new StringBuilder(); + + final JEditorPane updateMessage = new JEditorPane("text/html", ""); + if (updates.isNewVersionAvailable()) { + builder.append("A new version of the program is available at https://github.com/nikiroo/fanfix/releases"); + builder.append("
"); + builder.append("
"); + for (Version v : updates.getNewer()) { + builder.append("\tVersion " + v + ""); + builder.append("
"); + builder.append("
    "); + for (String item : updates.getChanges().get(v)) { + builder.append("
  • " + item + "
  • "); + } + builder.append("
"); + } + + // html content + updateMessage.setText("" // + + builder// + + ""); + + // handle link events + updateMessage.addHyperlinkListener(new HyperlinkListener() { + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType().equals( + HyperlinkEvent.EventType.ACTIVATED)) + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (IOException ee) { + Instance.syserr(ee); + } catch (URISyntaxException ee) { + Instance.syserr(ee); + } + } + }); + updateMessage.setEditable(false); + updateMessage.setBackground(new JLabel().getBackground()); + } + final String typeFinal = type; EventQueue.invokeLater(new Runnable() { public void run() { + if (updates.isNewVersionAvailable()) { + int rep = JOptionPane.showConfirmDialog(null, + updateMessage, "Updates available", + JOptionPane.OK_CANCEL_OPTION); + if (rep == JOptionPane.OK_OPTION) { + updates.ok(); + } else { + updates.ignore(); + } + } + new LocalReaderFrame(LocalReader.this, typeFinal) .setVisible(true); } -- 2.27.0