Commit | Line | Data |
---|---|---|
16a81ef7 | 1 | package be.nikiroo.fanfix.reader.tui; |
c1873e56 | 2 | |
edfd3577 | 3 | import java.util.ArrayList; |
6a26aa76 | 4 | import java.util.Arrays; |
edfd3577 | 5 | import java.util.List; |
df6e2d88 | 6 | import java.util.Map; |
edfd3577 | 7 | |
0861d62a | 8 | import jexer.TAction; |
0861d62a | 9 | import jexer.TButton; |
0861d62a | 10 | import jexer.TLabel; |
f63c4267 | 11 | import jexer.TText; |
0861d62a | 12 | import jexer.TWindow; |
cf9c5ed1 | 13 | import jexer.event.TCommandEvent; |
c1873e56 | 14 | import jexer.event.TResizeEvent; |
c1873e56 NR |
15 | import be.nikiroo.fanfix.data.Chapter; |
16 | import be.nikiroo.fanfix.data.MetaData; | |
17 | import be.nikiroo.fanfix.data.Paragraph; | |
9293188f | 18 | import be.nikiroo.fanfix.data.Paragraph.ParagraphType; |
c1873e56 | 19 | import be.nikiroo.fanfix.data.Story; |
df6e2d88 | 20 | import be.nikiroo.fanfix.reader.BasicReader; |
0bcb5c7f | 21 | import be.nikiroo.jexer.TSizeConstraint; |
8f34a795 | 22 | import be.nikiroo.jexer.TTable; |
c1873e56 | 23 | |
6a26aa76 NR |
24 | /** |
25 | * This window will contain the {@link Story} in a readable format, with a | |
26 | * chapter browser. | |
27 | * | |
28 | * @author niki | |
29 | */ | |
5dd985cf | 30 | class TuiReaderStoryWindow extends TWindow { |
c1873e56 | 31 | private Story story; |
6a26aa76 | 32 | private TLabel titleField; |
f63c4267 | 33 | private TText textField; |
6a26aa76 NR |
34 | private TTable table; |
35 | private int chapter = -99; // invalid value | |
edfd3577 | 36 | private List<TButton> navigationButtons; |
5b00c122 | 37 | private TLabel currentChapter; |
f00daa5e | 38 | private List<TSizeConstraint> sizeConstraints = new ArrayList<TSizeConstraint>(); |
c1873e56 | 39 | |
6322ab64 | 40 | // chapter: -1 for "none" (0 is desc) |
e0fb1417 NR |
41 | public TuiReaderStoryWindow(TuiReaderApplication app, Story story, |
42 | int chapter) { | |
43 | super(app, desc(story.getMeta()), 0, 0, 60, 18, CENTERED | RESIZABLE); | |
68e2c6d2 | 44 | |
e0fb1417 | 45 | this.story = story; |
c1873e56 | 46 | |
e0fb1417 | 47 | app.setStatusBar(this, desc(story.getMeta())); |
c1873e56 | 48 | |
6a26aa76 NR |
49 | // last = use window background |
50 | titleField = new TLabel(this, " Title", 0, 1, "tlabel", false); | |
f00daa5e | 51 | textField = new TText(this, "", 0, 0, 1, 1); |
494877be NR |
52 | table = new TTable(this, 0, 0, 1, 1, null, null, Arrays.asList("Key", |
53 | "Value"), true); | |
edfd3577 | 54 | |
e8796963 | 55 | titleField.setEnabled(false); |
e8796963 | 56 | |
0b7e3d78 | 57 | navigationButtons = new ArrayList<TButton>(5); |
edfd3577 | 58 | |
494877be | 59 | navigationButtons.add(addButton("<<", 0, 0, new TAction() { |
211f7ddb | 60 | @Override |
396e924c | 61 | public void DO() { |
6a26aa76 | 62 | setChapter(-1); |
396e924c | 63 | } |
edfd3577 | 64 | })); |
494877be | 65 | navigationButtons.add(addButton("< ", 4, 0, new TAction() { |
211f7ddb | 66 | @Override |
edfd3577 NR |
67 | public void DO() { |
68 | setChapter(TuiReaderStoryWindow.this.chapter - 1); | |
69 | } | |
70 | })); | |
494877be | 71 | navigationButtons.add(addButton("> ", 7, 0, new TAction() { |
211f7ddb | 72 | @Override |
edfd3577 NR |
73 | public void DO() { |
74 | setChapter(TuiReaderStoryWindow.this.chapter + 1); | |
75 | } | |
76 | })); | |
494877be | 77 | navigationButtons.add(addButton(">>", 10, 0, new TAction() { |
211f7ddb | 78 | @Override |
edfd3577 NR |
79 | public void DO() { |
80 | setChapter(getStory().getChapters().size()); | |
81 | } | |
82 | })); | |
b0e88ebd | 83 | |
8670a283 NR |
84 | navigationButtons.get(0).setEnabled(false); |
85 | navigationButtons.get(1).setEnabled(false); | |
edfd3577 | 86 | |
f00daa5e NR |
87 | currentChapter = addLabel("", 0, 0); |
88 | ||
494877be NR |
89 | TSizeConstraint.setSize(sizeConstraints, textField, 1, 3, -1, -1); |
90 | TSizeConstraint.setSize(sizeConstraints, table, 0, 3, 0, -1); | |
f00daa5e NR |
91 | TSizeConstraint.setSize(sizeConstraints, currentChapter, 14, -3, -1, |
92 | null); | |
93 | ||
94 | for (TButton navigationButton : navigationButtons) { | |
494877be NR |
95 | navigationButton.setShadowColor(null); |
96 | // navigationButton.setEmptyBorders(false); | |
f00daa5e NR |
97 | TSizeConstraint.setSize(sizeConstraints, navigationButton, null, |
98 | -3, null, null); | |
99 | } | |
100 | ||
101 | onResize(null); | |
6a26aa76 | 102 | |
edfd3577 | 103 | setChapter(chapter); |
c1873e56 NR |
104 | } |
105 | ||
106 | @Override | |
107 | public void onResize(TResizeEvent resize) { | |
f00daa5e NR |
108 | if (resize != null) { |
109 | super.onResize(resize); | |
110 | } | |
c1873e56 | 111 | |
f00daa5e NR |
112 | // TODO: find out why TText and TTable does not behave the same way |
113 | // (offset of 2 for height and width) | |
6a26aa76 | 114 | |
f00daa5e | 115 | TSizeConstraint.resize(sizeConstraints); |
edfd3577 | 116 | |
494877be NR |
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); | |
edfd3577 | 122 | |
ce424b19 | 123 | setCurrentChapterText(); |
edfd3577 NR |
124 | } |
125 | ||
5b00c122 NR |
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 | */ | |
edfd3577 | 134 | private void setChapter(int chapter) { |
edfd3577 NR |
135 | if (chapter > getStory().getChapters().size()) { |
136 | chapter = getStory().getChapters().size(); | |
137 | } | |
138 | ||
139 | if (chapter != this.chapter) { | |
140 | this.chapter = chapter; | |
b0e88ebd | 141 | |
8670a283 | 142 | int max = getStory().getChapters().size(); |
5b00c122 NR |
143 | navigationButtons.get(0).setEnabled(chapter > -1); |
144 | navigationButtons.get(1).setEnabled(chapter > -1); | |
494877be | 145 | navigationButtons.get(2).setEnabled(chapter < max); |
8670a283 | 146 | navigationButtons.get(3).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 | |
a6c830bb | 174 | Map<String, String> metaDesc = BasicReader.getMetaDesc(meta); |
df6e2d88 NR |
175 | String[][] metaDescObj = new String[metaDesc.size()][2]; |
176 | int i = 0; | |
a6c830bb NR |
177 | for (String key : metaDesc.keySet()) { |
178 | metaDescObj[i][0] = " " + key; | |
179 | metaDescObj[i][1] = metaDesc.get(key); | |
df6e2d88 | 180 | i++; |
f435c113 NR |
181 | } |
182 | ||
df6e2d88 | 183 | table.setRowData(metaDescObj); |
6a26aa76 NR |
184 | table.setHeaders(Arrays.asList("key", "value"), false); |
185 | table.toTop(); | |
5b00c122 NR |
186 | } |
187 | ||
188 | /** | |
189 | * Append the current chapter. | |
190 | * | |
191 | * @param builder | |
192 | * the builder to append to | |
193 | */ | |
6a26aa76 NR |
194 | private void displayChapterPage() { |
195 | table.setVisible(false); | |
196 | textField.setVisible(true); | |
e8796963 NR |
197 | table.setEnabled(false); |
198 | textField.setEnabled(true); | |
6a26aa76 NR |
199 | |
200 | StringBuilder builder = new StringBuilder(); | |
201 | ||
5b00c122 NR |
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(); | |
6a26aa76 | 211 | setCurrentTitle(String.format("Chapter %d: %s", chapter, chapName)); |
5b00c122 NR |
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"); | |
fdc55375 | 221 | } |
edfd3577 | 222 | } |
edfd3577 | 223 | } |
6a26aa76 NR |
224 | |
225 | setText(builder.toString()); | |
c1873e56 NR |
226 | } |
227 | ||
228 | private Story getStory() { | |
c1873e56 NR |
229 | return story; |
230 | } | |
231 | ||
5b00c122 NR |
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(); | |
ce424b19 | 266 | name = String.format("%-" + width + "s", name); |
5b00c122 NR |
267 | if (name.length() > width) { |
268 | name = name.substring(0, width); | |
269 | } | |
270 | ||
271 | currentChapter.setLabel(name); | |
c1873e56 | 272 | } |
5b00c122 NR |
273 | |
274 | /** | |
6a26aa76 | 275 | * Set the current title in-window. |
5b00c122 | 276 | * |
5b00c122 | 277 | * @param title |
6a26aa76 | 278 | * the new title |
5b00c122 | 279 | */ |
6a26aa76 | 280 | private void setCurrentTitle(String title) { |
e8796963 NR |
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; | |
6a26aa76 NR |
288 | titleField.setWidth(title.length()); |
289 | titleField.setLabel(title); | |
290 | } | |
5b00c122 | 291 | |
6a26aa76 NR |
292 | private static String desc(MetaData meta) { |
293 | return String.format("%s: %s", meta.getLuid(), meta.getTitle()); | |
5b00c122 | 294 | } |
cf9c5ed1 NR |
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 | } | |
c1873e56 | 305 | } |