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