59842ad9365378a467bcd4edbcedebd60482dcab
[fanfix.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 List<Map.Entry<String, String>> metaDesc = BasicReader
175 .getMetaDesc(meta);
176 String[][] metaDescObj = new String[metaDesc.size()][2];
177 int i = 0;
178 for (Map.Entry<String, String> entry : metaDesc) {
179 metaDescObj[i][0] = " " + entry.getKey();
180 metaDescObj[i][1] = entry.getValue();
181 i++;
182 }
183
184 table.setRowData(metaDescObj);
185 table.setHeaders(Arrays.asList("key", "value"), false);
186 table.toTop();
187 }
188
189 /**
190 * Append the current chapter.
191 *
192 * @param builder
193 * the builder to append to
194 */
195 private void displayChapterPage() {
196 table.setVisible(false);
197 textField.setVisible(true);
198 table.setEnabled(false);
199 textField.setEnabled(true);
200
201 StringBuilder builder = new StringBuilder();
202
203 Chapter chap = null;
204 if (chapter == 0) {
205 chap = getStory().getMeta().getResume();
206 } else if (chapter > 0) {
207 chap = getStory().getChapters().get(chapter - 1);
208 }
209
210 // TODO: i18n
211 String chapName = chap == null ? "[No RESUME]" : chap.getName();
212 setCurrentTitle(String.format("Chapter %d: %s", chapter, chapName));
213
214 if (chap != null) {
215 for (Paragraph para : chap) {
216 if (para.getType() == ParagraphType.BREAK) {
217 builder.append("\n");
218 }
219 builder.append(para.getContent()).append("\n");
220 if (para.getType() == ParagraphType.BREAK) {
221 builder.append("\n");
222 }
223 }
224 }
225
226 setText(builder.toString());
227 }
228
229 private Story getStory() {
230 return story;
231 }
232
233 /**
234 * Display the given text on the window.
235 *
236 * @param text
237 * the text to display
238 */
239 private void setText(String text) {
240 textField.setText(text);
241 textField.reflowData();
242 textField.toTop();
243 }
244
245 /**
246 * Set the current chapter area to the correct value.
247 */
248 private void setCurrentChapterText() {
249 String name;
250 if (chapter < 0) {
251 name = " " + getStory().getMeta().getTitle();
252 } else if (chapter == 0) {
253 Chapter resume = getStory().getMeta().getResume();
254 if (resume != null) {
255 name = String.format(" %s", resume.getName());
256 } else {
257 // TODO: i18n
258 name = "[No RESUME]";
259 }
260 } else {
261 int max = getStory().getChapters().size();
262 Chapter chap = getStory().getChapters().get(chapter - 1);
263 name = String.format(" %d/%d: %s", chapter, max, chap.getName());
264 }
265
266 int width = getWidth() - currentChapter.getX();
267 name = String.format("%-" + width + "s", name);
268 if (name.length() > width) {
269 name = name.substring(0, width);
270 }
271
272 currentChapter.setLabel(name);
273 }
274
275 /**
276 * Set the current title in-window.
277 *
278 * @param title
279 * the new title
280 */
281 private void setCurrentTitle(String title) {
282 String pad = "";
283 if (title.length() < getWidth()) {
284 int padSize = (getWidth() - title.length()) / 2;
285 pad = String.format("%" + padSize + "s", "");
286 }
287
288 title = pad + title + pad;
289 titleField.setWidth(title.length());
290 titleField.setLabel(title);
291 }
292
293 private static String desc(MetaData meta) {
294 return String.format("%s: %s", meta.getLuid(), meta.getTitle());
295 }
296
297 @Override
298 public void onCommand(TCommandEvent command) {
299 if (command.getCmd().equals(TuiReaderApplication.CMD_EXIT)) {
300 TuiReaderApplication.close(this);
301 } else {
302 // Handle our own event if needed here
303 super.onCommand(command);
304 }
305 }
306 }