TUI: code cleanup + show story info
[fanfix.git] / src / be / nikiroo / fanfix / reader / tui / TuiReaderStoryWindow.java
CommitLineData
16a81ef7 1package be.nikiroo.fanfix.reader.tui;
c1873e56 2
edfd3577
NR
3import java.util.ArrayList;
4import java.util.List;
5
0861d62a 6import jexer.TAction;
0861d62a 7import jexer.TButton;
0861d62a 8import jexer.TLabel;
f63c4267 9import jexer.TText;
0861d62a 10import jexer.TWindow;
c1873e56 11import jexer.event.TResizeEvent;
4f66bfa8 12import jexer.event.TResizeEvent.Type;
c1873e56
NR
13import be.nikiroo.fanfix.data.Chapter;
14import be.nikiroo.fanfix.data.MetaData;
15import be.nikiroo.fanfix.data.Paragraph;
9293188f 16import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
c1873e56 17import be.nikiroo.fanfix.data.Story;
e42573a0 18import be.nikiroo.fanfix.library.BasicLibrary;
c1873e56 19
5dd985cf 20class TuiReaderStoryWindow extends TWindow {
68e2c6d2 21 private BasicLibrary lib;
c1873e56
NR
22 private MetaData meta;
23 private Story story;
f63c4267 24 private TText textField;
bc2ea776 25 private int chapter = -1;
edfd3577 26 private List<TButton> navigationButtons;
5b00c122 27 private TLabel currentChapter;
c1873e56 28
6322ab64 29 // chapter: -1 for "none" (0 is desc)
5b00c122 30 public TuiReaderStoryWindow(TuiReaderApplication app, BasicLibrary lib,
68e2c6d2 31 MetaData meta, int chapter) {
c1873e56 32 super(app, desc(meta), 0, 0, 60, 18, CENTERED | RESIZABLE);
68e2c6d2 33
b0e88ebd 34 this.lib = lib;
c1873e56
NR
35 this.meta = meta;
36
5b00c122 37 app.setStatusBar(this, desc(meta));
c1873e56 38
f63c4267 39 textField = new TText(this, "", 0, 0, getWidth() - 2, getHeight() - 2);
edfd3577 40
0b7e3d78 41 navigationButtons = new ArrayList<TButton>(5);
edfd3577 42
396e924c 43 // -3 because 0-based and 2 for borders
edfd3577
NR
44 int row = getHeight() - 3;
45
b0e88ebd
NR
46 navigationButtons.add(addButton(" ", 0, row, null)); // for bg colour
47 // when <<
48 // button is
49 // pressed
0861d62a 50 navigationButtons.add(addButton("<< ", 0, row, new TAction() {
211f7ddb 51 @Override
396e924c 52 public void DO() {
edfd3577 53 setChapter(0);
396e924c 54 }
edfd3577 55 }));
0861d62a 56 navigationButtons.add(addButton("< ", 4, row, new TAction() {
211f7ddb 57 @Override
edfd3577
NR
58 public void DO() {
59 setChapter(TuiReaderStoryWindow.this.chapter - 1);
60 }
61 }));
0861d62a 62 navigationButtons.add(addButton("> ", 7, row, new TAction() {
211f7ddb 63 @Override
edfd3577
NR
64 public void DO() {
65 setChapter(TuiReaderStoryWindow.this.chapter + 1);
66 }
67 }));
0861d62a 68 navigationButtons.add(addButton(">> ", 10, row, new TAction() {
211f7ddb 69 @Override
edfd3577
NR
70 public void DO() {
71 setChapter(getStory().getChapters().size());
72 }
73 }));
b0e88ebd 74
8670a283
NR
75 navigationButtons.get(0).setEnabled(false);
76 navigationButtons.get(1).setEnabled(false);
77 navigationButtons.get(2).setEnabled(false);
edfd3577 78
5b00c122
NR
79 currentChapter = addLabel("", 14, row);
80 currentChapter.setWidth(getWidth() - 10);
edfd3577 81 setChapter(chapter);
c1873e56
NR
82 }
83
84 @Override
85 public void onResize(TResizeEvent resize) {
86 super.onResize(resize);
87
4f66bfa8
NR
88 // Resize the text field TODO: why setW/setH/reflow not enough for the
89 // scrollbars?
90 textField.onResize(new TResizeEvent(Type.WIDGET, resize.getWidth() - 2,
91 resize.getHeight() - 2));
edfd3577
NR
92
93 // -3 because 0-based and 2 for borders
94 int row = getHeight() - 3;
95
5b00c122
NR
96 String name = currentChapter.getLabel();
97 while (name.length() < resize.getWidth() - currentChapter.getX()) {
edfd3577
NR
98 name += " ";
99 }
5b00c122
NR
100 currentChapter.setLabel(name);
101 currentChapter.setWidth(resize.getWidth() - 10);
102 currentChapter.setY(row);
edfd3577
NR
103
104 for (TButton button : navigationButtons) {
105 button.setY(row);
106 }
107 }
108
5b00c122
NR
109 /**
110 * Display the current chapter in the window, or the {@link Story} info
111 * page.
112 *
113 * @param chapter
114 * the chapter (including "0" which is the description) or "-1"
115 * to display the info page instead
116 */
edfd3577 117 private void setChapter(int chapter) {
edfd3577
NR
118 if (chapter > getStory().getChapters().size()) {
119 chapter = getStory().getChapters().size();
120 }
121
122 if (chapter != this.chapter) {
123 this.chapter = chapter;
b0e88ebd 124
8670a283 125 int max = getStory().getChapters().size();
5b00c122
NR
126 navigationButtons.get(0).setEnabled(chapter > -1);
127 navigationButtons.get(1).setEnabled(chapter > -1);
128 navigationButtons.get(2).setEnabled(chapter > -1);
8670a283
NR
129 navigationButtons.get(3).setEnabled(chapter < max);
130 navigationButtons.get(4).setEnabled(chapter < max);
b0e88ebd 131
5b00c122
NR
132 StringBuilder builder = new StringBuilder();
133
134 if (chapter < 0) {
135 appendInfoPage(builder);
edfd3577 136 } else {
5b00c122 137 appendChapterPage(builder);
edfd3577
NR
138 }
139
5b00c122
NR
140 setText(builder.toString());
141 }
142
143 setCurrentChapterText();
144 }
edfd3577 145
5b00c122
NR
146 /**
147 * Append the info page about the current {@link Story}.
148 *
149 * @param builder
150 * the builder to append to
151 */
152 private StringBuilder appendInfoPage(StringBuilder builder) {
153 MetaData meta = getStory().getMeta();
edfd3577 154
5b00c122
NR
155 // TODO: use a ttable?
156
157 appendTitle(builder, meta.getTitle(), 1).append("\n");
158
159 // i18n
160 builder.append("Author: ").append(meta.getAuthor()).append("\n");
161 builder.append("Publication date: ").append(meta.getDate())
162 .append("\n");
163 builder.append("Word count: ").append(meta.getWords()).append("\n");
164
165 return builder;
166 }
167
168 /**
169 * Append the current chapter.
170 *
171 * @param builder
172 * the builder to append to
173 */
174 private void appendChapterPage(StringBuilder builder) {
175 Chapter chap = null;
176 if (chapter == 0) {
177 chap = getStory().getMeta().getResume();
178 } else if (chapter > 0) {
179 chap = getStory().getChapters().get(chapter - 1);
180 }
181
182 // TODO: i18n
183 String chapName = chap == null ? "[No RESUME]" : chap.getName();
184
185 appendTitle(builder,
186 String.format("Chapter %d: %s", chapter, chapName), 1);
187 builder.append("\n");
188
189 if (chap != null) {
190 for (Paragraph para : chap) {
191 if (para.getType() == ParagraphType.BREAK) {
192 builder.append("\n");
193 }
194 builder.append(para.getContent()).append("\n");
195 if (para.getType() == ParagraphType.BREAK) {
196 builder.append("\n");
fdc55375 197 }
edfd3577 198 }
edfd3577 199 }
c1873e56
NR
200 }
201
202 private Story getStory() {
203 if (story == null) {
edfd3577 204 // TODO: progress bar?
b0e88ebd 205 story = lib.getStory(meta.getLuid(), null);
c1873e56
NR
206 }
207 return story;
208 }
209
5b00c122
NR
210 /**
211 * Display the given text on the window.
212 *
213 * @param text
214 * the text to display
215 */
216 private void setText(String text) {
217 textField.setText(text);
218 textField.reflowData();
219 textField.toTop();
220 }
221
222 /**
223 * Set the current chapter area to the correct value.
224 */
225 private void setCurrentChapterText() {
226 String name;
227 if (chapter < 0) {
228 name = " " + getStory().getMeta().getTitle();
229 } else if (chapter == 0) {
230 Chapter resume = getStory().getMeta().getResume();
231 if (resume != null) {
232 name = String.format(" %s", resume.getName());
233 } else {
234 // TODO: i18n
235 name = "[No RESUME]";
236 }
237 } else {
238 int max = getStory().getChapters().size();
239 Chapter chap = getStory().getChapters().get(chapter - 1);
240 name = String.format(" %d/%d: %s", chapter, max, chap.getName());
241 }
242
243 int width = getWidth() - currentChapter.getX();
244 while (name.length() < width) {
245 name += " ";
246 }
247
248 if (name.length() > width) {
249 name = name.substring(0, width);
250 }
251
252 currentChapter.setLabel(name);
253
254 }
255
c1873e56
NR
256 private static String desc(MetaData meta) {
257 return String.format("%s: %s", meta.getLuid(), meta.getTitle());
258 }
5b00c122
NR
259
260 /**
261 * Append a title (on its own 2 lines).
262 *
263 * @param builder
264 * the {@link StringBuilder} to append to
265 * @param title
266 * the title itself
267 * @param level
268 * the title level, 1 being the highest level, 2 second level and
269 * so on
270 */
271 private static StringBuilder appendTitle(StringBuilder builder,
272 String title, int level) {
273 String hr;
274 switch (level) {
275 case 1:
276 hr = "======";
277 break;
278 case 2:
279 hr = "=-=-=-";
280 break;
281 case 3:
282 hr = "______";
283 break;
284 case 4:
285 hr = "------";
286 break;
287 default:
288 hr = "";
289 break;
290 }
291
292 int fullPad = title.length() / hr.length();
293 int rest = title.length() - (fullPad * hr.length());
294
295 builder.append(title).append("\n");
296 for (int i = 0; i < title.length() / hr.length(); i++) {
297 builder.append(hr);
298 }
299
300 if (rest > 0) {
301 builder.append(hr.substring(0, rest));
302 }
303
304 builder.append("\n");
305
306 return builder;
307 }
c1873e56 308}