- [ ] Real menus
- [x] Open a story in the reader and/or natively
- [ ] Update the screenshots
+ - [ ] Remember the current chapter and current read status of stories
- [ ] Network support
- [x] A server that can send the stories
- [x] A network implementation of the Library
}
public void setStory(String luid, Progress pg) throws IOException {
- story = lib.getStory(luid, pg);
+ story = getLibrary().getStory(luid, pg);
if (story == null) {
throw new IOException("Cannot retrieve story from library: " + luid);
}
}
}
+ public void read() throws IOException {
+ read(-1);
+ }
+
/**
* Return a new {@link BasicReader} ready for use if one is configured.
* <p>
* @throws IOException
* in case of I/O error
*/
- public static void open(BasicLibrary lib, String luid) throws IOException {
+ public static void openExternal(BasicLibrary lib, String luid)
+ throws IOException {
MetaData meta = lib.getInfo(luid);
File target = lib.getFile(luid);
- open(meta, target);
+ openExternal(meta, target);
}
/**
* @throws IOException
* in case of I/O error
*/
- protected static void open(MetaData meta, File target) throws IOException {
+ protected static void openExternal(MetaData meta, File target)
+ throws IOException {
String program = null;
if (meta.isImageDocument()) {
program = Instance.getUiConfig().getString(
file = localLibrary.getFile(luid);
}
- open(getLibrary().getInfo(luid), file);
+ openExternal(getLibrary().getInfo(luid), file);
}
void changeType(String luid, String newType) {
public void setLibrary(LocalLibrary lib);
/**
- * Create a new {@link BasicReader} for a {@link Story} in the
- * {@link LocalLibrary}.
+ * Set a {@link Story} from the current {@link Library} into the
+ * {@link Reader}.
*
* @param luid
* the {@link Story} ID
public void setStory(String luid, Progress pg) throws IOException;
/**
- * Create a new {@link BasicReader} for an external {@link Story}.
+ * Set an external {@link Story} into this {@link Reader}.
*
* @param source
* the {@link Story} {@link URL}
public void read() throws IOException;
/**
- * Read the selected chapter (starting at 1).
+ * Read the selected chapter (starting at 1, 0 = description, -1 = none).
*
* @param chapter
- * the chapter
+ * the chapter, or -1 for "no chapter"
*
* @throws IOException
* in case of I/O error or if the {@link Story} was not
* all
*/
public void browse(String source);
-
}
package be.nikiroo.fanfix.reader;
import java.io.IOException;
+import java.util.List;
+import jexer.TApplication;
+import jexer.TApplication.BackendType;
import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.data.MetaData;
+/**
+ * This {@link Reader}is based upon the TUI widget library 'jexer'
+ * (https://github.com/klamonte/jexer/) and offer, as its name suggest, a Text
+ * User Interface.
+ * <p>
+ * It is expected to be on par with the GUI version.
+ *
+ * @author niki
+ */
class TuiReader extends BasicReader {
- public void read() throws IOException {
- if (getStory() == null) {
- throw new IOException("No story to read");
+ /**
+ * Will detect the backend to use.
+ * <p>
+ * Swing is the default backend on Windows and MacOS while evreything else
+ * will use XTERM unless explicitly overridden by <tt>jexer.Swing</tt> =
+ * <tt>true</tt> or <tt>false</tt>.
+ *
+ * @return the backend to use
+ */
+ private static BackendType guessBackendType() {
+ // TODO: allow a config option to force one or the other?
+ TApplication.BackendType backendType = TApplication.BackendType.XTERM;
+ if (System.getProperty("os.name").startsWith("Windows")) {
+ backendType = TApplication.BackendType.SWING;
+ }
+
+ if (System.getProperty("os.name").startsWith("Mac")) {
+ backendType = TApplication.BackendType.SWING;
+ }
+
+ if (System.getProperty("jexer.Swing") != null) {
+ if (System.getProperty("jexer.Swing", "false").equals("true")) {
+ backendType = TApplication.BackendType.SWING;
+ } else {
+ backendType = TApplication.BackendType.XTERM;
+ }
}
- open(getLibrary(), getStory().getMeta().getLuid());
+ return backendType;
}
public void read(int chapter) throws IOException {
- // TODO: show a special page?
- read();
+ if (getStory() == null) {
+ throw new IOException("No story to read");
+ }
+
+ start(getStory().getMeta(), chapter);
}
public void browse(String source) {
+ start(getLibrary().getListBySource(source));
+ }
+
+ /**
+ * Start the application with the given stories.
+ *
+ * @param metas
+ * the stories to display
+ */
+ private void start(List<MetaData> metas) {
+ try {
+ TuiReaderApplication app = new TuiReaderApplication(metas, this,
+ guessBackendType());
+ new Thread(app).start();
+ } catch (Exception e) {
+ Instance.syserr(e);
+ }
+ }
+
+ /**
+ * Start the application with the given {@link MetaData} at the given open
+ * chapter.
+ *
+ * @param meta
+ * the story to display
+ * @param chapter
+ * the chapter to open
+ */
+ private void start(MetaData meta, int chapter) {
try {
- TuiReaderApplication app = new TuiReaderApplication(getLibrary()
- .getListBySource(source), this);
+ TuiReaderApplication app = new TuiReaderApplication(meta, chapter,
+ this, guessBackendType());
new Thread(app).start();
} catch (Exception e) {
Instance.syserr(e);
package be.nikiroo.fanfix.reader;
import java.io.IOException;
+import java.net.URL;
import java.util.List;
import jexer.TApplication;
import jexer.TMessageBox;
+import jexer.TWindow;
import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.fanfix.library.BasicLibrary;
+import be.nikiroo.fanfix.library.LocalLibrary;
+import be.nikiroo.utils.Progress;
-class TuiReaderApplication extends TApplication {
- private BasicReader reader;
+/**
+ * Manages the TUI reader, links and manages the {@link TWindow}s, starts the
+ * correct output mode.
+ *
+ * @author niki
+ */
+class TuiReaderApplication extends TApplication implements Reader {
+ private Reader reader;
- private static BackendType guessBackendType() {
- // Swing is the default backend on Windows unless explicitly
- // overridden by jexer.Swing.
- TApplication.BackendType backendType = TApplication.BackendType.XTERM;
- if (System.getProperty("os.name").startsWith("Windows")) {
- backendType = TApplication.BackendType.SWING;
- }
- if (System.getProperty("os.name").startsWith("Mac")) {
- backendType = TApplication.BackendType.SWING;
- }
- if (System.getProperty("jexer.Swing") != null) {
- if (System.getProperty("jexer.Swing", "false").equals("true")) {
- backendType = TApplication.BackendType.SWING;
- } else {
- backendType = TApplication.BackendType.XTERM;
- }
- }
- return backendType;
- }
+ public TuiReaderApplication(MetaData meta, int chapter, Reader reader,
+ BackendType backend) throws Exception {
+ this(reader, backend);
- public TuiReaderApplication(List<MetaData> stories, BasicReader reader)
- throws Exception {
- this(stories, reader, guessBackendType());
+ new TuiReaderMainWindow(this, meta, chapter);
}
- public TuiReaderApplication(List<MetaData> stories, BasicReader reader,
+ public TuiReaderApplication(List<MetaData> stories, Reader reader,
TApplication.BackendType backend) throws Exception {
+ this(reader, backend);
+
+ new TuiReaderMainWindow(this, stories);
+ }
+
+ private TuiReaderApplication(Reader reader, TApplication.BackendType backend)
+ throws Exception {
super(backend);
this.reader = reader;
addHelpMenu();
getBackend().setTitle("Fanfix");
+ }
- new TuiReaderMainWindow(this, stories);
+ public void read() throws IOException {
+ reader.read();
+ }
+
+ public void read(int chapter) throws IOException {
+ reader.read(chapter);
}
public void open(MetaData meta) {
+ open(meta, -1);
+ }
+
+ public void open(MetaData meta, int chapter) {
// TODO: open in editor + external option
if (!meta.isImageDocument()) {
- new TuiReaderStoryWindow(this, reader.getLibrary(), meta);
+ new TuiReaderStoryWindow(this, getLibrary(), meta, chapter);
} else {
try {
- BasicReader.open(reader.getLibrary(), meta.getLuid());
+ BasicReader.openExternal(getLibrary(), meta.getLuid());
} catch (IOException e) {
messageBox("Error when trying to open the story",
e.getMessage(), TMessageBox.Type.OK);
}
}
}
+
+ public Story getStory() {
+ return reader.getStory();
+ }
+
+ public BasicLibrary getLibrary() {
+ return reader.getLibrary();
+ }
+
+ public void setLibrary(LocalLibrary lib) {
+ reader.setLibrary(lib);
+ }
+
+ public void setStory(String luid, Progress pg) throws IOException {
+ reader.setStory(luid, pg);
+ }
+
+ public void setStory(URL source, Progress pg) throws IOException {
+ reader.setStory(source, pg);
+ }
+
+ public void browse(String source) {
+ reader.browse(source);
+ }
}
import jexer.TWindow;
import be.nikiroo.fanfix.data.MetaData;
+/**
+ * The library window, that will list all the (filtered) stories available in
+ * this {@link Library}.
+ *
+ * @author niki
+ */
class TuiReaderMainWindow extends TWindow {
private TList list;
private List<MetaData> listKeys;
private TuiReaderApplication reader;
/**
- * Constructor.
+ * Create a new {@link TuiReaderMainWindow} with the given stories in the
+ * list.
*
* @param reader
* the reader and main application
- * @param stories
+ * @param metas
* the stories to display
*/
- public TuiReaderMainWindow(TuiReaderApplication reader,
- List<MetaData> stories) {
+ public TuiReaderMainWindow(TuiReaderApplication reader, List<MetaData> metas) {
+ this(reader);
+ setMetas(metas);
+ }
+
+ /**
+ * Create a new {@link TuiReaderMainWindow} with only the given
+ * {@link MetaData} in the list, and open this {@link MetaData} at the given
+ * chapter.
+ *
+ * @param reader
+ * the reader and main application
+ * @param meta
+ * the story to display
+ * @param chapter
+ * the chapter to open
+ */
+ public TuiReaderMainWindow(TuiReaderApplication reader, MetaData meta,
+ int chapter) {
+ this(reader);
+
+ List<MetaData> metas = new ArrayList<MetaData>();
+ metas.add(meta);
+ setMetas(metas);
+
+ reader.open(meta, chapter);
+ }
+
+ /**
+ * Create a new {@link TuiReaderMainWindow} without any stories in the list.
+ *
+ * @param reader
+ * the reader and main application
+ */
+ public TuiReaderMainWindow(TuiReaderApplication reader) {
// Construct a demo window. X and Y don't matter because it will be
// centered on screen.
super(reader, "Library", 0, 0, 60, 18, CENTERED | RESIZABLE
listKeys = new ArrayList<MetaData>();
listItems = new ArrayList<String>();
-
- if (stories != null) {
- for (MetaData meta : stories) {
- listKeys.add(meta);
- listItems.add(desc(meta));
- }
- }
-
list = addList(listItems, 0, 0, getWidth(), getHeight(), new TAction() {
@Override
public void DO() {
}
});
- // TODO: add the current "type" or filter
+ // TODO: add the current "source/type" or filter
statusBar = newStatusBar("Library");
statusBar.addShortcutKeypress(TKeypress.kbF10, TCommand.cmExit, "Exit");
}
}
+ /**
+ * Update the list of stories displayed in this {@link TWindow}.
+ *
+ * @param metas
+ * the new list of stories to display
+ */
+ public void setMetas(List<MetaData> metas) {
+ listKeys.clear();
+ listItems.clear();
+
+ if (metas != null) {
+ for (MetaData meta : metas) {
+ listKeys.add(meta);
+ listItems.add(desc(meta));
+ }
+ }
+
+ list.setList(listItems);
+ }
+
private void enterOnStory(MetaData meta) {
reader.open(meta);
}
private List<TButton> navigationButtons;
private TLabel chapterName;
- public TuiReaderStoryWindow(TApplication app, BasicLibrary lib,
- MetaData meta) {
- this(app, lib, meta, 0);
- }
-
+ // chapter: -1 for "none" (0 is desc)
public TuiReaderStoryWindow(TApplication app, BasicLibrary lib,
MetaData meta, int chapter) {
super(app, desc(meta), 0, 0, 60, 18, CENTERED | RESIZABLE);