make it subtree
[fanfix.git] / reader / tui / TuiReaderStoryWindow.java
diff --git a/reader/tui/TuiReaderStoryWindow.java b/reader/tui/TuiReaderStoryWindow.java
new file mode 100644 (file)
index 0000000..4848ef8
--- /dev/null
@@ -0,0 +1,305 @@
+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.TButton;
+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.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 Story story;
+       private TLabel titleField;
+       private TText textField;
+       private TTable table;
+       private int chapter = -99; // invalid value
+       private List<TButton> navigationButtons;
+       private TLabel currentChapter;
+       private List<TSizeConstraint> sizeConstraints = new ArrayList<TSizeConstraint>();
+
+       // chapter: -1 for "none" (0 is desc)
+       public TuiReaderStoryWindow(TuiReaderApplication app, Story story,
+                       int chapter) {
+               super(app, desc(story.getMeta()), 0, 0, 60, 18, CENTERED | RESIZABLE);
+
+               this.story = story;
+
+               app.setStatusBar(this, desc(story.getMeta()));
+
+               // 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);
+
+               titleField.setEnabled(false);
+
+               navigationButtons = new ArrayList<TButton>(5);
+
+               navigationButtons.add(addButton("<<", 0, 0, new TAction() {
+                       @Override
+                       public void DO() {
+                               setChapter(-1);
+                       }
+               }));
+               navigationButtons.add(addButton("< ", 4, 0, new TAction() {
+                       @Override
+                       public void DO() {
+                               setChapter(TuiReaderStoryWindow.this.chapter - 1);
+                       }
+               }));
+               navigationButtons.add(addButton("> ", 7, 0, new TAction() {
+                       @Override
+                       public void DO() {
+                               setChapter(TuiReaderStoryWindow.this.chapter + 1);
+                       }
+               }));
+               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);
+
+               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) {
+               if (resize != null) {
+                       super.onResize(resize);
+               }
+
+               // TODO: find out why TText and TTable does not behave the same way
+               // (offset of 2 for height and width)
+
+               TSizeConstraint.resize(sizeConstraints);
+
+               // 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);
+
+               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 > getStory().getChapters().size()) {
+                       chapter = getStory().getChapters().size();
+               }
+
+               if (chapter != this.chapter) {
+                       this.chapter = chapter;
+
+                       int max = getStory().getChapters().size();
+                       navigationButtons.get(0).setEnabled(chapter > -1);
+                       navigationButtons.get(1).setEnabled(chapter > -1);
+                       navigationButtons.get(2).setEnabled(chapter < max);
+                       navigationButtons.get(3).setEnabled(chapter < max);
+
+                       if (chapter < 0) {
+                               displayInfoPage();
+                       } else {
+                               displayChapterPage();
+                       }
+               }
+
+               setCurrentChapterText();
+       }
+
+       /**
+        * 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);
+
+               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");
+                               }
+                       }
+               }
+
+               setText(builder.toString());
+       }
+
+       private Story getStory() {
+               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);
+               }
+       }
+}