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