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