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