From edd4628984f5f06e955606651fc828ac839f7f43 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Fri, 24 Feb 2017 08:52:35 +0100 Subject: [PATCH] Version 1.2.2: fixes, export in UI - bugs in UI fixed (refresh of icons) - new "Save as..." option in UI - fix \t handling in resource files - LocalReader can now be used with --read --- README.md | 4 +- VERSION | 2 +- ...es.jar => nikiroo-utils-1.2.2-sources.jar} | Bin 46799 -> 46800 bytes src/be/nikiroo/fanfix/Main.java | 4 +- src/be/nikiroo/fanfix/bundles/Config.java | 2 +- src/be/nikiroo/fanfix/bundles/UiConfig.java | 8 +- .../nikiroo/fanfix/bundles/config.properties | 4 +- .../fanfix/bundles/resources.properties | 36 ++++---- src/be/nikiroo/fanfix/bundles/ui.properties | 10 ++- src/be/nikiroo/fanfix/reader/BasicReader.java | 6 +- src/be/nikiroo/fanfix/reader/CliReader.java | 6 +- src/be/nikiroo/fanfix/reader/LocalReader.java | 80 ++++++++++++++--- .../fanfix/reader/LocalReaderBook.java | 85 +++++++++++------- .../fanfix/reader/LocalReaderFrame.java | 51 ++++------- 14 files changed, 187 insertions(+), 111 deletions(-) rename libs/{nikiroo-utils-1.2.1-sources.jar => nikiroo-utils-1.2.2-sources.jar} (94%) diff --git a/README.md b/README.md index 4e21622..571de10 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Currently missing, but either in progress or planned: - [x] Make it run when no args passed - [x] Fix the UI, it is ugly - [x] Work on the UI thread is BAD - - [ ] Allow export + - [x] Allow export - [x] Allow delete/refresh - [ ] Show a list of types - [x] ..in the menu @@ -104,7 +104,7 @@ Currently missing, but either in progress or planned: - [x] Use it for most user ouput - [ ] Use it for all user output - [ ] French translation -- [ ] Allow lauching a custom application instead of Desktop.start ? +- [x] Allow lauching a custom application instead of Desktop.start ? - [ ] Make a wrapper for firefox to create a new, empty profile ? - [x] Install a mechanism to handle stories import/export progress update - [x] Progress system diff --git a/VERSION b/VERSION index 6085e94..23aa839 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.1 +1.2.2 diff --git a/libs/nikiroo-utils-1.2.1-sources.jar b/libs/nikiroo-utils-1.2.2-sources.jar similarity index 94% rename from libs/nikiroo-utils-1.2.1-sources.jar rename to libs/nikiroo-utils-1.2.2-sources.jar index 0b9eac3f3d1fa00ddc8d4c9c099577fe1641ed34..62e994f0d55053eaa7f4713b3963b5b4103c12cc 100644 GIT binary patch delta 954 zcmX^Amg&MdUArIc^DmkE?_=!0cd-+m zyWKi{`r^do4}Y$jD*sKmf93KU8Hr5;y&p z7{TZty%W8^2VK~3-z>P{gRkiSZJmn`G9B7>zrXhNKZUCi?bh0he)gF)>@%3X;-f!H z7Qg?jk9p~A&*DESi5}QFjV*27`~`E4PQH7PXa74jp8A5>7pnX9KF#7QQ!;0~$EB7W z|FiyOuQ-E@be*7XQq$KB>N0%oitFS!wGGre)Yh-@ITz>r`(@SUWwVxQiU;M!PWYK| zx$pHa)lZYRz5lWHM8>T%dr!2e*UX-)c=Ftwzx4r`(*Fg&{91P6&WjwIYg_Jp6L_a8 z?wG9ecyjJ-o8NhiS@Jb<%XuZmemfOj^PZag&SlMm1@$xkXdPN7AKxjh)wO*8@q=y3 z(sy@mNpfW~xc2sQc9EUk);0S-Zm=ru`=z$8WaoK@_PoU<+oJZbe))a2+#E*hcaJ?( zFZeE)E@x3CBPOFG$O!t~veq z>Mf=8?RlNjec#irW?Rph?4uK$A^-B}f?1j$dUo%4aE|R|S<9v4g=KSgvc6a~-Qd^H z6JD-u(<3KdS)?KB%KuI{R91DO$pQW)E7ih|*_7tonD2bSs+fOM2wUfr>DD%1<@|MD zxMoD`y7P}ez?+@p*tTlMOWX_$b-I%$*2!#^jyMqtN`;f3t+fErn-$l2GVubFqZeG^ zAaJdZqj0`pL}7H57XTuu%OjuVEYXGAVn!OG5#Bi>?`fPTk!?}8jR z(pp}QjvIq#udWg42l}R#X2-zc!)F_|m>zGA8=OvDUf)!vHcuL|w5Kbr*-V&l-H?RF zFEh@+d7TG>oAo095e@P`o8RUw%!f%UQ zx)*|meYTkZgm0<%H%%N377(`le&k#C2C2px+*uLLcC-QXh1o=nZz528aMFx+JM%C9 zdKakx&b2f!^ZW#*ES~HRW%yM$Vfcevgx))Jk=Jyv6S?W{RxlO&%1>^8ya3?X@JnbB z6RZb4;b1rnZ}m7>fjL1md1YmC_8MAG*OxVwEKRtOYU_f`N1JwB*DjHtvVF&IL`KtQ z=0zqv;oIyCNp|M!|AR`~_>Z8^H$~>gX5+D@^`?h#FSrOAVHuxj+veGK3sC#uvp8+Y zu-F%hvtF7Ub0=a2hs@-ER}psn_&VHBK|K9E4mKRw=+31TD>8twPM=?q^YN)={f&p! zS##7k@rBEM20C;brL)!gJx#yg^xq8FaUVx2fldOw^uVEu>0uD9ahD#xWaA};e=Weh z03*ItTH!0YAVr7}pQyxifb_h@QCFG0i7<@SF7P)i30ax|7G(F*_odb9sm1GE|f344=pvlx>;dk>TM zuMm?~v>E}ElX0|K0nn4ov`zt)lN+^50T+{JwLt+%lc}{)0Vk>;M1& diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index d8803a5..b989c68 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -35,8 +35,8 @@ public class Main { *
  • NOUTF: if set to 1 or 'true', the program will prefer non-unicode * {@link String}s when possible
  • *
  • CONFIG_DIR: a path where to look for the .properties files - * before taking the included ones; they will also be saved/updated into - * this path when the program starts
  • + * before taking the usual ones; they will also be saved/updated into this + * path when the program starts *
  • DEBUG: if set to 1 or 'true', the program will override the DEBUG_ERR * configuration value with 'true'
  • * diff --git a/src/be/nikiroo/fanfix/bundles/Config.java b/src/be/nikiroo/fanfix/bundles/Config.java index 18fda5a..ca35733 100644 --- a/src/be/nikiroo/fanfix/bundles/Config.java +++ b/src/be/nikiroo/fanfix/bundles/Config.java @@ -10,7 +10,7 @@ import be.nikiroo.utils.resources.Meta; 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)") LANG, // - @Meta(what = "reader type", where = "", format = "CLI or LOCAL", info = "Select the reader to use to read stories (CLI = simple output to console, LOCAL = use local system file handler)") + @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)") 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)") CACHE_DIR, // diff --git a/src/be/nikiroo/fanfix/bundles/UiConfig.java b/src/be/nikiroo/fanfix/bundles/UiConfig.java index dd2018d..d211947 100644 --- a/src/be/nikiroo/fanfix/bundles/UiConfig.java +++ b/src/be/nikiroo/fanfix/bundles/UiConfig.java @@ -11,9 +11,13 @@ public enum UiConfig { @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.reader' in the conig directory (usually $HOME/.fanfix)") CACHE_DIR_LOCAL_READER, // @Meta(what = "Output type", where = "Local Reader", format = "One of the known output type", info = "The type of output for the Local Reader for non-images documents") - LOCAL_READER_NON_IMAGES_DOCUMENT_TYPE, // + NON_IMAGES_DOCUMENT_TYPE, // @Meta(what = "Output type", where = "Local Reader", format = "One of the known output type", info = "The type of output for the Local Reader for images documents") - LOCAL_READER_IMAGES_DOCUMENT_TYPE, // + IMAGES_DOCUMENT_TYPE, // + @Meta(what = "Program", where = "Local Reader", format = "A command to start", info = "The command launched for images documents -- default to the system default for the current file type") + IMAGES_DOCUMENT_READER, // + @Meta(what = "Program", where = "Local Reader", format = "A command to start", info = "The command launched for non images documents -- default to the system default for the current file type") + NON_IMAGES_DOCUMENT_READER, // @Meta(what = "A background colour", where = "Local Reader Frame", format = "#rrggbb", info = "The background colour if you don't want the default system one") BACKGROUND_COLOR, // } diff --git a/src/be/nikiroo/fanfix/bundles/config.properties b/src/be/nikiroo/fanfix/bundles/config.properties index fa53914..0b1d0ac 100644 --- a/src/be/nikiroo/fanfix/bundles/config.properties +++ b/src/be/nikiroo/fanfix/bundles/config.properties @@ -6,7 +6,7 @@ # Force the language (can be overwritten again with the env variable $LANG) LANG = # (WHAT: reader type, FORMAT: CLI or LOCAL) -# Select the reader to use to read stories (CLI = simple output to console, LOCAL = use local system file handler) +# Select the default reader to use to read stories (CLI = simple output to console, LOCAL = use local system file handler) READER_TYPE = # (WHAT: directory, FORMAT: absolute path, $HOME variable supported, / is always accepted as dir separator) # The directory where to store temporary files, defaults to directory 'tmp' in the conig directory (usually $HOME/.fanfix) @@ -19,7 +19,7 @@ CACHE_MAX_TIME_CHANGING = 24 CACHE_MAX_TIME_STABLE = # (WHAT: 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; 79x24) +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) # The directory where to get the default story covers DEFAULT_COVERS_DIR = $HOME/bin/epub/ diff --git a/src/be/nikiroo/fanfix/bundles/resources.properties b/src/be/nikiroo/fanfix/bundles/resources.properties index baddc94..9b01475 100644 --- a/src/be/nikiroo/fanfix/bundles/resources.properties +++ b/src/be/nikiroo/fanfix/bundles/resources.properties @@ -10,14 +10,14 @@ # (WHAT: help message, WHERE: cli, FORMAT: %s = supported input, %s = supported output) # help message for the syntax HELP_SYNTAX = Valid options:\n\ -t--import [URL]: import into library\n\ -t--export [id] [output_type] [target]: export story to target\n\ -t--convert [URL] [output_type] [target] (+info): convert URL into target\n\ -t--read [id] ([chapter number]): read the given story from the library\n\ -t--read-url [URL] ([chapter number]): convert on the fly and read the story, without saving it\n\ -t--list: list the stories present in the library\n\ -t--set-reader [reader type]: set the reader type to CLI or LOCAL for this command\n\ -t--help: this help message\n\ +\t--import [URL]: import into library\n\ +\t--export [id] [output_type] [target]: export story to target\n\ +\t--convert [URL] [output_type] [target] (+info): convert URL into target\n\ +\t--read [id] ([chapter number]): read the given story from the library\n\ +\t--read-url [URL] ([chapter number]): convert on the fly and read the story, without saving it\n\ +\t--list: list the stories present in the library\n\ +\t--set-reader [reader type]: set the reader type to CLI or LOCAL for this command\n\ +\t--help: this help message\n\ \n\ Supported input types:\n\ %s\n\ @@ -82,11 +82,11 @@ INPUT_DESC_EPUB = EPUB files created by this program (we do not support "all" EP # (WHAT: input format description, WHERE: SupportType) # Description of this input type INPUT_DESC_TEXT = Support class for local stories encoded in textual format, with a few rules :\n\ -tthe title must be on the first line, \n\ -tthe author (preceded by nothing, "by " or "©") must be on the second line, possibly with the publication date in parenthesis (i.e., "By Unknown (3rd October 1998)"), \n\ -tchapters must be declared with "Chapter x" or "Chapter x: NAME OF THE CHAPTER", where "x" is the chapter number,\n\ -ta description of the story must be given as chapter number 0,\n\ -ta cover image may be present with the same filename but a PNG, JPEG or JPG extension. +\tthe title must be on the first line, \n\ +\tthe author (preceded by nothing, "by " or "©") must be on the second line, possibly with the publication date in parenthesis (i.e., "By Unknown (3rd October 1998)"), \n\ +\tchapters must be declared with "Chapter x" or "Chapter x: NAME OF THE CHAPTER", where "x" is the chapter number,\n\ +\ta description of the story must be given as chapter number 0,\n\ +\ta cover image may be present with the same filename but a PNG, JPEG or JPG extension. # (WHAT: input format description, WHERE: SupportType) # Description of this input type INPUT_DESC_INFO_TEXT = Contains the same information as the TEXT format, but with a companion ".info" file to store some metadata @@ -111,11 +111,11 @@ OUTPUT_DESC_EPUB = Standard EPUB file working on most e-book readers and viewers # (WHAT: output format description, WHERE: OutputType) # Description of this output type OUTPUT_DESC_TEXT = Local stories encoded in textual format, with a few rules :\n\ -tthe title must be on the first line, \n\ -tthe author (preceded by nothing, "by " or "©") must be on the second line, possibly with the publication date in parenthesis (i.e., "By Unknown (3rd October 1998)"), \n\ -tchapters must be declared with "Chapter x" or "Chapter x: NAME OF THE CHAPTER", where "x" is the chapter number,\n\ -ta description of the story must be given as chapter number 0,\n\ -ta cover image may be present with the same filename but a PNG, JPEG or JPG extension. +\tthe title must be on the first line, \n\ +\tthe author (preceded by nothing, "by " or "©") must be on the second line, possibly with the publication date in parenthesis (i.e., "By Unknown (3rd October 1998)"), \n\ +\tchapters must be declared with "Chapter x" or "Chapter x: NAME OF THE CHAPTER", where "x" is the chapter number,\n\ +\ta description of the story must be given as chapter number 0,\n\ +\ta cover image may be present with the same filename but a PNG, JPEG or JPG extension. # (WHAT: output format description, WHERE: OutputType) # Description of this output type OUTPUT_DESC_INFO_TEXT = Contains the same information as the TEXT format, but with a companion ".info" file to store some metadata diff --git a/src/be/nikiroo/fanfix/bundles/ui.properties b/src/be/nikiroo/fanfix/bundles/ui.properties index 8f72914..8c7428d 100644 --- a/src/be/nikiroo/fanfix/bundles/ui.properties +++ b/src/be/nikiroo/fanfix/bundles/ui.properties @@ -7,10 +7,16 @@ CACHE_DIR_LOCAL_READER = # (WHAT: Output type, WHERE: Local Reader, FORMAT: One of the known output type) # The type of output for the Local Reader for non-images documents -LOCAL_READER_NON_IMAGES_DOCUMENT_TYPE = HTML +NON_IMAGES_DOCUMENT_TYPE = HTML # (WHAT: Output type, WHERE: Local Reader, FORMAT: One of the known output type) # The type of output for the Local Reader for images documents -LOCAL_READER_IMAGES_DOCUMENT_TYPE = CBZ +IMAGES_DOCUMENT_TYPE = CBZ +# (WHAT: Program, WHERE: Local Reader, FORMAT: A command to start) +# The command launched for images documents -- default to the system default for the current file type +IMAGES_DOCUMENT_READER = +# (WHAT: Program, WHERE: Local Reader, FORMAT: A command to start) +# The command launched for non images documents -- default to the system default for the current file type +NON_IMAGES_DOCUMENT_READER = # (WHAT: A background colour, WHERE: Local Reader Frame, FORMAT: #rrggbb) # The background colour if you don't want the default system one BACKGROUND_COLOR = #FFFFFF diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index 0bcfd93..ae1c8d4 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -134,8 +134,12 @@ public abstract class BasicReader { * * @param chapter * the chapter + * + * @throws IOException + * in case of I/O error or if the {@link Story} was not + * previously set */ - public abstract void read(int chapter); + public abstract void read(int chapter) throws IOException; /** * Start the reader in browse mode for the given type (or pass NULL for all diff --git a/src/be/nikiroo/fanfix/reader/CliReader.java b/src/be/nikiroo/fanfix/reader/CliReader.java index f9e4b70..5dc2288 100644 --- a/src/be/nikiroo/fanfix/reader/CliReader.java +++ b/src/be/nikiroo/fanfix/reader/CliReader.java @@ -58,7 +58,11 @@ class CliReader extends BasicReader { } @Override - public void read(int chapter) { + public void read(int chapter) throws IOException { + if (getStory() == null) { + throw new IOException("No story to read"); + } + if (chapter > getStory().getChapters().size()) { System.err.println("Chapter " + chapter + ": no such chapter"); } else { diff --git a/src/be/nikiroo/fanfix/reader/LocalReader.java b/src/be/nikiroo/fanfix/reader/LocalReader.java index b434737..45fca8c 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReader.java +++ b/src/be/nikiroo/fanfix/reader/LocalReader.java @@ -1,5 +1,6 @@ package be.nikiroo.fanfix.reader; +import java.awt.Desktop; import java.awt.EventQueue; import java.io.File; import java.io.IOException; @@ -7,6 +8,7 @@ import java.io.IOException; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.Library; 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; @@ -22,29 +24,48 @@ class LocalReader extends BasicReader { "Cannote create cache directory for local reader: " + dir); } - // TODO: can throw an exception, manage that (convert to IOEx ?) - OutputType text = OutputType.valueOfNullOkUC(Instance.getUiConfig() - .getString(UiConfig.LOCAL_READER_NON_IMAGES_DOCUMENT_TYPE)); - if (text == null) { - text = OutputType.HTML; - } + OutputType text = null; + OutputType images = null; + + try { + text = OutputType.valueOfNullOkUC(Instance.getUiConfig().getString( + UiConfig.NON_IMAGES_DOCUMENT_TYPE)); + if (text == null) { + text = OutputType.HTML; + } + + images = OutputType.valueOfNullOkUC(Instance.getUiConfig() + .getString(UiConfig.IMAGES_DOCUMENT_TYPE)); + if (images == null) { + images = OutputType.CBZ; + } + } catch (Exception e) { + UiConfig key = (text == null) ? UiConfig.NON_IMAGES_DOCUMENT_TYPE + : UiConfig.IMAGES_DOCUMENT_TYPE; + String value = Instance.getUiConfig().getString(key); - OutputType images = OutputType.valueOfNullOkUC(Instance.getUiConfig() - .getString(UiConfig.LOCAL_READER_IMAGES_DOCUMENT_TYPE)); - if (images == null) { - images = OutputType.CBZ; + throw new IOException( + String.format( + "The configuration option %s is not valid: %s", + key, value), e); } - // lib = new Library(dir, text, images); } @Override public void read() throws IOException { + if (getStory() == null) { + throw new IOException("No story to read"); + } + + open(getStory().getMeta().getLuid(), null); } @Override - public void read(int chapter) { + public void read(int chapter) throws IOException { + // TODO: show a special page? + read(); } /** @@ -128,12 +149,47 @@ class LocalReader extends BasicReader { }); } + // refresh = delete from LocalReader cache (TODO: rename?) void refresh(String luid) { lib.delete(luid); } + // delete from main library void delete(String luid) { lib.delete(luid); Instance.getLibrary().delete(luid); } + + // open the given book + void open(String luid, Progress pg) throws IOException { + MetaData meta = Instance.getLibrary().getInfo(luid); + File target = getTarget(luid, pg); + + String program = null; + if (meta.isImageDocument()) { + program = Instance.getUiConfig().getString( + UiConfig.IMAGES_DOCUMENT_READER); + } else { + program = Instance.getUiConfig().getString( + UiConfig.NON_IMAGES_DOCUMENT_READER); + } + + if (program != null && program.trim().isEmpty()) { + program = null; + } + + if (program == null) { + try { + Desktop.getDesktop().browse(target.toURI()); + } catch (UnsupportedOperationException e) { + Runtime.getRuntime().exec( + new String[] { "xdg-open", target.getAbsolutePath() }); + + } + } else { + Runtime.getRuntime().exec( + new String[] { program, target.getAbsolutePath() }); + + } + } } diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderBook.java b/src/be/nikiroo/fanfix/reader/LocalReaderBook.java index 3b29e7f..d19b603 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReaderBook.java +++ b/src/be/nikiroo/fanfix/reader/LocalReaderBook.java @@ -89,7 +89,7 @@ class LocalReaderBook extends JPanel { private boolean cached; /** - * Create a new {@link LocalReaderBook} item for the givn {@link Story}. + * Create a new {@link LocalReaderBook} item for the given {@link Story}. * * @param meta * the story {@code}link MetaData} @@ -97,32 +97,16 @@ class LocalReaderBook extends JPanel { * TRUE if it is locally cached */ public LocalReaderBook(MetaData meta, boolean cached) { - this.luid = meta.getLuid(); this.cached = cached; - - BufferedImage resizedImage = new BufferedImage(SPINE_WIDTH - + COVER_WIDTH, SPINE_HEIGHT + COVER_HEIGHT + HOFFSET, - BufferedImage.TYPE_4BYTE_ABGR); - Graphics2D g = resizedImage.createGraphics(); - g.setColor(Color.white); - g.fillRect(0, HOFFSET, COVER_WIDTH, COVER_HEIGHT); - if (meta.getCover() != null) { - g.drawImage(meta.getCover(), 0, HOFFSET, COVER_WIDTH, COVER_HEIGHT, - null); - } else { - g.setColor(Color.black); - g.drawLine(0, HOFFSET, COVER_WIDTH, HOFFSET + COVER_HEIGHT); - g.drawLine(COVER_WIDTH, HOFFSET, 0, HOFFSET + COVER_HEIGHT); - } - g.dispose(); - - icon = new JLabel(new ImageIcon(resizedImage)); + luid = meta.getLuid(); String optAuthor = meta.getAuthor(); if (optAuthor != null && !optAuthor.isEmpty()) { optAuthor = "(" + optAuthor + ")"; } + icon = new JLabel(generateCoverIcon(meta.getCover())); + title = new JLabel( String.format( "" @@ -132,12 +116,11 @@ class LocalReaderBook extends JPanel { TEXT_WIDTH, TEXT_HEIGHT, meta.getTitle(), AUTHOR_COLOR, optAuthor)); - this.setLayout(new BorderLayout(10, 10)); - this.add(icon, BorderLayout.CENTER); - this.add(title, BorderLayout.SOUTH); + setLayout(new BorderLayout(10, 10)); + add(icon, BorderLayout.CENTER); + add(title, BorderLayout.SOUTH); setupListeners(); - setSelected(false); } /** @@ -156,8 +139,10 @@ class LocalReaderBook extends JPanel { * TRUE if it is selected */ public void setSelected(boolean selected) { - this.selected = selected; - repaint(); + if (this.selected != selected) { + this.selected = selected; + repaint(); + } } /** @@ -167,8 +152,10 @@ class LocalReaderBook extends JPanel { * TRUE if it is mouse-hovered */ private void setHovered(boolean hovered) { - this.hovered = hovered; - repaint(); + if (this.hovered != hovered) { + this.hovered = hovered; + repaint(); + } } /** @@ -268,19 +255,25 @@ class LocalReaderBook extends JPanel { public void setCached(boolean cached) { if (this.cached != cached) { this.cached = cached; - invalidate(); + repaint(); } } /** - * Draw a "cached" icon and a partially transparent overlay if needed - * depending upon the selection and mouse-hover states on top of the normal - * component. + * Paint the item, then call {@link LocalReaderBook#paintOverlay(Graphics)}. */ @Override public void paint(Graphics g) { super.paint(g); + paintOverlay(g); + } + /** + * Draw a partially transparent overlay if needed depending upon the + * selection and mouse-hover states on top of the normal component, as well + * as a possible "cached" icon if the item is cached. + */ + public void paintOverlay(Graphics g) { Rectangle clip = g.getClipBounds(); if (clip.getWidth() <= 0 || clip.getHeight() <= 0) { return; @@ -332,4 +325,32 @@ class LocalReaderBook extends JPanel { + HOFFSET + 30, 10, 20, 20); } } + + /** + * Generate a cover icon based upon the given cover image (which may be + * NULL). + * + * @param image + * the cover image, or NULL for none + * + * @return the icon + */ + private ImageIcon generateCoverIcon(BufferedImage image) { + BufferedImage resizedImage = new BufferedImage(SPINE_WIDTH + + COVER_WIDTH, SPINE_HEIGHT + COVER_HEIGHT + HOFFSET, + BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D g = resizedImage.createGraphics(); + g.setColor(Color.white); + g.fillRect(0, HOFFSET, COVER_WIDTH, COVER_HEIGHT); + if (image != null) { + g.drawImage(image, 0, HOFFSET, COVER_WIDTH, COVER_HEIGHT, null); + } else { + g.setColor(Color.black); + g.drawLine(0, HOFFSET, COVER_WIDTH, HOFFSET + COVER_HEIGHT); + g.drawLine(COVER_WIDTH, HOFFSET, 0, HOFFSET + COVER_HEIGHT); + } + g.dispose(); + + return new ImageIcon(resizedImage); + } } diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java index 985e104..16684f9 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java @@ -2,7 +2,6 @@ package be.nikiroo.fanfix.reader; import java.awt.BorderLayout; import java.awt.Color; -import java.awt.Desktop; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -113,6 +112,7 @@ class LocalReaderFrame extends JFrame { this.type = type; stories = Instance.getLibrary().getList(type); books.clear(); + bookPane.invalidate(); bookPane.removeAll(); for (MetaData meta : stories) { LocalReaderBook book = new LocalReaderBook(meta, @@ -210,7 +210,6 @@ class LocalReaderFrame extends JFrame { List tt = Instance.getLibrary().getTypes(); tt.add(0, null); for (final String type : tt) { - JMenuItem item = new JMenuItem(type == null ? "All books" : type); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { @@ -288,6 +287,15 @@ class LocalReaderFrame extends JFrame { return export; } + /** + * Create a {@link FileFilter} that accepts all files and return the given + * description. + * + * @param desc + * the description + * + * @return the filter + */ private FileFilter createAllFilter(final String desc) { return new FileFilter() { @Override @@ -387,36 +395,14 @@ class LocalReaderFrame extends JFrame { outOfUi(pg, new Runnable() { public void run() { try { - File target = LocalReaderFrame.this.reader.getTarget( - book.getLuid(), pg); - book.setCached(true); - // TODO: allow custom programs, with - // Desktop/xdg-open fallback - try { - Desktop.getDesktop().browse(target.toURI()); - } catch (UnsupportedOperationException e) { - String browsers[] = new String[] { "xdg-open", - "epiphany", "konqueror", "firefox", "chrome", - "google-chrome", "mozilla" }; - - Runtime runtime = Runtime.getRuntime(); - for (String browser : browsers) { - try { - runtime.exec(new String[] { browser, - target.getAbsolutePath() }); - runtime = null; - break; - } catch (IOException ioe) { - // continue, try next browser - } - } - - if (runtime != null) { - throw new IOException( - "Cannot find a working GUI browser..."); + reader.open(book.getLuid(), pg); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + book.setCached(true); } - } + }); } catch (IOException e) { + // TODO: error message? Instance.syserr(e); } } @@ -509,8 +495,6 @@ class LocalReaderFrame extends JFrame { "Cannot import: " + url, e.getMessage(), JOptionPane.ERROR_MESSAGE); - - setEnabled(true); } else { refreshBooks(type); } @@ -536,17 +520,14 @@ class LocalReaderFrame extends JFrame { public void setEnabled(boolean b) { for (LocalReaderBook book : books) { book.setEnabled(b); - book.validate(); book.repaint(); } bar.setEnabled(b); bookPane.setEnabled(b); - bookPane.validate(); bookPane.repaint(); super.setEnabled(b); - validate(); repaint(); } } -- 2.27.0