Move TSizeConstraint into new package
[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;
f63c4267 13import jexer.TText;
0861d62a 14import jexer.TWindow;
cf9c5ed1 15import jexer.event.TCommandEvent;
c1873e56 16import jexer.event.TResizeEvent;
c1873e56
NR
17import be.nikiroo.fanfix.data.Chapter;
18import be.nikiroo.fanfix.data.MetaData;
19import be.nikiroo.fanfix.data.Paragraph;
9293188f 20import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
c1873e56 21import be.nikiroo.fanfix.data.Story;
0bcb5c7f 22import be.nikiroo.jexer.TSizeConstraint;
8f34a795 23import be.nikiroo.jexer.TTable;
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 {
c1873e56 33 private Story story;
6a26aa76 34 private TLabel titleField;
f63c4267 35 private TText textField;
6a26aa76
NR
36 private TTable table;
37 private int chapter = -99; // invalid value
edfd3577 38 private List<TButton> navigationButtons;
5b00c122 39 private TLabel currentChapter;
f00daa5e 40 private List<TSizeConstraint> sizeConstraints = new ArrayList<TSizeConstraint>();
c1873e56 41
6322ab64 42 // chapter: -1 for "none" (0 is desc)
e0fb1417
NR
43 public TuiReaderStoryWindow(TuiReaderApplication app, Story story,
44 int chapter) {
45 super(app, desc(story.getMeta()), 0, 0, 60, 18, CENTERED | RESIZABLE);
68e2c6d2 46
e0fb1417 47 this.story = story;
c1873e56 48
e0fb1417 49 app.setStatusBar(this, desc(story.getMeta()));
c1873e56 50
6a26aa76
NR
51 // last = use window background
52 titleField = new TLabel(this, " Title", 0, 1, "tlabel", false);
f00daa5e 53 textField = new TText(this, "", 0, 0, 1, 1);
494877be
NR
54 table = new TTable(this, 0, 0, 1, 1, null, null, Arrays.asList("Key",
55 "Value"), true);
edfd3577 56
e8796963 57 titleField.setEnabled(false);
e8796963 58
0b7e3d78 59 navigationButtons = new ArrayList<TButton>(5);
edfd3577 60
494877be 61 navigationButtons.add(addButton("<<", 0, 0, new TAction() {
211f7ddb 62 @Override
396e924c 63 public void DO() {
6a26aa76 64 setChapter(-1);
396e924c 65 }
edfd3577 66 }));
494877be 67 navigationButtons.add(addButton("< ", 4, 0, new TAction() {
211f7ddb 68 @Override
edfd3577
NR
69 public void DO() {
70 setChapter(TuiReaderStoryWindow.this.chapter - 1);
71 }
72 }));
494877be 73 navigationButtons.add(addButton("> ", 7, 0, new TAction() {
211f7ddb 74 @Override
edfd3577
NR
75 public void DO() {
76 setChapter(TuiReaderStoryWindow.this.chapter + 1);
77 }
78 }));
494877be 79 navigationButtons.add(addButton(">>", 10, 0, new TAction() {
211f7ddb 80 @Override
edfd3577
NR
81 public void DO() {
82 setChapter(getStory().getChapters().size());
83 }
84 }));
b0e88ebd 85
8670a283
NR
86 navigationButtons.get(0).setEnabled(false);
87 navigationButtons.get(1).setEnabled(false);
edfd3577 88
f00daa5e
NR
89 currentChapter = addLabel("", 0, 0);
90
494877be
NR
91 TSizeConstraint.setSize(sizeConstraints, textField, 1, 3, -1, -1);
92 TSizeConstraint.setSize(sizeConstraints, table, 0, 3, 0, -1);
f00daa5e
NR
93 TSizeConstraint.setSize(sizeConstraints, currentChapter, 14, -3, -1,
94 null);
95
96 for (TButton navigationButton : navigationButtons) {
494877be
NR
97 navigationButton.setShadowColor(null);
98 // navigationButton.setEmptyBorders(false);
f00daa5e
NR
99 TSizeConstraint.setSize(sizeConstraints, navigationButton, null,
100 -3, null, null);
101 }
102
103 onResize(null);
6a26aa76 104
edfd3577 105 setChapter(chapter);
c1873e56
NR
106 }
107
108 @Override
109 public void onResize(TResizeEvent resize) {
f00daa5e
NR
110 if (resize != null) {
111 super.onResize(resize);
112 }
c1873e56 113
f00daa5e
NR
114 // TODO: find out why TText and TTable does not behave the same way
115 // (offset of 2 for height and width)
6a26aa76 116
f00daa5e 117 TSizeConstraint.resize(sizeConstraints);
edfd3577 118
494877be
NR
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);
edfd3577 124
ce424b19 125 setCurrentChapterText();
edfd3577
NR
126 }
127
5b00c122
NR
128 /**
129 * Display the current chapter in the window, or the {@link Story} info
130 * page.
131 *
132 * @param chapter
133 * the chapter (including "0" which is the description) or "-1"
134 * to display the info page instead
135 */
edfd3577 136 private void setChapter(int chapter) {
edfd3577
NR
137 if (chapter > getStory().getChapters().size()) {
138 chapter = getStory().getChapters().size();
139 }
140
141 if (chapter != this.chapter) {
142 this.chapter = chapter;
b0e88ebd 143
8670a283 144 int max = getStory().getChapters().size();
5b00c122
NR
145 navigationButtons.get(0).setEnabled(chapter > -1);
146 navigationButtons.get(1).setEnabled(chapter > -1);
494877be 147 navigationButtons.get(2).setEnabled(chapter < max);
8670a283 148 navigationButtons.get(3).setEnabled(chapter < max);
b0e88ebd 149
5b00c122 150 if (chapter < 0) {
6a26aa76 151 displayInfoPage();
edfd3577 152 } else {
6a26aa76 153 displayChapterPage();
edfd3577 154 }
5b00c122
NR
155 }
156
157 setCurrentChapterText();
158 }
edfd3577 159
5b00c122
NR
160 /**
161 * Append the info page about the current {@link Story}.
162 *
163 * @param builder
164 * the builder to append to
165 */
6a26aa76
NR
166 private void displayInfoPage() {
167 textField.setVisible(false);
168 table.setVisible(true);
e8796963
NR
169 textField.setEnabled(false);
170 table.setEnabled(true);
5b00c122 171
6a26aa76 172 MetaData meta = getStory().getMeta();
5b00c122 173
6a26aa76 174 setCurrentTitle(meta.getTitle());
5b00c122 175
f435c113
NR
176 StringBuilder tags = new StringBuilder();
177 for (String tag : meta.getTags()) {
178 if (tags.length() > 0) {
179 tags.append(", ");
180 }
181 tags.append(tag);
182 }
183
6a26aa76 184 table.setRowData(new String[][] { //
e8796963
NR
185 new String[] { " Author", meta.getAuthor() }, //
186 new String[] { " Publication date", formatDate(meta.getDate()) },
f435c113
NR
187 new String[] { " Published on", meta.getPublisher() },
188 new String[] { " URL", meta.getUrl() },
e8796963 189 new String[] { " Word count", format(meta.getWords()) },
f435c113
NR
190 new String[] { " Source", meta.getSource() },
191 new String[] { " Subject", meta.getSubject() },
192 new String[] { " Language", meta.getLang() },
193 new String[] { " Tags", tags.toString() } //
6a26aa76
NR
194 });
195 table.setHeaders(Arrays.asList("key", "value"), false);
196 table.toTop();
5b00c122
NR
197 }
198
e8796963
NR
199 private String format(long value) {
200 String display = "";
201
202 while (value > 0) {
203 if (!display.isEmpty()) {
204 display = "." + display;
205 }
206 display = (value % 1000) + display;
207 value = value / 1000;
208 }
209
210 return display;
211 }
212
213 private String formatDate(String date) {
214 long ms = 0;
215
216 try {
217 ms = StringUtils.toTime(date);
218 } catch (ParseException e) {
219 }
220
221 if (ms <= 0) {
222 SimpleDateFormat sdf = new SimpleDateFormat(
223 "yyyy-MM-dd'T'HH:mm:ssXXX");
224 try {
225 ms = sdf.parse(date).getTime();
226 } catch (ParseException e) {
227 }
228 }
229
230 if (ms > 0) {
231 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
232 return sdf.format(new Date(ms));
233 }
234
235 // :(
236 return date;
237 }
238
5b00c122
NR
239 /**
240 * Append the current chapter.
241 *
242 * @param builder
243 * the builder to append to
244 */
6a26aa76
NR
245 private void displayChapterPage() {
246 table.setVisible(false);
247 textField.setVisible(true);
e8796963
NR
248 table.setEnabled(false);
249 textField.setEnabled(true);
6a26aa76
NR
250
251 StringBuilder builder = new StringBuilder();
252
5b00c122
NR
253 Chapter chap = null;
254 if (chapter == 0) {
255 chap = getStory().getMeta().getResume();
256 } else if (chapter > 0) {
257 chap = getStory().getChapters().get(chapter - 1);
258 }
259
260 // TODO: i18n
261 String chapName = chap == null ? "[No RESUME]" : chap.getName();
6a26aa76 262 setCurrentTitle(String.format("Chapter %d: %s", chapter, chapName));
5b00c122
NR
263
264 if (chap != null) {
265 for (Paragraph para : chap) {
266 if (para.getType() == ParagraphType.BREAK) {
267 builder.append("\n");
268 }
269 builder.append(para.getContent()).append("\n");
270 if (para.getType() == ParagraphType.BREAK) {
271 builder.append("\n");
fdc55375 272 }
edfd3577 273 }
edfd3577 274 }
6a26aa76
NR
275
276 setText(builder.toString());
c1873e56
NR
277 }
278
279 private Story getStory() {
c1873e56
NR
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();
ce424b19 317 name = String.format("%-" + width + "s", name);
5b00c122
NR
318 if (name.length() > width) {
319 name = name.substring(0, width);
320 }
321
322 currentChapter.setLabel(name);
c1873e56 323 }
5b00c122
NR
324
325 /**
6a26aa76 326 * Set the current title in-window.
5b00c122 327 *
5b00c122 328 * @param title
6a26aa76 329 * the new title
5b00c122 330 */
6a26aa76 331 private void setCurrentTitle(String title) {
e8796963
NR
332 String pad = "";
333 if (title.length() < getWidth()) {
334 int padSize = (getWidth() - title.length()) / 2;
335 pad = String.format("%" + padSize + "s", "");
336 }
337
338 title = pad + title + pad;
6a26aa76
NR
339 titleField.setWidth(title.length());
340 titleField.setLabel(title);
341 }
5b00c122 342
6a26aa76
NR
343 private static String desc(MetaData meta) {
344 return String.format("%s: %s", meta.getLuid(), meta.getTitle());
5b00c122 345 }
cf9c5ed1
NR
346
347 @Override
348 public void onCommand(TCommandEvent command) {
349 if (command.getCmd().equals(TuiReaderApplication.CMD_EXIT)) {
350 TuiReaderApplication.close(this);
351 } else {
352 // Handle our own event if needed here
353 super.onCommand(command);
354 }
355 }
c1873e56 356}