package be.nikiroo.fanfix.reader.tui;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import jexer.TAction;
-import jexer.TApplication;
import jexer.TButton;
-import jexer.TCommand;
-import jexer.TKeypress;
import jexer.TLabel;
import jexer.TText;
import jexer.TWindow;
+import jexer.event.TCommandEvent;
import jexer.event.TResizeEvent;
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.fanfix.library.BasicLibrary;
+import be.nikiroo.fanfix.reader.BasicReader;
+import be.nikiroo.jexer.TSizeConstraint;
+import be.nikiroo.jexer.TTable;
+/**
+ * This window will contain the {@link Story} in a readable format, with a
+ * chapter browser.
+ *
+ * @author niki
+ */
class TuiReaderStoryWindow extends TWindow {
- private BasicLibrary lib;
- private MetaData meta;
private Story story;
+ private TLabel titleField;
private TText textField;
- private int chapter = -1;
+ private TTable table;
+ private int chapter = -99; // invalid value
private List<TButton> navigationButtons;
- private TLabel chapterName;
+ private TLabel currentChapter;
+ private List<TSizeConstraint> sizeConstraints = new ArrayList<TSizeConstraint>();
// 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);
+ public TuiReaderStoryWindow(TuiReaderApplication app, Story story,
+ int chapter) {
+ super(app, desc(story.getMeta()), 0, 0, 60, 18, CENTERED | RESIZABLE);
- this.lib = lib;
- this.meta = meta;
+ this.story = story;
- // TODO: show all meta info before?
+ app.setStatusBar(this, desc(story.getMeta()));
- textField = new TText(this, "", 0, 0, getWidth() - 2, getHeight() - 2);
+ // last = use window background
+ titleField = new TLabel(this, " Title", 0, 1, "tlabel", false);
+ textField = new TText(this, "", 0, 0, 1, 1);
+ table = new TTable(this, 0, 0, 1, 1, null, null, Arrays.asList("Key",
+ "Value"), true);
- statusBar = newStatusBar(desc(meta));
- statusBar.addShortcutKeypress(TKeypress.kbF10, TCommand.cmExit, "Exit");
+ titleField.setEnabled(false);
navigationButtons = new ArrayList<TButton>(5);
- // -3 because 0-based and 2 for borders
- int row = getHeight() - 3;
-
- navigationButtons.add(addButton(" ", 0, row, null)); // for bg colour
- // when <<
- // button is
- // pressed
- navigationButtons.add(addButton("<< ", 0, row, new TAction() {
+ navigationButtons.add(addButton("<<", 0, 0, new TAction() {
@Override
public void DO() {
- setChapter(0);
+ setChapter(-1);
}
}));
- navigationButtons.add(addButton("< ", 4, row, new TAction() {
+ navigationButtons.add(addButton("< ", 4, 0, new TAction() {
@Override
public void DO() {
setChapter(TuiReaderStoryWindow.this.chapter - 1);
}
}));
- navigationButtons.add(addButton("> ", 7, row, new TAction() {
+ navigationButtons.add(addButton("> ", 7, 0, new TAction() {
@Override
public void DO() {
setChapter(TuiReaderStoryWindow.this.chapter + 1);
}
}));
- navigationButtons.add(addButton(">> ", 10, row, new TAction() {
+ navigationButtons.add(addButton(">>", 10, 0, new TAction() {
@Override
public void DO() {
setChapter(getStory().getChapters().size());
navigationButtons.get(0).setEnabled(false);
navigationButtons.get(1).setEnabled(false);
- navigationButtons.get(2).setEnabled(false);
- chapterName = addLabel("", 14, row);
- chapterName.setWidth(getWidth() - 10);
+ currentChapter = addLabel("", 0, 0);
+
+ TSizeConstraint.setSize(sizeConstraints, textField, 1, 3, -1, -1);
+ TSizeConstraint.setSize(sizeConstraints, table, 0, 3, 0, -1);
+ TSizeConstraint.setSize(sizeConstraints, currentChapter, 14, -3, -1,
+ null);
+
+ for (TButton navigationButton : navigationButtons) {
+ navigationButton.setShadowColor(null);
+ // navigationButton.setEmptyBorders(false);
+ TSizeConstraint.setSize(sizeConstraints, navigationButton, null,
+ -3, null, null);
+ }
+
+ onResize(null);
+
setChapter(chapter);
}
@Override
public void onResize(TResizeEvent resize) {
- super.onResize(resize);
+ if (resize != null) {
+ super.onResize(resize);
+ }
- // Resize the text field
- textField.setWidth(resize.getWidth() - 2);
- textField.setHeight(resize.getHeight() - 2);
- textField.reflow();
+ // TODO: find out why TText and TTable does not behave the same way
+ // (offset of 2 for height and width)
- // -3 because 0-based and 2 for borders
- int row = getHeight() - 3;
+ TSizeConstraint.resize(sizeConstraints);
- String name = chapterName.getLabel();
- while (name.length() < resize.getWidth() - chapterName.getX()) {
- name += " ";
- }
- chapterName.setLabel(name);
- chapterName.setWidth(resize.getWidth() - 10);
- chapterName.setY(row);
+ // Improve the disposition of the scrollbars
+ textField.getVerticalScroller().setX(textField.getWidth());
+ textField.getVerticalScroller().setHeight(textField.getHeight());
+ textField.getHorizontalScroller().setX(-1);
+ textField.getHorizontalScroller().setWidth(textField.getWidth() + 1);
- for (TButton button : navigationButtons) {
- button.setY(row);
- }
+ setCurrentChapterText();
}
+ /**
+ * Display the current chapter in the window, or the {@link Story} info
+ * page.
+ *
+ * @param chapter
+ * the chapter (including "0" which is the description) or "-1"
+ * to display the info page instead
+ */
private void setChapter(int chapter) {
- if (chapter < 0) {
- chapter = 0;
- }
-
if (chapter > getStory().getChapters().size()) {
chapter = getStory().getChapters().size();
}
this.chapter = chapter;
int max = getStory().getChapters().size();
- navigationButtons.get(0).setEnabled(chapter > 0);
- navigationButtons.get(1).setEnabled(chapter > 0);
- navigationButtons.get(2).setEnabled(chapter > 0);
+ navigationButtons.get(0).setEnabled(chapter > -1);
+ navigationButtons.get(1).setEnabled(chapter > -1);
+ navigationButtons.get(2).setEnabled(chapter < max);
navigationButtons.get(3).setEnabled(chapter < max);
- navigationButtons.get(4).setEnabled(chapter < max);
-
- Chapter chap;
- String name;
- if (chapter == 0) {
- chap = getStory().getMeta().getResume();
- if (chap != null)
- name = String.format(" %s", chap.getName());
- else
- name = "[No RESUME]";
+
+ if (chapter < 0) {
+ displayInfoPage();
} else {
- chap = getStory().getChapters().get(chapter - 1);
- name = String
- .format(" %d/%d: %s", chapter, max, chap.getName());
+ displayChapterPage();
}
+ }
- while (name.length() < getWidth() - chapterName.getX()) {
- name += " ";
- }
+ setCurrentChapterText();
+ }
- chapterName.setLabel(name);
+ /**
+ * Append the info page about the current {@link Story}.
+ *
+ * @param builder
+ * the builder to append to
+ */
+ private void displayInfoPage() {
+ textField.setVisible(false);
+ table.setVisible(true);
+ textField.setEnabled(false);
+ table.setEnabled(true);
- StringBuilder builder = new StringBuilder();
- // TODO: i18n
- String c = String.format("Chapter %d: %s", chapter,
- chap == null ? "[No RESUME]" : chap.getName());
- builder.append(c).append("\n");
- for (int i = 0; i < c.length(); i++) {
- builder.append("═");
- }
- builder.append("\n\n");
- if (chap != null) {
- for (Paragraph para : chap) {
- if (para.getType() == ParagraphType.BREAK) {
- builder.append("\n");
- }
- builder.append(para.getContent()).append("\n");
- if (para.getType() == ParagraphType.BREAK) {
- builder.append("\n");
- }
+ MetaData meta = getStory().getMeta();
+
+ setCurrentTitle(meta.getTitle());
+
+ Map<String, String> metaDesc = BasicReader.getMetaDesc(meta);
+ String[][] metaDescObj = new String[metaDesc.size()][2];
+ int i = 0;
+ for (String key : metaDesc.keySet()) {
+ metaDescObj[i][0] = " " + key;
+ metaDescObj[i][1] = metaDesc.get(key);
+ i++;
+ }
+
+ table.setRowData(metaDescObj);
+ table.setHeaders(Arrays.asList("key", "value"), false);
+ table.toTop();
+ }
+
+ /**
+ * Append the current chapter.
+ *
+ * @param builder
+ * the builder to append to
+ */
+ private void displayChapterPage() {
+ table.setVisible(false);
+ textField.setVisible(true);
+ table.setEnabled(false);
+ textField.setEnabled(true);
+
+ StringBuilder builder = new StringBuilder();
+
+ Chapter chap = null;
+ if (chapter == 0) {
+ chap = getStory().getMeta().getResume();
+ } else if (chapter > 0) {
+ chap = getStory().getChapters().get(chapter - 1);
+ }
+
+ // TODO: i18n
+ String chapName = chap == null ? "[No RESUME]" : chap.getName();
+ setCurrentTitle(String.format("Chapter %d: %s", chapter, chapName));
+
+ if (chap != null) {
+ for (Paragraph para : chap) {
+ if (para.getType() == ParagraphType.BREAK) {
+ builder.append("\n");
+ }
+ builder.append(para.getContent()).append("\n");
+ if (para.getType() == ParagraphType.BREAK) {
+ builder.append("\n");
}
}
- textField.setText(builder.toString());
- textField.reflow();
- textField.toTop();
}
+
+ setText(builder.toString());
}
private Story getStory() {
- if (story == null) {
- // TODO: progress bar?
- story = lib.getStory(meta.getLuid(), null);
- }
return story;
}
+ /**
+ * Display the given text on the window.
+ *
+ * @param text
+ * the text to display
+ */
+ private void setText(String text) {
+ textField.setText(text);
+ textField.reflowData();
+ textField.toTop();
+ }
+
+ /**
+ * Set the current chapter area to the correct value.
+ */
+ private void setCurrentChapterText() {
+ String name;
+ if (chapter < 0) {
+ name = " " + getStory().getMeta().getTitle();
+ } else if (chapter == 0) {
+ Chapter resume = getStory().getMeta().getResume();
+ if (resume != null) {
+ name = String.format(" %s", resume.getName());
+ } else {
+ // TODO: i18n
+ name = "[No RESUME]";
+ }
+ } else {
+ int max = getStory().getChapters().size();
+ Chapter chap = getStory().getChapters().get(chapter - 1);
+ name = String.format(" %d/%d: %s", chapter, max, chap.getName());
+ }
+
+ int width = getWidth() - currentChapter.getX();
+ name = String.format("%-" + width + "s", name);
+ if (name.length() > width) {
+ name = name.substring(0, width);
+ }
+
+ currentChapter.setLabel(name);
+ }
+
+ /**
+ * Set the current title in-window.
+ *
+ * @param title
+ * the new title
+ */
+ private void setCurrentTitle(String title) {
+ String pad = "";
+ if (title.length() < getWidth()) {
+ int padSize = (getWidth() - title.length()) / 2;
+ pad = String.format("%" + padSize + "s", "");
+ }
+
+ title = pad + title + pad;
+ titleField.setWidth(title.length());
+ titleField.setLabel(title);
+ }
+
private static String desc(MetaData meta) {
return String.format("%s: %s", meta.getLuid(), meta.getTitle());
}
+
+ @Override
+ public void onCommand(TCommandEvent command) {
+ if (command.getCmd().equals(TuiReaderApplication.CMD_EXIT)) {
+ TuiReaderApplication.close(this);
+ } else {
+ // Handle our own event if needed here
+ super.onCommand(command);
+ }
+ }
}