1 package be
.nikiroo
.fanfix
.reader
.tui
;
3 import java
.text
.ParseException
;
4 import java
.text
.SimpleDateFormat
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Arrays
;
16 import jexer
.event
.TCommandEvent
;
17 import jexer
.event
.TResizeEvent
;
18 import jexer
.event
.TResizeEvent
.Type
;
19 import be
.nikiroo
.fanfix
.data
.Chapter
;
20 import be
.nikiroo
.fanfix
.data
.MetaData
;
21 import be
.nikiroo
.fanfix
.data
.Paragraph
;
22 import be
.nikiroo
.fanfix
.data
.Paragraph
.ParagraphType
;
23 import be
.nikiroo
.fanfix
.data
.Story
;
24 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
25 import be
.nikiroo
.utils
.StringUtils
;
28 * This window will contain the {@link Story} in a readable format, with a
33 class TuiReaderStoryWindow
extends TWindow
{
34 private BasicLibrary lib
;
35 private MetaData meta
;
37 private TLabel titleField
;
38 private TText textField
;
40 private int chapter
= -99; // invalid value
41 private List
<TButton
> navigationButtons
;
42 private TLabel currentChapter
;
44 // chapter: -1 for "none" (0 is desc)
45 public TuiReaderStoryWindow(TuiReaderApplication app
, BasicLibrary lib
,
46 MetaData meta
, int chapter
) {
47 super(app
, desc(meta
), 0, 0, 60, 18, CENTERED
| RESIZABLE
);
52 app
.setStatusBar(this, desc(meta
));
54 // last = use window background
55 titleField
= new TLabel(this, " Title", 0, 1, "tlabel", false);
56 textField
= new TText(this, "", 1, 3, getWidth() - 4, getHeight() - 5);
57 table
= new TTable(this, 0, 3, getWidth(), getHeight() - 4, null, null,
58 Arrays
.asList("Key", "Value"), true);
60 titleField
.setEnabled(false);
61 textField
.getVerticalScroller().setX(
62 textField
.getVerticalScroller().getX() + 1);
64 navigationButtons
= new ArrayList
<TButton
>(5);
66 // -3 because 0-based and 2 for borders
67 int row
= getHeight() - 3;
69 // for bg colour when << button is pressed
70 navigationButtons
.add(addButton(" ", 0, row
, null));
71 navigationButtons
.add(addButton("<< ", 0, row
, new TAction() {
77 navigationButtons
.add(addButton("< ", 4, row
, new TAction() {
80 setChapter(TuiReaderStoryWindow
.this.chapter
- 1);
83 navigationButtons
.add(addButton("> ", 7, row
, new TAction() {
86 setChapter(TuiReaderStoryWindow
.this.chapter
+ 1);
89 navigationButtons
.add(addButton(">> ", 10, row
, new TAction() {
92 setChapter(getStory().getChapters().size());
96 navigationButtons
.get(0).setEnabled(false);
97 navigationButtons
.get(1).setEnabled(false);
98 navigationButtons
.get(2).setEnabled(false);
100 currentChapter
= addLabel("", 14, row
);
101 currentChapter
.setWidth(getWidth() - 10);
107 public void onResize(TResizeEvent resize
) {
108 super.onResize(resize
);
110 // Resize the text field
111 // TODO: why setW/setH/reflow not enough for the scrollbars?
112 textField
.onResize(new TResizeEvent(Type
.WIDGET
, resize
.getWidth() - 4,
113 resize
.getHeight() - 5));
114 textField
.getVerticalScroller().setX(
115 textField
.getVerticalScroller().getX() + 1);
117 table
.setWidth(getWidth());
118 table
.setHeight(getHeight() - 4);
121 // -3 because 0-based and 2 for borders
122 int row
= getHeight() - 3;
124 String name
= currentChapter
.getLabel();
125 while (name
.length() < resize
.getWidth() - currentChapter
.getX()) {
128 currentChapter
.setLabel(name
);
129 currentChapter
.setWidth(resize
.getWidth() - 10);
130 currentChapter
.setY(row
);
132 for (TButton button
: navigationButtons
) {
138 * Display the current chapter in the window, or the {@link Story} info
142 * the chapter (including "0" which is the description) or "-1"
143 * to display the info page instead
145 private void setChapter(int chapter
) {
146 if (chapter
> getStory().getChapters().size()) {
147 chapter
= getStory().getChapters().size();
150 if (chapter
!= this.chapter
) {
151 this.chapter
= chapter
;
153 int max
= getStory().getChapters().size();
154 navigationButtons
.get(0).setEnabled(chapter
> -1);
155 navigationButtons
.get(1).setEnabled(chapter
> -1);
156 navigationButtons
.get(2).setEnabled(chapter
> -1);
157 navigationButtons
.get(3).setEnabled(chapter
< max
);
158 navigationButtons
.get(4).setEnabled(chapter
< max
);
163 displayChapterPage();
167 setCurrentChapterText();
171 * Append the info page about the current {@link Story}.
174 * the builder to append to
176 private void displayInfoPage() {
177 textField
.setVisible(false);
178 table
.setVisible(true);
179 textField
.setEnabled(false);
180 table
.setEnabled(true);
182 MetaData meta
= getStory().getMeta();
184 setCurrentTitle(meta
.getTitle());
186 table
.setRowData(new String
[][] { //
187 new String
[] { " Author", meta
.getAuthor() }, //
188 new String
[] { " Publication date", formatDate(meta
.getDate()) },
189 new String
[] { " Word count", format(meta
.getWords()) },
190 new String
[] { " Source", meta
.getSource() } //
192 table
.setHeaders(Arrays
.asList("key", "value"), false);
196 private String
format(long value
) {
200 if (!display
.isEmpty()) {
201 display
= "." + display
;
203 display
= (value
% 1000) + display
;
204 value
= value
/ 1000;
210 private String
formatDate(String date
) {
214 ms
= StringUtils
.toTime(date
);
215 } catch (ParseException e
) {
219 SimpleDateFormat sdf
= new SimpleDateFormat(
220 "yyyy-MM-dd'T'HH:mm:ssXXX");
222 ms
= sdf
.parse(date
).getTime();
223 } catch (ParseException e
) {
228 SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
229 return sdf
.format(new Date(ms
));
237 * Append the current chapter.
240 * the builder to append to
242 private void displayChapterPage() {
243 table
.setVisible(false);
244 textField
.setVisible(true);
245 table
.setEnabled(false);
246 textField
.setEnabled(true);
248 StringBuilder builder
= new StringBuilder();
252 chap
= getStory().getMeta().getResume();
253 } else if (chapter
> 0) {
254 chap
= getStory().getChapters().get(chapter
- 1);
258 String chapName
= chap
== null ?
"[No RESUME]" : chap
.getName();
259 setCurrentTitle(String
.format("Chapter %d: %s", chapter
, chapName
));
262 for (Paragraph para
: chap
) {
263 if (para
.getType() == ParagraphType
.BREAK
) {
264 builder
.append("\n");
266 builder
.append(para
.getContent()).append("\n");
267 if (para
.getType() == ParagraphType
.BREAK
) {
268 builder
.append("\n");
273 setText(builder
.toString());
276 private Story
getStory() {
278 // TODO: progress bar?
279 story
= lib
.getStory(meta
.getLuid(), null);
285 * Display the given text on the window.
288 * the text to display
290 private void setText(String text
) {
291 textField
.setText(text
);
292 textField
.reflowData();
297 * Set the current chapter area to the correct value.
299 private void setCurrentChapterText() {
302 name
= " " + getStory().getMeta().getTitle();
303 } else if (chapter
== 0) {
304 Chapter resume
= getStory().getMeta().getResume();
305 if (resume
!= null) {
306 name
= String
.format(" %s", resume
.getName());
309 name
= "[No RESUME]";
312 int max
= getStory().getChapters().size();
313 Chapter chap
= getStory().getChapters().get(chapter
- 1);
314 name
= String
.format(" %d/%d: %s", chapter
, max
, chap
.getName());
317 int width
= getWidth() - currentChapter
.getX();
318 while (name
.length() < width
) {
322 if (name
.length() > width
) {
323 name
= name
.substring(0, width
);
326 currentChapter
.setLabel(name
);
330 * Set the current title in-window.
335 private void setCurrentTitle(String title
) {
337 if (title
.length() < getWidth()) {
338 int padSize
= (getWidth() - title
.length()) / 2;
339 pad
= String
.format("%" + padSize
+ "s", "");
342 title
= pad
+ title
+ pad
;
343 titleField
.setWidth(title
.length());
344 titleField
.setLabel(title
);
347 private static String
desc(MetaData meta
) {
348 return String
.format("%s: %s", meta
.getLuid(), meta
.getTitle());
352 public void onCommand(TCommandEvent command
) {
353 if (command
.getCmd().equals(TuiReaderApplication
.CMD_EXIT
)) {
354 TuiReaderApplication
.close(this);
356 // Handle our own event if needed here
357 super.onCommand(command
);