package be.nikiroo.fanfix.reader.tui;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
import java.util.List;
import jexer.TAction;
import jexer.TLabel;
import jexer.TText;
import jexer.TWindow;
+import jexer.event.TCommandEvent;
import jexer.event.TResizeEvent;
-import jexer.event.TResizeEvent.Type;
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.jexer.TSizeConstraint;
+import be.nikiroo.jexer.TTable;
+import be.nikiroo.utils.StringUtils;
+
+/**
+ * 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 currentChapter;
+ private List<TSizeConstraint> sizeConstraints = new ArrayList<TSizeConstraint>();
// chapter: -1 for "none" (0 is desc)
- public TuiReaderStoryWindow(TuiReaderApplication 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;
- app.setStatusBar(this, desc(meta));
+ 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);
- navigationButtons = new ArrayList<TButton>(5);
+ titleField.setEnabled(false);
- // -3 because 0-based and 2 for borders
- int row = getHeight() - 3;
+ navigationButtons = new ArrayList<TButton>(5);
- 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);
- currentChapter = addLabel("", 14, row);
- currentChapter.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 TODO: why setW/setH/reflow not enough for the
- // scrollbars?
- textField.onResize(new TResizeEvent(Type.WIDGET, resize.getWidth() - 2,
- resize.getHeight() - 2));
+ // 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 = currentChapter.getLabel();
- while (name.length() < resize.getWidth() - currentChapter.getX()) {
- name += " ";
- }
- currentChapter.setLabel(name);
- currentChapter.setWidth(resize.getWidth() - 10);
- currentChapter.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();
}
/**
int max = getStory().getChapters().size();
navigationButtons.get(0).setEnabled(chapter > -1);
navigationButtons.get(1).setEnabled(chapter > -1);
- navigationButtons.get(2).setEnabled(chapter > -1);
+ navigationButtons.get(2).setEnabled(chapter < max);
navigationButtons.get(3).setEnabled(chapter < max);
- navigationButtons.get(4).setEnabled(chapter < max);
-
- StringBuilder builder = new StringBuilder();
if (chapter < 0) {
- appendInfoPage(builder);
+ displayInfoPage();
} else {
- appendChapterPage(builder);
+ displayChapterPage();
}
-
- setText(builder.toString());
}
setCurrentChapterText();
* @param builder
* the builder to append to
*/
- private StringBuilder appendInfoPage(StringBuilder builder) {
+ private void displayInfoPage() {
+ textField.setVisible(false);
+ table.setVisible(true);
+ textField.setEnabled(false);
+ table.setEnabled(true);
+
MetaData meta = getStory().getMeta();
- // TODO: use a ttable?
+ setCurrentTitle(meta.getTitle());
+
+ StringBuilder tags = new StringBuilder();
+ for (String tag : meta.getTags()) {
+ if (tags.length() > 0) {
+ tags.append(", ");
+ }
+ tags.append(tag);
+ }
+
+ table.setRowData(new String[][] { //
+ new String[] { " Author", meta.getAuthor() }, //
+ new String[] { " Publication date", formatDate(meta.getDate()) },
+ new String[] { " Published on", meta.getPublisher() },
+ new String[] { " URL", meta.getUrl() },
+ new String[] { " Word count", format(meta.getWords()) },
+ new String[] { " Source", meta.getSource() },
+ new String[] { " Subject", meta.getSubject() },
+ new String[] { " Language", meta.getLang() },
+ new String[] { " Tags", tags.toString() } //
+ });
+ table.setHeaders(Arrays.asList("key", "value"), false);
+ table.toTop();
+ }
- appendTitle(builder, meta.getTitle(), 1).append("\n");
+ private String format(long value) {
+ String display = "";
- // i18n
- builder.append("Author: ").append(meta.getAuthor()).append("\n");
- builder.append("Publication date: ").append(meta.getDate())
- .append("\n");
- builder.append("Word count: ").append(meta.getWords()).append("\n");
+ while (value > 0) {
+ if (!display.isEmpty()) {
+ display = "." + display;
+ }
+ display = (value % 1000) + display;
+ value = value / 1000;
+ }
- return builder;
+ return display;
+ }
+
+ private String formatDate(String date) {
+ long ms = 0;
+
+ try {
+ ms = StringUtils.toTime(date);
+ } catch (ParseException e) {
+ }
+
+ if (ms <= 0) {
+ SimpleDateFormat sdf = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ssXXX");
+ try {
+ ms = sdf.parse(date).getTime();
+ } catch (ParseException e) {
+ }
+ }
+
+ if (ms > 0) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ return sdf.format(new Date(ms));
+ }
+
+ // :(
+ return date;
}
/**
* @param builder
* the builder to append to
*/
- private void appendChapterPage(StringBuilder builder) {
+ 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();
// TODO: i18n
String chapName = chap == null ? "[No RESUME]" : chap.getName();
-
- appendTitle(builder,
- String.format("Chapter %d: %s", chapter, chapName), 1);
- builder.append("\n");
+ setCurrentTitle(String.format("Chapter %d: %s", chapter, chapName));
if (chap != null) {
for (Paragraph para : chap) {
}
}
}
+
+ setText(builder.toString());
}
private Story getStory() {
- if (story == null) {
- // TODO: progress bar?
- story = lib.getStory(meta.getLuid(), null);
- }
return story;
}
}
int width = getWidth() - currentChapter.getX();
- while (name.length() < width) {
- name += " ";
- }
-
+ name = String.format("%-" + width + "s", name);
if (name.length() > width) {
name = name.substring(0, width);
}
currentChapter.setLabel(name);
-
- }
-
- private static String desc(MetaData meta) {
- return String.format("%s: %s", meta.getLuid(), meta.getTitle());
}
/**
- * Append a title (on its own 2 lines).
+ * Set the current title in-window.
*
- * @param builder
- * the {@link StringBuilder} to append to
* @param title
- * the title itself
- * @param level
- * the title level, 1 being the highest level, 2 second level and
- * so on
+ * the new title
*/
- private static StringBuilder appendTitle(StringBuilder builder,
- String title, int level) {
- String hr;
- switch (level) {
- case 1:
- hr = "======";
- break;
- case 2:
- hr = "=-=-=-";
- break;
- case 3:
- hr = "______";
- break;
- case 4:
- hr = "------";
- break;
- default:
- hr = "";
- break;
+ private void setCurrentTitle(String title) {
+ String pad = "";
+ if (title.length() < getWidth()) {
+ int padSize = (getWidth() - title.length()) / 2;
+ pad = String.format("%" + padSize + "s", "");
}
- int fullPad = title.length() / hr.length();
- int rest = title.length() - (fullPad * hr.length());
+ title = pad + title + pad;
+ titleField.setWidth(title.length());
+ titleField.setLabel(title);
+ }
- builder.append(title).append("\n");
- for (int i = 0; i < title.length() / hr.length(); i++) {
- builder.append(hr);
- }
+ private static String desc(MetaData meta) {
+ return String.format("%s: %s", meta.getLuid(), meta.getTitle());
+ }
- if (rest > 0) {
- builder.append(hr.substring(0, rest));
+ @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);
}
-
- builder.append("\n");
-
- return builder;
}
}