Commit | Line | Data |
---|---|---|
16a81ef7 | 1 | package be.nikiroo.fanfix.reader.tui; |
c1873e56 | 2 | |
bc2ea776 | 3 | import java.io.IOException; |
c1873e56 NR |
4 | import java.util.ArrayList; |
5 | import java.util.List; | |
6 | ||
5dd985cf | 7 | import jexer.TAction; |
ce424b19 | 8 | import jexer.TComboBox; |
bce88a00 NR |
9 | import jexer.TCommand; |
10 | import jexer.TField; | |
e2d017a3 | 11 | import jexer.TFileOpenBox.Type; |
bce88a00 | 12 | import jexer.TKeypress; |
ce424b19 | 13 | import jexer.TLabel; |
5dd985cf | 14 | import jexer.TList; |
bce88a00 | 15 | import jexer.TStatusBar; |
5dd985cf | 16 | import jexer.TWindow; |
cf9c5ed1 | 17 | import jexer.event.TCommandEvent; |
bce88a00 | 18 | import jexer.event.TKeypressEvent; |
e2d017a3 | 19 | import jexer.event.TMenuEvent; |
ce424b19 | 20 | import jexer.event.TResizeEvent; |
bc2ea776 | 21 | import be.nikiroo.fanfix.Instance; |
c1873e56 | 22 | import be.nikiroo.fanfix.data.MetaData; |
bc2ea776 | 23 | import be.nikiroo.fanfix.library.BasicLibrary; |
e2d017a3 | 24 | import be.nikiroo.fanfix.output.BasicOutput.OutputType; |
16a81ef7 | 25 | import be.nikiroo.fanfix.reader.Reader; |
c1873e56 | 26 | |
6322ab64 NR |
27 | /** |
28 | * The library window, that will list all the (filtered) stories available in | |
bc2ea776 | 29 | * this {@link BasicLibrary}. |
6322ab64 NR |
30 | * |
31 | * @author niki | |
32 | */ | |
5dd985cf | 33 | class TuiReaderMainWindow extends TWindow { |
bce88a00 NR |
34 | public static final int MENU_SEARCH = 1100; |
35 | public static final TCommand CMD_SEARCH = new TCommand(MENU_SEARCH) { | |
36 | }; | |
37 | ||
b2274262 NR |
38 | public enum Mode { |
39 | SOURCE, AUTHOR, | |
40 | } | |
41 | ||
c1873e56 NR |
42 | private TList list; |
43 | private List<MetaData> listKeys; | |
44 | private List<String> listItems; | |
bc2ea776 | 45 | private Reader reader; |
b2274262 NR |
46 | |
47 | private Mode mode = Mode.SOURCE; | |
48 | private String target = null; | |
bce88a00 | 49 | private String filter = ""; |
b2274262 | 50 | |
ce424b19 NR |
51 | private List<TSizeConstraint> sizeConstraints = new ArrayList<TSizeConstraint>(); |
52 | ||
53 | // TODO: because no way to find out the current index!! | |
54 | private TComboBox select; | |
b2274262 | 55 | private TComboBox option; |
6322ab64 NR |
56 | |
57 | /** | |
58 | * Create a new {@link TuiReaderMainWindow} without any stories in the list. | |
59 | * | |
60 | * @param reader | |
61 | * the reader and main application | |
62 | */ | |
63 | public TuiReaderMainWindow(TuiReaderApplication reader) { | |
c1873e56 | 64 | // Construct a demo window. X and Y don't matter because it will be |
bc2ea776 | 65 | // centred on screen. |
4f66bfa8 | 66 | super(reader, "Library", 0, 0, 60, 18, CENTERED | RESIZABLE); |
c1873e56 NR |
67 | |
68 | this.reader = reader; | |
69 | ||
c1873e56 NR |
70 | listKeys = new ArrayList<MetaData>(); |
71 | listItems = new ArrayList<String>(); | |
e9d43c7c | 72 | |
ce424b19 NR |
73 | addSearch(); |
74 | addList(); | |
75 | addSelect(); // TODO: last so it can draw over the rest | |
5dd985cf | 76 | |
bce88a00 NR |
77 | TStatusBar statusBar = reader.setStatusBar(this, "Library"); |
78 | statusBar.addShortcutKeypress(TKeypress.kbCtrlF, CMD_SEARCH, "Search"); | |
c1873e56 | 79 | |
ce424b19 NR |
80 | TSizeConstraint.resize(sizeConstraints); |
81 | ||
bc2ea776 NR |
82 | // TODO: remove when not used anymore |
83 | ||
84 | // addLabel("Label (1,1)", 1, 1); | |
85 | // addButton("&Button (35,1)", 35, 1, new TAction() { | |
86 | // public void DO() { | |
87 | // } | |
88 | // }); | |
89 | // addCheckbox(1, 2, "Checky (1,2)", false); | |
90 | // addProgressBar(1, 3, 30, 42); | |
91 | // TRadioGroup groupy = addRadioGroup(1, 4, "Radio groupy"); | |
92 | // groupy.addRadioButton("Fanfan"); | |
93 | // groupy.addRadioButton("Tulipe"); | |
94 | // addField(1, 10, 20, false, "text not fixed."); | |
95 | // addField(1, 11, 20, true, "text fixed."); | |
96 | // addText("20x4 Text in (12,20)", 1, 12, 20, 4); | |
97 | // | |
98 | // TTreeView tree = addTreeView(30, 5, 20, 5); | |
99 | // TTreeItem root = new TTreeItem(tree, "expended root", true); | |
100 | // tree.setSelected(root); // needed to allow arrow navigation without | |
101 | // // mouse-clicking before | |
102 | // | |
103 | // root.addChild("child"); | |
104 | // root.addChild("child 2").addChild("sub child"); | |
105 | } | |
c1873e56 | 106 | |
ce424b19 NR |
107 | private void addSearch() { |
108 | TLabel lblSearch = addLabel("Search: ", 0, 0); | |
109 | ||
110 | TField search = new TField(this, 0, 0, 1, true) { | |
111 | @Override | |
112 | public void onKeypress(TKeypressEvent keypress) { | |
113 | super.onKeypress(keypress); | |
114 | TKeypress key = keypress.getKey(); | |
115 | if (key.isFnKey() && key.getKeyCode() == TKeypress.ENTER) { | |
116 | TuiReaderMainWindow.this.filter = getText(); | |
117 | TuiReaderMainWindow.this.refreshStories(); | |
118 | } | |
119 | } | |
120 | }; | |
121 | ||
122 | TSizeConstraint.setSize(sizeConstraints, lblSearch, 5, 1, null, null); | |
123 | TSizeConstraint.setSize(sizeConstraints, search, 15, 1, -5, null); | |
124 | } | |
125 | ||
126 | private void addList() { | |
127 | list = addList(listItems, 0, 0, 10, 10, new TAction() { | |
128 | @Override | |
129 | public void DO() { | |
130 | MetaData meta = getSelectedMeta(); | |
131 | if (meta != null) { | |
132 | readStory(meta); | |
133 | } | |
134 | } | |
135 | }); | |
136 | ||
137 | TSizeConstraint.setSize(sizeConstraints, list, 0, 7, 0, 0); | |
138 | } | |
139 | ||
140 | private void addSelect() { | |
141 | // TODO: make a full list | |
142 | final List<String> options = new ArrayList<String>(); | |
b2274262 | 143 | options.add("(show all)"); |
ce424b19 NR |
144 | options.add("Sources"); |
145 | options.add("Author"); | |
146 | ||
147 | // TODO | |
b2274262 NR |
148 | final List<String> selects = new ArrayList<String>(); |
149 | selects.add("(show all)"); | |
ce424b19 | 150 | for (String source : reader.getLibrary().getSources()) { |
b2274262 | 151 | selects.add(source); |
ce424b19 NR |
152 | } |
153 | ||
154 | TLabel lblSelect = addLabel("Select: ", 0, 0); | |
155 | ||
156 | // TODO: why must be last so to be able to draw over the rest | |
157 | // TODO: make it so we cannot add manual entries | |
158 | // TODO: how to select the item via keyboard? why double-click via | |
159 | // mouse? | |
160 | // TODO: how to change the values size on resize? | |
161 | // TODO: setWidth() does not impact the display width, only the control | |
162 | // and the down arrow on the right | |
163 | // TODO: width 1 +resize + click on down arrow = bad format exception | |
b2274262 NR |
164 | select = addComboBox(0, 0, 10, selects, 0, |
165 | Math.min(selects.size() + 1, getHeight() - 1 - 1), | |
ce424b19 NR |
166 | new TAction() { |
167 | @Override | |
168 | public void DO() { | |
b2274262 | 169 | // TODO: detect (show all) |
ce424b19 | 170 | if (select.getText().equals("(show all)")) { |
b2274262 | 171 | setMode(mode, null); |
ce424b19 | 172 | } else { |
b2274262 | 173 | setMode(mode, select.getText()); |
ce424b19 | 174 | } |
ce424b19 NR |
175 | } |
176 | }); | |
177 | ||
b2274262 NR |
178 | option = addComboBox(0, 0, 10, options, 0, |
179 | Math.min(selects.size() + 1, getHeight() - 1 - 1), | |
ce424b19 NR |
180 | new TAction() { |
181 | @Override | |
182 | public void DO() { | |
b2274262 NR |
183 | // TODO clear not working!! |
184 | ||
185 | String smode = option.getText(); | |
186 | Mode mode; | |
187 | if (smode == null || smode.equals("(show all)")) { | |
188 | mode = null; | |
189 | select.setVisible(false); | |
190 | select.setEnabled(false); | |
191 | } else if (smode.equals("Sources")) { | |
192 | mode = Mode.SOURCE; | |
193 | select.setVisible(true); | |
194 | select.setEnabled(true); | |
195 | selects.clear(); | |
196 | selects.add("(show all)"); | |
197 | for (String source : reader.getLibrary() | |
198 | .getSources()) { | |
199 | selects.add(source); | |
200 | } | |
201 | } else { | |
202 | mode = Mode.AUTHOR; | |
203 | select.setVisible(true); | |
204 | select.setEnabled(true); | |
205 | selects.clear(); | |
206 | selects.add("(show all)"); | |
207 | for (String author : reader.getLibrary() | |
208 | .getAuthors()) { | |
209 | selects.add(author); | |
210 | } | |
211 | } | |
212 | ||
213 | // TODO: detect (show all) | |
214 | if (select.getText().equals("(show all)")) { | |
215 | setMode(mode, null); | |
216 | } else { | |
217 | setMode(mode, select.getText()); | |
ce424b19 NR |
218 | } |
219 | } | |
220 | }); | |
221 | ||
222 | TSizeConstraint.setSize(sizeConstraints, lblSelect, 5, 3, null, null); | |
223 | TSizeConstraint.setSize(sizeConstraints, option, 15, 3, -5, null); | |
224 | TSizeConstraint.setSize(sizeConstraints, select, 15, 4, -5, null); | |
225 | } | |
226 | ||
227 | @Override | |
228 | public void onResize(TResizeEvent resize) { | |
229 | super.onResize(resize); | |
230 | TSizeConstraint.resize(sizeConstraints); | |
231 | } | |
232 | ||
4f66bfa8 NR |
233 | @Override |
234 | public void onClose() { | |
235 | setVisible(false); | |
236 | super.onClose(); | |
237 | } | |
238 | ||
e2d017a3 | 239 | /** |
bce88a00 NR |
240 | * Refresh the list of stories displayed in this library. |
241 | * <p> | |
242 | * Will take the current settings into account (filter, source...). | |
e2d017a3 | 243 | */ |
e2d017a3 | 244 | public void refreshStories() { |
b2274262 NR |
245 | List<MetaData> metas; |
246 | if (mode == Mode.SOURCE) { | |
247 | metas = reader.getLibrary().getListBySource(target); | |
248 | } else if (mode == Mode.AUTHOR) { | |
249 | metas = reader.getLibrary().getListByAuthor(target); | |
250 | } else { | |
251 | metas = reader.getLibrary().getList(); | |
252 | } | |
253 | ||
e2d017a3 NR |
254 | setMetas(metas); |
255 | } | |
256 | ||
bc2ea776 | 257 | /** |
b2274262 NR |
258 | * Change the author/source filter and display all stories matching this |
259 | * target. | |
bc2ea776 | 260 | * |
b2274262 NR |
261 | * @param mode |
262 | * the new mode or NULL for no sorting | |
263 | * @param target | |
264 | * the actual target for the given mode, or NULL for all of them | |
bc2ea776 | 265 | */ |
b2274262 NR |
266 | public void setMode(Mode mode, String target) { |
267 | this.mode = mode; | |
268 | this.target = target; | |
bce88a00 | 269 | refreshStories(); |
c1873e56 NR |
270 | } |
271 | ||
6322ab64 NR |
272 | /** |
273 | * Update the list of stories displayed in this {@link TWindow}. | |
bce88a00 NR |
274 | * <p> |
275 | * If a filter is set, only the stories which pass the filter will be | |
276 | * displayed. | |
6322ab64 NR |
277 | * |
278 | * @param metas | |
279 | * the new list of stories to display | |
280 | */ | |
e2d017a3 | 281 | private void setMetas(List<MetaData> metas) { |
6322ab64 NR |
282 | listKeys.clear(); |
283 | listItems.clear(); | |
284 | ||
285 | if (metas != null) { | |
286 | for (MetaData meta : metas) { | |
bce88a00 NR |
287 | String desc = desc(meta); |
288 | if (filter.isEmpty() | |
289 | || desc.toLowerCase().contains(filter.toLowerCase())) { | |
290 | listKeys.add(meta); | |
291 | listItems.add(desc); | |
292 | } | |
6322ab64 NR |
293 | } |
294 | } | |
295 | ||
296 | list.setList(listItems); | |
297 | } | |
298 | ||
e2d017a3 NR |
299 | public MetaData getSelectedMeta() { |
300 | if (list.getSelectedIndex() >= 0) { | |
301 | return listKeys.get(list.getSelectedIndex()); | |
302 | } | |
303 | ||
304 | return null; | |
305 | } | |
306 | ||
307 | public void readStory(MetaData meta) { | |
bc2ea776 NR |
308 | try { |
309 | reader.setChapter(-1); | |
310 | reader.setMeta(meta); | |
311 | reader.read(); | |
312 | } catch (IOException e) { | |
62c63b07 | 313 | Instance.getTraceHandler().error(e); |
bc2ea776 | 314 | } |
c1873e56 NR |
315 | } |
316 | ||
317 | private String desc(MetaData meta) { | |
318 | return String.format("%5s: %s", meta.getLuid(), meta.getTitle()); | |
319 | } | |
e2d017a3 | 320 | |
cf9c5ed1 NR |
321 | @Override |
322 | public void onCommand(TCommandEvent command) { | |
323 | if (command.getCmd().equals(TuiReaderApplication.CMD_EXIT)) { | |
324 | TuiReaderApplication.close(this); | |
325 | } else { | |
326 | // Handle our own event if needed here | |
327 | super.onCommand(command); | |
328 | } | |
329 | } | |
330 | ||
e2d017a3 NR |
331 | @Override |
332 | public void onMenu(TMenuEvent menu) { | |
333 | MetaData meta = getSelectedMeta(); | |
334 | if (meta != null) { | |
335 | switch (menu.getId()) { | |
336 | case TuiReaderApplication.MENU_OPEN: | |
337 | readStory(meta); | |
338 | ||
339 | return; | |
340 | case TuiReaderApplication.MENU_EXPORT: | |
341 | ||
342 | try { | |
343 | // TODO: choose type, pg, error | |
344 | OutputType outputType = OutputType.EPUB; | |
345 | String path = fileOpenBox(".", Type.SAVE); | |
346 | reader.getLibrary().export(meta.getLuid(), outputType, | |
347 | path, null); | |
348 | } catch (IOException e) { | |
349 | // TODO | |
350 | e.printStackTrace(); | |
351 | } | |
352 | ||
353 | return; | |
bce88a00 | 354 | |
e2d017a3 NR |
355 | case -1: |
356 | try { | |
357 | reader.getLibrary().delete(meta.getLuid()); | |
358 | } catch (IOException e) { | |
359 | // TODO | |
360 | } | |
361 | ||
362 | return; | |
363 | } | |
364 | } | |
365 | ||
366 | super.onMenu(menu); | |
367 | } | |
368 | } |