MangaHub: fix 4.3 infinite loop
[nikiroo-utils.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderSearchFrame.java
CommitLineData
4357eb54
NR
1package be.nikiroo.fanfix.reader.ui;
2
3import java.awt.BorderLayout;
d16065ec 4import java.awt.Component;
4357eb54
NR
5import java.awt.EventQueue;
6import java.awt.event.ActionEvent;
7import java.awt.event.ActionListener;
4357eb54
NR
8import java.lang.reflect.InvocationTargetException;
9import java.util.ArrayList;
10import java.util.List;
11
4357eb54
NR
12import javax.swing.JComboBox;
13import javax.swing.JFrame;
08e2185a 14import javax.swing.JLabel;
4357eb54
NR
15import javax.swing.JPanel;
16import javax.swing.JScrollPane;
4357eb54
NR
17
18import be.nikiroo.fanfix.Instance;
19import be.nikiroo.fanfix.data.MetaData;
d16065ec 20import be.nikiroo.fanfix.reader.ui.GuiReaderBook.BookActionListener;
4357eb54
NR
21import be.nikiroo.fanfix.searchable.BasicSearchable;
22import be.nikiroo.fanfix.searchable.SearchableTag;
23import be.nikiroo.fanfix.supported.SupportType;
24
25/**
26 * This frame will allow you to search through the supported websites for new
27 * stories/comics.
28 *
29 * @author niki
30 */
6e950847
NR
31// JCombobox<E> not 1.6 compatible
32@SuppressWarnings({ "unchecked", "rawtypes" })
741e8467 33public class GuiReaderSearchFrame extends JFrame {
4357eb54
NR
34 private static final long serialVersionUID = 1L;
35
36 private List<SupportType> supportTypes;
4357eb54 37
415c7454 38 private JComboBox comboSupportTypes;
dc3b0033 39 private ActionListener comboSupportTypesListener;
9c598207 40 private GuiReaderSearchByPanel searchPanel;
cf032e29 41 private GuiReaderNavBar navbar;
4357eb54
NR
42
43 private boolean seeWordcount;
44 private GuiReaderGroup books;
45
741e8467 46 public GuiReaderSearchFrame(final GuiReader reader) {
4357eb54
NR
47 super("Browse stories");
48 setLayout(new BorderLayout());
49 setSize(800, 600);
50
4357eb54 51 supportTypes = new ArrayList<SupportType>();
dc3b0033 52 supportTypes.add(null);
4357eb54
NR
53 for (SupportType type : SupportType.values()) {
54 if (BasicSearchable.getSearchable(type) != null) {
55 supportTypes.add(type);
56 }
57 }
4357eb54 58
415c7454 59 comboSupportTypes = new JComboBox(
4357eb54 60 supportTypes.toArray(new SupportType[] {}));
dc3b0033
NR
61
62 comboSupportTypesListener = new ActionListener() {
4357eb54
NR
63 @Override
64 public void actionPerformed(ActionEvent e) {
dc3b0033
NR
65 final SupportType support = (SupportType) comboSupportTypes
66 .getSelectedItem();
9c598207 67 setWaiting(true);
dc3b0033
NR
68 new Thread(new Runnable() {
69 @Override
70 public void run() {
71 try {
72 updateSupportType(support);
73 } finally {
74 setWaiting(false);
75 }
76 }
77 }).start();
4357eb54 78 }
dc3b0033
NR
79 };
80 comboSupportTypes.addActionListener(comboSupportTypesListener);
81
08e2185a
NR
82 JPanel searchSites = new JPanel(new BorderLayout());
83 searchSites.add(comboSupportTypes, BorderLayout.CENTER);
84 searchSites.add(new JLabel(" " + "Website : "), BorderLayout.WEST);
4357eb54 85
dc3b0033 86 searchPanel = new GuiReaderSearchByPanel(
9c598207 87 new GuiReaderSearchByPanel.Waitable() {
7cc1e743 88 @Override
9c598207
NR
89 public void setWaiting(boolean waiting) {
90 GuiReaderSearchFrame.this.setWaiting(waiting);
7cc1e743 91 }
7cc1e743 92
dc3b0033
NR
93 @Override
94 public void fireEvent() {
95 updatePages(searchPanel.getPage(),
96 searchPanel.getMaxPage());
97 List<GuiReaderBookInfo> infos = new ArrayList<GuiReaderBookInfo>();
98 for (MetaData meta : searchPanel.getStories()) {
99 infos.add(GuiReaderBookInfo.fromMeta(meta));
100 }
101
cf032e29
NR
102 int page = searchPanel.getPage();
103 if (page <= 0) {
104 navbar.setMin(1);
105 navbar.setMax(1);
106 } else {
107 int max = searchPanel.getMaxPage();
108 navbar.setMin(1);
109 navbar.setMax(max);
110 navbar.setIndex(page);
111 }
dc3b0033
NR
112 updateBooks(infos);
113
114 // ! 1-based index !
115 int item = searchPanel.getStoryItem();
116 if (item > 0 && item <= books.getBooksCount()) {
117 books.setSelectedBook(item - 1, false);
118 }
119 }
120 });
4357eb54 121
08e2185a
NR
122 JPanel top = new JPanel(new BorderLayout());
123 top.add(searchSites, BorderLayout.NORTH);
ce5a42e7 124 top.add(searchPanel, BorderLayout.CENTER);
4357eb54
NR
125
126 add(top, BorderLayout.NORTH);
127
128 books = new GuiReaderGroup(reader, null, null);
d16065ec
NR
129 books.setActionListener(new BookActionListener() {
130 @Override
131 public void select(GuiReaderBook book) {
132 }
133
134 @Override
135 public void popupRequested(GuiReaderBook book, Component target,
136 int x, int y) {
137 }
138
139 @Override
140 public void action(GuiReaderBook book) {
141 new GuiReaderSearchAction(reader.getLibrary(), book.getInfo())
142 .setVisible(true);
143 }
144 });
4357eb54
NR
145 JScrollPane scroll = new JScrollPane(books);
146 scroll.getVerticalScrollBar().setUnitIncrement(16);
147 add(scroll, BorderLayout.CENTER);
cf032e29
NR
148
149 navbar = new GuiReaderNavBar(-1, -1) {
150 private static final long serialVersionUID = 1L;
151
152 @Override
153 protected String computeLabel(int index, int min, int max) {
154 if (index <= 0) {
155 return "";
156 }
157 return super.computeLabel(index, min, max);
158 }
159 };
160
161 navbar.addActionListener(new ActionListener() {
162 @Override
163 public void actionPerformed(ActionEvent e) {
164 searchPanel.setPage(navbar.getIndex());
165 }
166 });
167
168 add(navbar, BorderLayout.SOUTH);
4357eb54
NR
169 }
170
dc3b0033
NR
171 /**
172 * Update the {@link SupportType} currently displayed to the user.
173 * <p>
174 * Will also cause a search for the new base tags of the given support if
175 * not NULL.
176 * <p>
177 * This operation can be long and should be run outside the UI thread.
178 *
179 * @param supportType
180 * the new {@link SupportType}
181 */
182 private void updateSupportType(final SupportType supportType) {
183 inUi(new Runnable() {
81acd363
NR
184 @Override
185 public void run() {
dc3b0033
NR
186 books.clear();
187
188 comboSupportTypes
189 .removeActionListener(comboSupportTypesListener);
190 comboSupportTypes.setSelectedItem(supportType);
191 comboSupportTypes.addActionListener(comboSupportTypesListener);
7cc1e743 192 }
dc3b0033
NR
193 });
194
195 searchPanel.setSupportType(supportType);
7cc1e743
NR
196 }
197
dc3b0033
NR
198 /**
199 * Update the pages and the lined buttons currently displayed on screen.
200 * <p>
201 * Those are the same pages and maximum pages used by
202 * {@link GuiReaderSearchByPanel#search(String, int, int)} and
203 * {@link GuiReaderSearchByPanel#searchTag(SearchableTag, int, int)}.
204 *
205 * @param page
206 * the current page of results
207 * @param maxPage
208 * the maximum number of pages of results
209 */
9c598207 210 private void updatePages(final int page, final int maxPage) {
7cc1e743
NR
211 inUi(new Runnable() {
212 @Override
213 public void run() {
cf032e29
NR
214 if (maxPage >= 1) {
215 navbar.setMin(1);
216 navbar.setMax(maxPage);
217 navbar.setIndex(page);
218 } else {
219 navbar.setMin(-1);
220 navbar.setMax(-1);
221 }
81acd363
NR
222 }
223 });
224 }
225
dc3b0033
NR
226 /**
227 * Update the currently displayed books.
228 *
229 * @param infos
230 * the new books
231 */
81acd363
NR
232 private void updateBooks(final List<GuiReaderBookInfo> infos) {
233 inUi(new Runnable() {
234 @Override
235 public void run() {
236 books.refreshBooks(infos, seeWordcount);
237 }
238 });
239 }
240
dc3b0033
NR
241 /**
242 * Search for the given terms on the currently selected searchable. This
243 * will update the displayed books if needed.
244 * <p>
245 * This operation is asynchronous.
246 *
247 * @param keywords
248 * the keywords to search for
249 * @param page
250 * the page of results to load
251 * @param item
252 * the item to select (or 0 for none by default)
253 */
4357eb54
NR
254 public void search(final SupportType searchOn, final String keywords,
255 final int page, final int item) {
9c598207
NR
256 setWaiting(true);
257 new Thread(new Runnable() {
7cc1e743
NR
258 @Override
259 public void run() {
dc3b0033
NR
260 try {
261 updateSupportType(searchOn);
262 searchPanel.search(keywords, page, item);
263 } finally {
264 setWaiting(false);
265 }
7cc1e743 266 }
9c598207 267 }).start();
4357eb54
NR
268 }
269
dc3b0033
NR
270 /**
271 * Search for the given tag on the currently selected searchable. This will
272 * update the displayed books if needed.
273 * <p>
274 * If the tag contains children tags, those will be displayed so you can
275 * select them; if the tag is a leaf tag, the linked stories will be
276 * displayed.
277 * <p>
278 * This operation is asynchronous.
279 *
280 * @param tag
281 * the tag to search for, or NULL for base tags
282 * @param page
283 * the page of results to load
284 * @param item
285 * the item to select (or 0 for none by default)
286 */
c499d79f
NR
287 public void searchTag(final SupportType searchOn, final int page,
288 final int item, final SearchableTag tag) {
9c598207
NR
289 setWaiting(true);
290 new Thread(new Runnable() {
c499d79f
NR
291 @Override
292 public void run() {
dc3b0033
NR
293 try {
294 updateSupportType(searchOn);
295 searchPanel.searchTag(tag, page, item);
296 } finally {
297 setWaiting(false);
298 }
4357eb54 299 }
9c598207 300 }).start();
bf2b37b0
NR
301 }
302
4357eb54
NR
303 /**
304 * Process the given action in the main Swing UI thread.
305 * <p>
306 * The code will make sure the current thread is the main UI thread and, if
307 * not, will switch to it before executing the runnable.
308 * <p>
309 * Synchronous operation.
310 *
311 * @param run
312 * the action to run
313 */
741e8467 314 static void inUi(final Runnable run) {
4357eb54
NR
315 if (EventQueue.isDispatchThread()) {
316 run.run();
317 } else {
318 try {
319 EventQueue.invokeAndWait(run);
320 } catch (InterruptedException e) {
bf2b37b0 321 error(e);
4357eb54 322 } catch (InvocationTargetException e) {
bf2b37b0 323 error(e);
4357eb54
NR
324 }
325 }
326 }
c499d79f 327
dc3b0033
NR
328 /**
329 * An error occurred, inform the user and/or log the error.
330 *
331 * @param e
332 * the error
333 */
741e8467 334 static void error(Exception e) {
bf2b37b0
NR
335 Instance.getTraceHandler().error(e);
336 }
337
dc3b0033
NR
338 /**
339 * An error occurred, inform the user and/or log the error.
340 *
341 * @param e
342 * the error message
343 */
741e8467
NR
344 static void error(String e) {
345 Instance.getTraceHandler().error(e);
346 }
7cc1e743 347
9c598207
NR
348 /**
349 * Enables or disables this component, depending on the value of the
350 * parameter <code>b</code>. An enabled component can respond to user input
351 * and generate events. Components are enabled initially by default.
352 * <p>
353 * Disabling this component will also affect its children.
354 *
355 * @param b
356 * If <code>true</code>, this component is enabled; otherwise
357 * this component is disabled
358 */
359 @Override
360 public void setEnabled(boolean b) {
361 super.setEnabled(b);
362 books.setEnabled(b);
363 searchPanel.setEnabled(b);
364 }
365
dc3b0033
NR
366 /**
367 * Set the item in wait mode, blocking it from accepting UI input.
368 *
369 * @param waiting
370 * TRUE for wait more, FALSE to restore normal mode
371 */
9c598207 372 private void setWaiting(final boolean waiting) {
c499d79f
NR
373 inUi(new Runnable() {
374 @Override
375 public void run() {
741e8467 376 GuiReaderSearchFrame.this.setEnabled(!waiting);
c499d79f
NR
377 }
378 });
379 }
4357eb54 380}