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
;
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
.TSizeConstraint
;
23 import be
.nikiroo
.jexer
.TTable
;
24 import be
.nikiroo
.utils
.StringUtils
;
27 * This window will contain the {@link Story} in a readable format, with a
32 class TuiReaderStoryWindow
extends TWindow
{
34 private TLabel titleField
;
35 private TText textField
;
37 private int chapter
= -99; // invalid value
38 private List
<TButton
> navigationButtons
;
39 private TLabel currentChapter
;
40 private List
<TSizeConstraint
> sizeConstraints
= new ArrayList
<TSizeConstraint
>();
42 // chapter: -1 for "none" (0 is desc)
43 public TuiReaderStoryWindow(TuiReaderApplication app
, Story story
,
45 super(app
, desc(story
.getMeta()), 0, 0, 60, 18, CENTERED
| RESIZABLE
);
49 app
.setStatusBar(this, desc(story
.getMeta()));
51 // last = use window background
52 titleField
= new TLabel(this, " Title", 0, 1, "tlabel", false);
53 textField
= new TText(this, "", 0, 0, 1, 1);
54 table
= new TTable(this, 0, 0, 1, 1, null, null, Arrays
.asList("Key",
57 titleField
.setEnabled(false);
59 navigationButtons
= new ArrayList
<TButton
>(5);
61 navigationButtons
.add(addButton("<<", 0, 0, new TAction() {
67 navigationButtons
.add(addButton("< ", 4, 0, new TAction() {
70 setChapter(TuiReaderStoryWindow
.this.chapter
- 1);
73 navigationButtons
.add(addButton("> ", 7, 0, new TAction() {
76 setChapter(TuiReaderStoryWindow
.this.chapter
+ 1);
79 navigationButtons
.add(addButton(">>", 10, 0, new TAction() {
82 setChapter(getStory().getChapters().size());
86 navigationButtons
.get(0).setEnabled(false);
87 navigationButtons
.get(1).setEnabled(false);
89 currentChapter
= addLabel("", 0, 0);
91 TSizeConstraint
.setSize(sizeConstraints
, textField
, 1, 3, -1, -1);
92 TSizeConstraint
.setSize(sizeConstraints
, table
, 0, 3, 0, -1);
93 TSizeConstraint
.setSize(sizeConstraints
, currentChapter
, 14, -3, -1,
96 for (TButton navigationButton
: navigationButtons
) {
97 navigationButton
.setShadowColor(null);
98 // navigationButton.setEmptyBorders(false);
99 TSizeConstraint
.setSize(sizeConstraints
, navigationButton
, null,
109 public void onResize(TResizeEvent resize
) {
110 if (resize
!= null) {
111 super.onResize(resize
);
114 // TODO: find out why TText and TTable does not behave the same way
115 // (offset of 2 for height and width)
117 TSizeConstraint
.resize(sizeConstraints
);
119 // Improve the disposition of the scrollbars
120 textField
.getVerticalScroller().setX(textField
.getWidth());
121 textField
.getVerticalScroller().setHeight(textField
.getHeight());
122 textField
.getHorizontalScroller().setX(-1);
123 textField
.getHorizontalScroller().setWidth(textField
.getWidth() + 1);
125 setCurrentChapterText();
129 * Display the current chapter in the window, or the {@link Story} info
133 * the chapter (including "0" which is the description) or "-1"
134 * to display the info page instead
136 private void setChapter(int chapter
) {
137 if (chapter
> getStory().getChapters().size()) {
138 chapter
= getStory().getChapters().size();
141 if (chapter
!= this.chapter
) {
142 this.chapter
= chapter
;
144 int max
= getStory().getChapters().size();
145 navigationButtons
.get(0).setEnabled(chapter
> -1);
146 navigationButtons
.get(1).setEnabled(chapter
> -1);
147 navigationButtons
.get(2).setEnabled(chapter
< max
);
148 navigationButtons
.get(3).setEnabled(chapter
< max
);
153 displayChapterPage();
157 setCurrentChapterText();
161 * Append the info page about the current {@link Story}.
164 * the builder to append to
166 private void displayInfoPage() {
167 textField
.setVisible(false);
168 table
.setVisible(true);
169 textField
.setEnabled(false);
170 table
.setEnabled(true);
172 MetaData meta
= getStory().getMeta();
174 setCurrentTitle(meta
.getTitle());
176 StringBuilder tags
= new StringBuilder();
177 for (String tag
: meta
.getTags()) {
178 if (tags
.length() > 0) {
184 table
.setRowData(new String
[][] { //
185 new String
[] { " Author", meta
.getAuthor() }, //
186 new String
[] { " Publication date", formatDate(meta
.getDate()) },
187 new String
[] { " Published on", meta
.getPublisher() },
188 new String
[] { " URL", meta
.getUrl() },
189 new String
[] { " Word count", format(meta
.getWords()) },
190 new String
[] { " Source", meta
.getSource() },
191 new String
[] { " Subject", meta
.getSubject() },
192 new String
[] { " Language", meta
.getLang() },
193 new String
[] { " Tags", tags
.toString() } //
195 table
.setHeaders(Arrays
.asList("key", "value"), false);
199 private String
format(long value
) {
203 if (!display
.isEmpty()) {
204 display
= "." + display
;
206 display
= (value
% 1000) + display
;
207 value
= value
/ 1000;
213 private String
formatDate(String date
) {
217 ms
= StringUtils
.toTime(date
);
218 } catch (ParseException e
) {
222 SimpleDateFormat sdf
= new SimpleDateFormat(
223 "yyyy-MM-dd'T'HH:mm:ssXXX");
225 ms
= sdf
.parse(date
).getTime();
226 } catch (ParseException e
) {
231 SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd");
232 return sdf
.format(new Date(ms
));
240 * Append the current chapter.
243 * the builder to append to
245 private void displayChapterPage() {
246 table
.setVisible(false);
247 textField
.setVisible(true);
248 table
.setEnabled(false);
249 textField
.setEnabled(true);
251 StringBuilder builder
= new StringBuilder();
255 chap
= getStory().getMeta().getResume();
256 } else if (chapter
> 0) {
257 chap
= getStory().getChapters().get(chapter
- 1);
261 String chapName
= chap
== null ?
"[No RESUME]" : chap
.getName();
262 setCurrentTitle(String
.format("Chapter %d: %s", chapter
, chapName
));
265 for (Paragraph para
: chap
) {
266 if (para
.getType() == ParagraphType
.BREAK
) {
267 builder
.append("\n");
269 builder
.append(para
.getContent()).append("\n");
270 if (para
.getType() == ParagraphType
.BREAK
) {
271 builder
.append("\n");
276 setText(builder
.toString());
279 private Story
getStory() {
284 * Display the given text on the window.
287 * the text to display
289 private void setText(String text
) {
290 textField
.setText(text
);
291 textField
.reflowData();
296 * Set the current chapter area to the correct value.
298 private void setCurrentChapterText() {
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());
308 name
= "[No RESUME]";
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());
316 int width
= getWidth() - currentChapter
.getX();
317 name
= String
.format("%-" + width
+ "s", name
);
318 if (name
.length() > width
) {
319 name
= name
.substring(0, width
);
322 currentChapter
.setLabel(name
);
326 * Set the current title in-window.
331 private void setCurrentTitle(String title
) {
333 if (title
.length() < getWidth()) {
334 int padSize
= (getWidth() - title
.length()) / 2;
335 pad
= String
.format("%" + padSize
+ "s", "");
338 title
= pad
+ title
+ pad
;
339 titleField
.setWidth(title
.length());
340 titleField
.setLabel(title
);
343 private static String
desc(MetaData meta
) {
344 return String
.format("%s: %s", meta
.getLuid(), meta
.getTitle());
348 public void onCommand(TCommandEvent command
) {
349 if (command
.getCmd().equals(TuiReaderApplication
.CMD_EXIT
)) {
350 TuiReaderApplication
.close(this);
352 // Handle our own event if needed here
353 super.onCommand(command
);