TUI: improve story info, padding
[fanfix.git] / src / be / nikiroo / fanfix / reader / tui / TuiReaderStoryWindow.java
CommitLineData
16a81ef7 1package be.nikiroo.fanfix.reader.tui;
c1873e56 2
e8796963
NR
3import java.text.ParseException;
4import java.text.SimpleDateFormat;
edfd3577 5import java.util.ArrayList;
6a26aa76 6import java.util.Arrays;
e8796963 7import java.util.Date;
edfd3577
NR
8import java.util.List;
9
0861d62a 10import jexer.TAction;
0861d62a 11import jexer.TButton;
0861d62a 12import jexer.TLabel;
6a26aa76 13import jexer.TTable;
f63c4267 14import jexer.TText;
0861d62a 15import jexer.TWindow;
c1873e56 16import jexer.event.TResizeEvent;
4f66bfa8 17import jexer.event.TResizeEvent.Type;
c1873e56
NR
18import be.nikiroo.fanfix.data.Chapter;
19import be.nikiroo.fanfix.data.MetaData;
20import be.nikiroo.fanfix.data.Paragraph;
9293188f 21import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
c1873e56 22import be.nikiroo.fanfix.data.Story;
e42573a0 23import be.nikiroo.fanfix.library.BasicLibrary;
e8796963 24import be.nikiroo.utils.StringUtils;
c1873e56 25
6a26aa76
NR
26/**
27 * This window will contain the {@link Story} in a readable format, with a
28 * chapter browser.
29 *
30 * @author niki
31 */
5dd985cf 32class TuiReaderStoryWindow extends TWindow {
68e2c6d2 33 private BasicLibrary lib;
c1873e56
NR
34 private MetaData meta;
35 private Story story;
6a26aa76 36 private TLabel titleField;
f63c4267 37 private TText textField;
6a26aa76
NR
38 private TTable table;
39 private int chapter = -99; // invalid value
edfd3577 40 private List<TButton> navigationButtons;
5b00c122 41 private TLabel currentChapter;
c1873e56 42
6322ab64 43 // chapter: -1 for "none" (0 is desc)
5b00c122 44 public TuiReaderStoryWindow(TuiReaderApplication app, BasicLibrary lib,
68e2c6d2 45 MetaData meta, int chapter) {
c1873e56 46 super(app, desc(meta), 0, 0, 60, 18, CENTERED | RESIZABLE);
68e2c6d2 47
b0e88ebd 48 this.lib = lib;
c1873e56
NR
49 this.meta = meta;
50
5b00c122 51 app.setStatusBar(this, desc(meta));
c1873e56 52
6a26aa76
NR
53 // last = use window background
54 titleField = new TLabel(this, " Title", 0, 1, "tlabel", false);
e8796963 55 textField = new TText(this, "", 1, 3, getWidth() - 4, getHeight() - 5);
6a26aa76
NR
56 table = new TTable(this, 0, 3, getWidth(), getHeight() - 4, null, null,
57 Arrays.asList("Key", "Value"), true);
edfd3577 58
e8796963
NR
59 titleField.setEnabled(false);
60 textField.getVerticalScroller().setX(
61 textField.getVerticalScroller().getX() + 1);
62
0b7e3d78 63 navigationButtons = new ArrayList<TButton>(5);
edfd3577 64
396e924c 65 // -3 because 0-based and 2 for borders
edfd3577
NR
66 int row = getHeight() - 3;
67
6a26aa76
NR
68 // for bg colour when << button is pressed
69 navigationButtons.add(addButton(" ", 0, row, null));
0861d62a 70 navigationButtons.add(addButton("<< ", 0, row, new TAction() {
211f7ddb 71 @Override
396e924c 72 public void DO() {
6a26aa76 73 setChapter(-1);
396e924c 74 }
edfd3577 75 }));
0861d62a 76 navigationButtons.add(addButton("< ", 4, row, new TAction() {
211f7ddb 77 @Override
edfd3577
NR
78 public void DO() {
79 setChapter(TuiReaderStoryWindow.this.chapter - 1);
80 }
81 }));
0861d62a 82 navigationButtons.add(addButton("> ", 7, row, new TAction() {
211f7ddb 83 @Override
edfd3577
NR
84 public void DO() {
85 setChapter(TuiReaderStoryWindow.this.chapter + 1);
86 }
87 }));
0861d62a 88 navigationButtons.add(addButton(">> ", 10, row, new TAction() {
211f7ddb 89 @Override
edfd3577
NR
90 public void DO() {
91 setChapter(getStory().getChapters().size());
92 }
93 }));
b0e88ebd 94
8670a283
NR
95 navigationButtons.get(0).setEnabled(false);
96 navigationButtons.get(1).setEnabled(false);
97 navigationButtons.get(2).setEnabled(false);
edfd3577 98
5b00c122
NR
99 currentChapter = addLabel("", 14, row);
100 currentChapter.setWidth(getWidth() - 10);
6a26aa76 101
edfd3577 102 setChapter(chapter);
c1873e56
NR
103 }
104
105 @Override
106 public void onResize(TResizeEvent resize) {
107 super.onResize(resize);
108
e8796963
NR
109 // Resize the text field
110 // TODO: why setW/setH/reflow not enough for the scrollbars?
111 textField.onResize(new TResizeEvent(Type.WIDGET, resize.getWidth() - 4,
6a26aa76 112 resize.getHeight() - 5));
e8796963
NR
113 textField.getVerticalScroller().setX(
114 textField.getVerticalScroller().getX() + 1);
6a26aa76
NR
115
116 table.setWidth(getWidth());
117 table.setHeight(getHeight() - 4);
118 table.reflowData();
edfd3577
NR
119
120 // -3 because 0-based and 2 for borders
121 int row = getHeight() - 3;
122
5b00c122
NR
123 String name = currentChapter.getLabel();
124 while (name.length() < resize.getWidth() - currentChapter.getX()) {
edfd3577
NR
125 name += " ";
126 }
5b00c122
NR
127 currentChapter.setLabel(name);
128 currentChapter.setWidth(resize.getWidth() - 10);
129 currentChapter.setY(row);
edfd3577
NR
130
131 for (TButton button : navigationButtons) {
132 button.setY(row);
133 }
134 }
135
5b00c122
NR
136 /**
137 * Display the current chapter in the window, or the {@link Story} info
138 * page.
139 *
140 * @param chapter
141 * the chapter (including "0" which is the description) or "-1"
142 * to display the info page instead
143 */
edfd3577 144 private void setChapter(int chapter) {
edfd3577
NR
145 if (chapter > getStory().getChapters().size()) {
146 chapter = getStory().getChapters().size();
147 }
148
149 if (chapter != this.chapter) {
150 this.chapter = chapter;
b0e88ebd 151
8670a283 152 int max = getStory().getChapters().size();
5b00c122
NR
153 navigationButtons.get(0).setEnabled(chapter > -1);
154 navigationButtons.get(1).setEnabled(chapter > -1);
155 navigationButtons.get(2).setEnabled(chapter > -1);
8670a283
NR
156 navigationButtons.get(3).setEnabled(chapter < max);
157 navigationButtons.get(4).setEnabled(chapter < max);
b0e88ebd 158
5b00c122 159 if (chapter < 0) {
6a26aa76 160 displayInfoPage();
edfd3577 161 } else {
6a26aa76 162 displayChapterPage();
edfd3577 163 }
5b00c122
NR
164 }
165
166 setCurrentChapterText();
167 }
edfd3577 168
5b00c122
NR
169 /**
170 * Append the info page about the current {@link Story}.
171 *
172 * @param builder
173 * the builder to append to
174 */
6a26aa76
NR
175 private void displayInfoPage() {
176 textField.setVisible(false);
177 table.setVisible(true);
e8796963
NR
178 textField.setEnabled(false);
179 table.setEnabled(true);
5b00c122 180
6a26aa76 181 MetaData meta = getStory().getMeta();
5b00c122 182
6a26aa76 183 setCurrentTitle(meta.getTitle());
5b00c122 184
6a26aa76 185 table.setRowData(new String[][] { //
e8796963
NR
186 new String[] { " Author", meta.getAuthor() }, //
187 new String[] { " Publication date", formatDate(meta.getDate()) },
188 new String[] { " Word count", format(meta.getWords()) },
189 new String[] { " Source", meta.getSource() } //
6a26aa76
NR
190 });
191 table.setHeaders(Arrays.asList("key", "value"), false);
192 table.toTop();
5b00c122
NR
193 }
194
e8796963
NR
195 private String format(long value) {
196 String display = "";
197
198 while (value > 0) {
199 if (!display.isEmpty()) {
200 display = "." + display;
201 }
202 display = (value % 1000) + display;
203 value = value / 1000;
204 }
205
206 return display;
207 }
208
209 private String formatDate(String date) {
210 long ms = 0;
211
212 try {
213 ms = StringUtils.toTime(date);
214 } catch (ParseException e) {
215 }
216
217 if (ms <= 0) {
218 SimpleDateFormat sdf = new SimpleDateFormat(
219 "yyyy-MM-dd'T'HH:mm:ssXXX");
220 try {
221 ms = sdf.parse(date).getTime();
222 } catch (ParseException e) {
223 }
224 }
225
226 if (ms > 0) {
227 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
228 return sdf.format(new Date(ms));
229 }
230
231 // :(
232 return date;
233 }
234
5b00c122
NR
235 /**
236 * Append the current chapter.
237 *
238 * @param builder
239 * the builder to append to
240 */
6a26aa76
NR
241 private void displayChapterPage() {
242 table.setVisible(false);
243 textField.setVisible(true);
e8796963
NR
244 table.setEnabled(false);
245 textField.setEnabled(true);
6a26aa76
NR
246
247 StringBuilder builder = new StringBuilder();
248
5b00c122
NR
249 Chapter chap = null;
250 if (chapter == 0) {
251 chap = getStory().getMeta().getResume();
252 } else if (chapter > 0) {
253 chap = getStory().getChapters().get(chapter - 1);
254 }
255
256 // TODO: i18n
257 String chapName = chap == null ? "[No RESUME]" : chap.getName();
6a26aa76 258 setCurrentTitle(String.format("Chapter %d: %s", chapter, chapName));
5b00c122
NR
259
260 if (chap != null) {
261 for (Paragraph para : chap) {
262 if (para.getType() == ParagraphType.BREAK) {
263 builder.append("\n");
264 }
265 builder.append(para.getContent()).append("\n");
266 if (para.getType() == ParagraphType.BREAK) {
267 builder.append("\n");
fdc55375 268 }
edfd3577 269 }
edfd3577 270 }
6a26aa76
NR
271
272 setText(builder.toString());
c1873e56
NR
273 }
274
275 private Story getStory() {
276 if (story == null) {
edfd3577 277 // TODO: progress bar?
b0e88ebd 278 story = lib.getStory(meta.getLuid(), null);
c1873e56
NR
279 }
280 return story;
281 }
282
5b00c122
NR
283 /**
284 * Display the given text on the window.
285 *
286 * @param text
287 * the text to display
288 */
289 private void setText(String text) {
290 textField.setText(text);
291 textField.reflowData();
292 textField.toTop();
293 }
294
295 /**
296 * Set the current chapter area to the correct value.
297 */
298 private void setCurrentChapterText() {
299 String name;
300 if (chapter < 0) {
301 name = " " + getStory().getMeta().getTitle();
302 } else if (chapter == 0) {
303 Chapter resume = getStory().getMeta().getResume();
304 if (resume != null) {
305 name = String.format(" %s", resume.getName());
306 } else {
307 // TODO: i18n
308 name = "[No RESUME]";
309 }
310 } else {
311 int max = getStory().getChapters().size();
312 Chapter chap = getStory().getChapters().get(chapter - 1);
313 name = String.format(" %d/%d: %s", chapter, max, chap.getName());
314 }
315
316 int width = getWidth() - currentChapter.getX();
317 while (name.length() < width) {
318 name += " ";
319 }
320
321 if (name.length() > width) {
322 name = name.substring(0, width);
323 }
324
325 currentChapter.setLabel(name);
c1873e56 326 }
5b00c122
NR
327
328 /**
6a26aa76 329 * Set the current title in-window.
5b00c122 330 *
5b00c122 331 * @param title
6a26aa76 332 * the new title
5b00c122 333 */
6a26aa76 334 private void setCurrentTitle(String title) {
e8796963
NR
335 String pad = "";
336 if (title.length() < getWidth()) {
337 int padSize = (getWidth() - title.length()) / 2;
338 pad = String.format("%" + padSize + "s", "");
339 }
340
341 title = pad + title + pad;
6a26aa76
NR
342 titleField.setWidth(title.length());
343 titleField.setLabel(title);
344 }
5b00c122 345
6a26aa76
NR
346 private static String desc(MetaData meta) {
347 return String.format("%s: %s", meta.getLuid(), meta.getTitle());
5b00c122 348 }
c1873e56 349}