From 333f0e7b5e333e8f6222881ce35398f403fc4121 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Tue, 14 Feb 2017 20:33:28 +0100 Subject: [PATCH] Lot of fixes + first (bad, ugly) working GUI The GUI is now launched at startup if no arguments were passed, too --- README.md | 27 ++- VERSION | 2 +- src/be/nikiroo/fanfix/Library.java | 23 ++- src/be/nikiroo/fanfix/Main.java | 37 ++-- src/be/nikiroo/fanfix/output/Html.java | 114 ++++++++++-- src/be/nikiroo/fanfix/output/html.style.css | 105 +++++++++++ src/be/nikiroo/fanfix/reader/BasicReader.java | 20 ++- src/be/nikiroo/fanfix/reader/CliReader.java | 3 +- src/be/nikiroo/fanfix/reader/LocalReader.java | 9 +- .../fanfix/reader/LocalReaderBook.java | 163 ++++++++++++++++++ .../fanfix/reader/LocalReaderFrame.java | 118 +++++++++++-- .../fanfix/supported/BasicSupport.java | 24 +-- .../nikiroo/fanfix/supported/Fanfiction.java | 83 +++++---- .../nikiroo/fanfix/supported/Fimfiction.java | 2 +- .../nikiroo/fanfix/supported/InfoReader.java | 9 +- src/be/nikiroo/fanfix/supported/Text.java | 2 +- 16 files changed, 617 insertions(+), 124 deletions(-) create mode 100644 src/be/nikiroo/fanfix/output/html.style.css create mode 100644 src/be/nikiroo/fanfix/reader/LocalReaderBook.java diff --git a/README.md b/README.md index 92f5929..6e2d8b0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ To help organize your stories, it can also work as a local library. In this mode, you can: - Import a story from its URL (or even another file) - Export a story to a file (.epub or .cbz) -- Display a story from the local library +- Display a story from the local library in text format in the console +- Display a story from the local library graphically **by calling a native program to handle it** (though Fanfix can automatically process it into HTML so any browser can open it) ## Supported websites @@ -81,7 +82,23 @@ Note that calling ```make libs``` will export the libraries into the src/ direct ## TODO Currently missing, but either in progress or planned: -- A GUI (work in progress) -- Some readers other than CLI (TUI, GUI) -- Check if it can work on Android -- French translation +- [ ] A GUI (work in progress) + - [x] Make one + - [x] Make it run when no args passed + - [ ] Fix the UI, it is ugly + - [ ] Work on the UI thread is BAD + - [ ] Allow export + - [ ] Show a list of types + - [x] ..in the menu + - [ ] ..as a screen view + - options screen +- [ ] A TUI reader +- [ ] Check if it can work on Android +- [ ] Translations + - [x] i18n system in place + - [x] Make use of it + - [x] Use it for all user output (some WIP remains) + - [ ] French translation +- [ ] Allow lauching a custom application instead of Desktop.star ? + - [ ] Make a wrapper for firefox to create a new, empty profile ? + diff --git a/VERSION b/VERSION index 2003b63..965065d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.2 +0.9.3 diff --git a/src/be/nikiroo/fanfix/Library.java b/src/be/nikiroo/fanfix/Library.java index 15b81e4..b5f246c 100644 --- a/src/be/nikiroo/fanfix/Library.java +++ b/src/be/nikiroo/fanfix/Library.java @@ -52,6 +52,23 @@ public class Library { dir.mkdirs(); } + /** + * List all the known types of stories. + * + * @return the types + */ + public List getTypes() { + List list = new ArrayList(); + for (Entry entry : getStories().entrySet()) { + String storyType = entry.getValue().getParentFile().getName(); + if (!list.contains(storyType)) { + list.add(storyType); + } + } + + return list; + } + /** * List all the stories of the given source type in the {@link Library}, or * all the stories if NULL is passed as a type. @@ -61,13 +78,11 @@ public class Library { * * @return the stories */ - public List getList(SupportType type) { - String typeString = type == null ? null : type.getSourceName(); - + public List getList(String type) { List list = new ArrayList(); for (Entry entry : getStories().entrySet()) { String storyType = entry.getValue().getParentFile().getName(); - if (typeString == null || typeString.equalsIgnoreCase(storyType)) { + if (type == null || type.equalsIgnoreCase(storyType)) { list.add(entry.getKey()); } } diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index 46eb0b5..1c1bc54 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -22,7 +22,7 @@ import be.nikiroo.fanfix.supported.BasicSupport.SupportType; */ public class Main { private enum MainAction { - IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER + IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, } /** @@ -46,9 +46,9 @@ public class Main { * target *
  • --read [id] ([chapter number]): read the given story from the library *
  • - *
  • --read-url [URL] ([cahpter number]): convert on the fly and read the + *
  • --read-url [URL] ([chapter number]): convert on the fly and read the * story, without saving it
  • - *
  • --list: list the stories present in the library
  • + *
  • --list ([type]): list the stories present in the library
  • *
  • --set-reader [reader type]: set the reader type to CLI or LOCAL for * this command
  • * @@ -62,7 +62,7 @@ public class Main { String typeString = null; String chapString = null; String target = null; - MainAction action = MainAction.HELP; + MainAction action = MainAction.START; Boolean plusInfo = null; boolean noMoreActions = false; @@ -154,6 +154,9 @@ public class Main { case SET_READER: exitCode = setReaderType(args[i]); break; + case START: + exitCode = 255; // not supposed to be selected by user + break; } } @@ -184,6 +187,10 @@ public class Main { break; case SET_READER: break; + case START: + BasicReader.setDefaultReaderType(ReaderType.LOCAL); + BasicReader.getReader().start(null); + break; } } @@ -232,7 +239,7 @@ public class Main { * * @return the exit return code (0 = success) */ - private static int imprt(String urlString) { + public static int imprt(String urlString) { try { Story story = Instance.getLibrary().imprt(getUrl(urlString)); System.out.println(story.getMeta().getLuid() + ": \"" @@ -257,7 +264,7 @@ public class Main { * * @return the exit return code (0 = success) */ - private static int export(String luid, String typeString, String target) { + public static int export(String luid, String typeString, String target) { OutputType type = OutputType.valueOfNullOkUC(typeString); if (type == null) { Instance.syserr(new Exception(trans(StringId.OUTPUT_DESC, @@ -280,23 +287,13 @@ public class Main { * is passed, in which case all stories will be listed). * * @param typeString - * the {@link SupportType} to list the known stories of, or NULL - * to list all stories + * the type to list the known stories of, or NULL to list all + * stories * * @return the exit return code (0 = success) */ - private static int list(String typeString) { - SupportType type = null; - try { - type = SupportType.valueOfNullOkUC(typeString); - } catch (Exception e) { - Instance.syserr(new Exception( - trans(StringId.INPUT_DESC, typeString), e)); - return 1; - } - + private static int list(String type) { BasicReader.getReader().start(type); - return 0; } @@ -379,7 +376,7 @@ public class Main { } else { try { BasicSupport support = BasicSupport.getSupport(source); - + if (support != null) { Story story = support.process(source); diff --git a/src/be/nikiroo/fanfix/output/Html.java b/src/be/nikiroo/fanfix/output/Html.java index 29a0f32..6e0bb6d 100644 --- a/src/be/nikiroo/fanfix/output/Html.java +++ b/src/be/nikiroo/fanfix/output/Html.java @@ -3,16 +3,22 @@ package be.nikiroo.fanfix.output; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; + +import javax.imageio.ImageIO; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.bundles.StringId; +import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.data.Chapter; +import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Paragraph.ParagraphType; import be.nikiroo.fanfix.data.Story; +import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.StringUtils; class Html extends BasicOutput { + private File dir; protected FileWriter writer; private boolean inDialogue = false; private boolean inNormal = false; @@ -20,12 +26,13 @@ class Html extends BasicOutput { @Override public File process(Story story, File targetDir, String targetName) throws IOException { + String targetNameOrig = targetName; + File target = new File(targetDir, targetName); target.mkdir(); + dir = target; targetName = new File(targetName, "index").getPath(); - - String targetNameOrig = targetName; targetName += getDefaultExtension(); target = new File(targetDir, targetName); @@ -38,6 +45,13 @@ class Html extends BasicOutput { writer = null; } + String format = Instance.getConfig() + .getString(Config.IMAGE_FORMAT_COVER).toLowerCase(); + if (story.getMeta().getCover() != null) { + ImageIO.write(story.getMeta().getCover(), format, new File(dir, + "cover." + format)); + } + return target; } @@ -49,8 +63,39 @@ class Html extends BasicOutput { @Override protected void writeStoryHeader(Story story) throws IOException { String title = ""; + String tags = ""; + String author = ""; + Chapter resume = null; if (story.getMeta() != null) { - title = story.getMeta().getTitle(); + MetaData meta = story.getMeta(); + title = meta.getTitle(); + resume = meta.getResume(); + if (meta.getTags() != null) { + for (String tag : meta.getTags()) { + if (!tags.isEmpty()) { + tags += ", "; + } + tags += tag; + } + + if (!tags.isEmpty()) { + tags = "(" + tags + ")"; + } + } + author = meta.getAuthor(); + } + + String format = Instance.getConfig() + .getString(Config.IMAGE_FORMAT_COVER).toLowerCase(); + + InputStream inStyle = getClass().getResourceAsStream("html.style.css"); + if (inStyle == null) { + throw new IOException("Cannot find style.css resource"); + } + try { + IOUtils.write(inStyle, new File(dir, "style.css")); + } finally { + inStyle.close(); } writer.write(""); @@ -58,11 +103,39 @@ class Html extends BasicOutput { writer.write("\n"); writer.write("\n "); writer.write("\n "); + writer.write("\n "); writer.write("\n " + StringUtils.xmlEscape(title) + ""); writer.write("\n"); writer.write("\n\n"); - writer.write("

    " + StringUtils.xmlEscape(title) + "

    \n\n"); + writer.write("\n
    "); + writer.write("\n

    " + StringUtils.xmlEscape(title) + "

    "); + writer.write("\n
    " + StringUtils.xmlEscape(tags) + + "
    "); + writer.write("\n
    "); + writer.write("\n "); + writer.write("\n
    "); + writer.write("\n
    " + + StringUtils.xmlEscape(author) + "
    "); + writer.write("\n
    "); + + writer.write("\n

    "); + + if (resume != null) { + for (Paragraph para : resume) { + writeParagraph(para); + } + if (inDialogue) { + writer.write(" \n"); + inDialogue = false; + } + if (inNormal) { + writer.write(" \n"); + inNormal = false; + } + } + + writer.write("\n
    "); } @Override @@ -72,21 +145,40 @@ class Html extends BasicOutput { @Override protected void writeChapterHeader(Chapter chap) throws IOException { - String txt; + String nameOrNumber; if (chap.getName() != null && !chap.getName().isEmpty()) { - txt = Instance.getTrans().getString(StringId.CHAPTER_NAMED, - chap.getNumber(), chap.getName()); + nameOrNumber = chap.getName(); } else { - txt = Instance.getTrans().getString(StringId.CHAPTER_UNNAMED, - chap.getNumber()); + nameOrNumber = Integer.toString(chap.getNumber()); } - writer.write("

    " + StringUtils.xmlEscape(txt) + "

    \n\n"); + writer.write("\n

    "); + writer.write("\n Chapter " + + chap.getNumber() + ": "); + writer.write("\n " + + StringUtils.xmlEscape(nameOrNumber) + ""); + writer.write("\n

    "); + writer.write("\n "); + writer.write("\n
    \n"); inDialogue = false; inNormal = false; } + @Override + protected void writeChapterFooter(Chapter chap) throws IOException { + if (inDialogue) { + writer.write("
    \n"); + inDialogue = false; + } + if (inNormal) { + writer.write(" \n"); + inNormal = false; + } + + writer.write("\n "); + } + @Override protected void writeParagraphHeader(Paragraph para) throws IOException { if (para.getType() == ParagraphType.QUOTE && !inDialogue) { diff --git a/src/be/nikiroo/fanfix/output/html.style.css b/src/be/nikiroo/fanfix/output/html.style.css new file mode 100644 index 0000000..fd7cdec --- /dev/null +++ b/src/be/nikiroo/fanfix/output/html.style.css @@ -0,0 +1,105 @@ +html { + text-align: justify; + max-width: 800px; + margin: auto; +} + +.titlepage { + padding-left: 10%; + padding-right: 10%; + width: 80%; +} + +h1 { + padding-bottom: 0; + margin-bottom: 0; + text-align: left; +} + +.type { + position: relative; + font-size: large; + color: #666666; + font-weight: bold; + padding-bottom: 10px; + text-align: left; +} + +.cover, .page-image { + width: 100%; +} + +.cover img { + height: 45%; + max-width: 100%; + margin: auto; +} + +.author { + text-align: right; + font-size: large; + font-style: italic; +} + +.book, .chapter_content { + text-indent: 40px; + padding-top: 40px; + padding-left: 5%; + padding-right: 5%; + width: 90%; +} + +h2 { + border: 1px solid black; + color: #222222; + padding-left: 10px; + padding-right: 10px; + display: block; + padding-bottom: 0; + margin-bottom: 0; +} + +h2 .chap { + color: #000000; + font-size: large; + font-variant: small-caps; + display: block; +} + +h2 .chap:first-letter { + font-weight: bold; +} + +h2 .chapnumber { + color: #000000; + font-size: xx-large; +} + +h2 .chaptitle { + color: #444444; + font-size: large; + font-style: italic; + padding-bottom: 5px; + text-align: right; + display: block; +} + +.normals { + /* padding-bottom: 20px; */ + +} + +.normal { + /* padding-bottom: 20px; */ + +} + +.dialogues { + /* padding-top: 10px; + padding-bottom: 10px; */ + +} + +.dialogue { + font-style: italic; +} \ No newline at end of file diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index 3403f87..eabbe7e 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -8,7 +8,6 @@ import be.nikiroo.fanfix.Library; import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.supported.BasicSupport; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; /** * The class that handles the different {@link Story} readers you can use. @@ -147,7 +146,7 @@ public abstract class BasicReader { * the type of {@link Story} to take into account, or NULL for * all */ - public abstract void start(SupportType type); + public abstract void start(String type); /** * Return a new {@link BasicReader} ready for use if one is configured. @@ -157,13 +156,18 @@ public abstract class BasicReader { * @return a {@link BasicReader}, or NULL if none configured */ public static BasicReader getReader() { - if (defaultType != null) { - switch (defaultType) { - // case LOCAL: - // return new LocalReader().setType(ReaderType.LOCAL); - case CLI: - return new CliReader().setType(ReaderType.CLI); + try { + if (defaultType != null) { + switch (defaultType) { + case LOCAL: + return new LocalReader().setType(ReaderType.LOCAL); + case CLI: + return new CliReader().setType(ReaderType.CLI); + } } + } catch (IOException e) { + Instance.syserr(new Exception("Cannot create a reader of type: " + + defaultType, e)); } return null; diff --git a/src/be/nikiroo/fanfix/reader/CliReader.java b/src/be/nikiroo/fanfix/reader/CliReader.java index b3dd3ed..f9e4b70 100644 --- a/src/be/nikiroo/fanfix/reader/CliReader.java +++ b/src/be/nikiroo/fanfix/reader/CliReader.java @@ -9,7 +9,6 @@ import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Story; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; /** * Command line {@link Story} reader. @@ -75,7 +74,7 @@ class CliReader extends BasicReader { } @Override - public void start(SupportType type) { + public void start(String type) { List stories; stories = Instance.getLibrary().getList(type); diff --git a/src/be/nikiroo/fanfix/reader/LocalReader.java b/src/be/nikiroo/fanfix/reader/LocalReader.java index 26f48f5..bfb8b97 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReader.java +++ b/src/be/nikiroo/fanfix/reader/LocalReader.java @@ -10,7 +10,6 @@ import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.output.BasicOutput.OutputType; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; class LocalReader extends BasicReader { private Library lib; @@ -76,8 +75,8 @@ class LocalReader extends BasicReader { } @Override - public void start(SupportType type) { - final SupportType typeFinal = type; + public void start(String type) { + final String typeFinal = type; EventQueue.invokeLater(new Runnable() { public void run() { new LocalReaderFrame(LocalReader.this, typeFinal) @@ -85,8 +84,4 @@ class LocalReader extends BasicReader { } }); } - - public static void main(String[] args) throws IOException { - new LocalReader().start(null); - } } diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderBook.java b/src/be/nikiroo/fanfix/reader/LocalReaderBook.java new file mode 100644 index 0000000..7fc8171 --- /dev/null +++ b/src/be/nikiroo/fanfix/reader/LocalReaderBook.java @@ -0,0 +1,163 @@ +package be.nikiroo.fanfix.reader; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Date; +import java.util.EventListener; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import be.nikiroo.fanfix.data.MetaData; + +/** + * A book item presented in a {@link LocalReaderFrame}. + * + * @author niki + */ +class LocalReaderBook extends JPanel { + /** + * Action on a book item. + * + * @author niki + */ + interface BookActionListner extends EventListener { + /** + * The book was selected (single click). + * + * @param book + * the {@link LocalReaderBook} itself + */ + public void select(LocalReaderBook book); + + /** + * The book was double-clicked. + * + * @param book + * the {@link LocalReaderBook} itself + */ + public void action(LocalReaderBook book); + } + + private static final long serialVersionUID = 1L; + private JLabel icon; + private JLabel title; + private JLabel author; + private boolean selected; + private boolean hovered; + private Date lastClick; + private long doubleClickDelay = 200; // in ms + private List listeners; + + public LocalReaderBook(MetaData meta) { + if (meta.getCover() != null) { + BufferedImage resizedImage = new BufferedImage(100, 150, + BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D g = resizedImage.createGraphics(); + g.drawImage(meta.getCover(), 0, 0, 100, 150, null); + g.dispose(); + + icon = new JLabel(new ImageIcon(resizedImage)); + } else { + icon = new JLabel(" [ no cover ] "); + } + + title = new JLabel(meta.getTitle()); + author = new JLabel("by " + meta.getAuthor()); + + this.setLayout(new BorderLayout()); + this.add(icon, BorderLayout.CENTER); + this.add(title, BorderLayout.SOUTH); + + setupListeners(); + setSelected(false); + } + + /** + * The book current selection state. + * + * @return the selected + */ + public boolean isSelected() { + return selected; + } + + /** + * The book current selection state. + * + * @param selected + * the selected to set + */ + public void setSelected(boolean selected) { + this.selected = selected; + fixColor(); + } + + private void setHovered(boolean hovered) { + this.hovered = hovered; + fixColor(); + } + + private void fixColor() { + if (selected && !hovered) { + setBackground(new Color(180, 180, 255)); + } else if (!selected && hovered) { + setBackground(new Color(230, 230, 255)); + } else if (selected && hovered) { + setBackground(new Color(200, 200, 255)); + } else { + setBackground(new Color(255, 255, 255)); + } + } + + private void setupListeners() { + listeners = new ArrayList(); + addMouseListener(new MouseListener() { + public void mouseReleased(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + setHovered(false); + } + + public void mouseEntered(MouseEvent e) { + setHovered(true); + } + + public void mouseClicked(MouseEvent e) { + Date now = new Date(); + if (lastClick != null + && now.getTime() - lastClick.getTime() < doubleClickDelay) { + click(true); + } else { + click(false); + } + lastClick = now; + } + }); + } + + private void click(boolean doubleClick) { + for (BookActionListner listener : listeners) { + if (doubleClick) { + listener.action(this); + } else { + listener.select(this); + } + } + } + + public void addActionListener(BookActionListner listener) { + listeners.add(listener); + } +} diff --git a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java index f196f95..ed8a039 100644 --- a/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/LocalReaderFrame.java @@ -4,21 +4,34 @@ import java.awt.Desktop; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.WindowEvent; +import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; -import javax.swing.JButton; import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.Main; import be.nikiroo.fanfix.data.MetaData; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; +import be.nikiroo.fanfix.reader.LocalReaderBook.BookActionListner; class LocalReaderFrame extends JFrame { private static final long serialVersionUID = 1L; private LocalReader reader; + private List stories; + private List books; + private JPanel bookPane; + private String type; - public LocalReaderFrame(LocalReader reader, SupportType type) { + public LocalReaderFrame(LocalReader reader, String type) { super("HTML reader"); this.reader = reader; @@ -27,29 +40,96 @@ class LocalReaderFrame extends JFrame { setSize(800, 600); setLayout(new FlowLayout()); - // TODO: list all stories, list all TMP stories (and format?) + books = new ArrayList(); + bookPane = new JPanel(); + add(bookPane); - List stories = Instance.getLibrary().getList(type); - for (MetaData story : stories) { - JButton button = new JButton(story.getTitle()); - final String luid = story.getLuid(); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + refreshBooks(type); + setJMenuBar(createMenu()); + + setVisible(true); + } + + private void refreshBooks(String type) { + this.type = type; + stories = Instance.getLibrary().getList(type); + books.clear(); + bookPane.removeAll(); + for (MetaData meta : stories) { + LocalReaderBook book = new LocalReaderBook(meta); + books.add(book); + final String luid = meta.getLuid(); + book.addActionListener(new BookActionListner() { + public void select(LocalReaderBook book) { + for (LocalReaderBook abook : books) { + abook.setSelected(abook == book); + } + } + + public void action(LocalReaderBook book) { try { - // TODO: config option (image, non image): TXT, - // custom-HTML, CBZ, EPUB - Desktop.getDesktop().browse( - LocalReaderFrame.this.reader.getTarget(luid) - .toURI()); - } catch (IOException e1) { - e1.printStackTrace(); + File target = LocalReaderFrame.this.reader + .getTarget(luid); + Desktop.getDesktop().browse(target.toURI()); + } catch (IOException e) { + Instance.syserr(e); } } }); - add(button); + bookPane.add(book); } - setVisible(true); + bookPane.validate(); + bookPane.repaint(); + } + + private JMenuBar createMenu() { + JMenuBar bar = new JMenuBar(); + + JMenu file = new JMenu("File"); + + JMenuItem imprt = new JMenuItem("Import", KeyEvent.VK_I); + imprt.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + String url = JOptionPane.showInputDialog(LocalReaderFrame.this, + "url?"); + if (Main.imprt(url) != 0) { + JOptionPane.showMessageDialog(LocalReaderFrame.this, + "Cannot import", "Imort error", + JOptionPane.ERROR_MESSAGE); + } else { + refreshBooks(type); + } + } + }); + JMenu types = new JMenu("Type"); + List tt = Instance.getLibrary().getTypes(); + tt.add(0, null); + for (final String type : tt) { + JMenuItem item = new JMenuItem(type == null ? "[all]" : type); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + refreshBooks(type); + } + }); + types.add(item); + } + JMenuItem exit = new JMenuItem("Exit", KeyEvent.VK_X); + exit.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + LocalReaderFrame.this.dispatchEvent(new WindowEvent( + LocalReaderFrame.this, WindowEvent.WINDOW_CLOSING)); + } + }); + + file.add(imprt); + file.add(types); + file.addSeparator(); + file.add(exit); + + bar.add(file); + + return bar; } } diff --git a/src/be/nikiroo/fanfix/supported/BasicSupport.java b/src/be/nikiroo/fanfix/supported/BasicSupport.java index ed7c4db..61a4500 100644 --- a/src/be/nikiroo/fanfix/supported/BasicSupport.java +++ b/src/be/nikiroo/fanfix/supported/BasicSupport.java @@ -514,7 +514,7 @@ public abstract class BasicSupport { String line = scan.next().trim(); boolean image = false; if (line.startsWith("[") && line.endsWith("]")) { - URL url = getImageUrl(source, + URL url = getImageUrl(this, source, line.substring(1, line.length() - 1).trim()); if (url != null) { paras.add(new Paragraph(url)); @@ -582,7 +582,7 @@ public abstract class BasicSupport { && Instance.getCoverDir() != null) { try { File fileCover = new File(Instance.getCoverDir(), subject); - return getImage(fileCover.toURI().toURL(), subject); + return getImage(null, fileCover.toURI().toURL(), subject); } catch (MalformedURLException e) { } } @@ -603,8 +603,8 @@ public abstract class BasicSupport { } } - static BufferedImage getImage(URL source, String line) { - URL url = getImageUrl(source, line); + static BufferedImage getImage(BasicSupport support, URL source, String line) { + URL url = getImageUrl(support, source, line); if (url != null) { InputStream in = null; try { @@ -636,7 +636,7 @@ public abstract class BasicSupport { * @return the image URL if found, or NULL * */ - static URL getImageUrl(URL source, String line) { + static URL getImageUrl(BasicSupport support, URL source, String line) { URL url = null; if (line != null) { @@ -645,11 +645,11 @@ public abstract class BasicSupport { if (source != null) { path = new File(source.getFile()).getParent(); try { - String urlBase = new File(new File(path), line.trim()) - .toURI().toURL().toString(); + String basePath = new File(new File(path), line.trim()) + .getAbsolutePath(); for (String ext : getImageExt(true)) { - if (new File(urlBase + ext).exists()) { - url = new File(urlBase + ext).toURI().toURL(); + if (new File(basePath + ext).exists()) { + url = new File(basePath + ext).toURI().toURL(); } } } catch (Exception e) { @@ -663,6 +663,7 @@ public abstract class BasicSupport { for (String ext : getImageExt(true)) { if (Instance.getCache().check(new URL(line + ext))) { url = new URL(line + ext); + break; } } @@ -671,8 +672,7 @@ public abstract class BasicSupport { for (String ext : getImageExt(true)) { try { url = new URL(line + ext); - Instance.getCache().refresh(url, - getSupport(url), true); + Instance.getCache().refresh(url, support, true); break; } catch (IOException e) { // no image with this ext @@ -688,7 +688,7 @@ public abstract class BasicSupport { // refresh the cached file if (url != null) { try { - Instance.getCache().refresh(url, getSupport(url), true); + Instance.getCache().refresh(url, support, true); } catch (IOException e) { // woops, broken image url = null; diff --git a/src/be/nikiroo/fanfix/supported/Fanfiction.java b/src/be/nikiroo/fanfix/supported/Fanfiction.java index 2cbe696..5f049e4 100644 --- a/src/be/nikiroo/fanfix/supported/Fanfiction.java +++ b/src/be/nikiroo/fanfix/supported/Fanfiction.java @@ -196,7 +196,7 @@ class Fanfiction extends BasicSupport { + "/" + url.getPath() + "/" + line; } - return getImage(null, line); + return getImage(this, null, line); } } } @@ -220,39 +220,62 @@ class Fanfiction extends BasicSupport { String line = getLine(in, "id=chap_select", 0); String key = "