GUI search
[fanfix.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderSearch.java
1 package be.nikiroo.fanfix.reader.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.EventQueue;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.io.IOException;
9 import java.lang.reflect.InvocationTargetException;
10 import java.util.ArrayList;
11 import java.util.List;
12
13 import javax.swing.JButton;
14 import javax.swing.JComboBox;
15 import javax.swing.JFrame;
16 import javax.swing.JLabel;
17 import javax.swing.JPanel;
18 import javax.swing.JScrollPane;
19 import javax.swing.JTabbedPane;
20 import javax.swing.JTextField;
21
22 import be.nikiroo.fanfix.Instance;
23 import be.nikiroo.fanfix.data.MetaData;
24 import be.nikiroo.fanfix.reader.ui.GuiReaderBook.BookActionListener;
25 import be.nikiroo.fanfix.searchable.BasicSearchable;
26 import be.nikiroo.fanfix.searchable.SearchableTag;
27 import be.nikiroo.fanfix.supported.SupportType;
28
29 /**
30 * This frame will allow you to search through the supported websites for new
31 * stories/comics.
32 *
33 * @author niki
34 */
35 public class GuiReaderSearch extends JFrame {
36 private static final long serialVersionUID = 1L;
37
38 private List<SupportType> supportTypes;
39 private SupportType supportType;
40 private boolean searchByTags;
41 private List<SearchableTag> tags;
42 private String keywords;
43 private int page;
44 private int maxPage;
45
46 private JComboBox<SupportType> comboSupportTypes;
47 private JTabbedPane searchTabs;
48 private JTextField keywordsField;
49 private JButton submitKeywords;
50
51 private boolean seeWordcount;
52 private GuiReaderGroup books;
53
54 public GuiReaderSearch(final GuiReader reader) {
55 super("Browse stories");
56 setLayout(new BorderLayout());
57 setSize(800, 600);
58
59 tags = new ArrayList<SearchableTag>();
60 page = 1; // TODO
61 maxPage = -1;
62 searchByTags = false;
63
64 supportTypes = new ArrayList<SupportType>();
65 for (SupportType type : SupportType.values()) {
66 if (BasicSearchable.getSearchable(type) != null) {
67 supportTypes.add(type);
68 }
69 }
70 supportType = supportTypes.isEmpty() ? null : supportTypes.get(0);
71
72 comboSupportTypes = new JComboBox<SupportType>(
73 supportTypes.toArray(new SupportType[] {}));
74 comboSupportTypes.addActionListener(new ActionListener() {
75 @Override
76 public void actionPerformed(ActionEvent e) {
77 updateSupportType((SupportType) comboSupportTypes
78 .getSelectedItem());
79 }
80 });
81 JPanel searchSites = new JPanel(new BorderLayout());
82 searchSites.add(comboSupportTypes, BorderLayout.CENTER);
83 searchSites.add(new JLabel(" " + "Website : "), BorderLayout.WEST);
84
85 searchTabs = new JTabbedPane();
86 searchTabs.addTab("By name", createByNameSearchPanel());
87 searchTabs.addTab("By tags", createByTagSearchPanel());
88
89 JPanel top = new JPanel(new BorderLayout());
90 top.add(searchSites, BorderLayout.NORTH);
91 top.add(searchTabs, BorderLayout.CENTER);
92
93 add(top, BorderLayout.NORTH);
94
95 books = new GuiReaderGroup(reader, null, null);
96 books.setActionListener(new BookActionListener() {
97 @Override
98 public void select(GuiReaderBook book) {
99 }
100
101 @Override
102 public void popupRequested(GuiReaderBook book, Component target,
103 int x, int y) {
104 }
105
106 @Override
107 public void action(GuiReaderBook book) {
108 new GuiReaderSearchAction(reader.getLibrary(), book.getInfo())
109 .setVisible(true);
110 }
111 });
112 JScrollPane scroll = new JScrollPane(books);
113 scroll.getVerticalScrollBar().setUnitIncrement(16);
114 add(scroll, BorderLayout.CENTER);
115 }
116
117 private JPanel createByNameSearchPanel() {
118 JPanel byName = new JPanel(new BorderLayout());
119
120 keywordsField = new JTextField();
121 byName.add(keywordsField, BorderLayout.CENTER);
122
123 submitKeywords = new JButton("Search");
124 byName.add(submitKeywords, BorderLayout.EAST);
125
126 // TODO: ENTER -> search
127
128 submitKeywords.addActionListener(new ActionListener() {
129 @Override
130 public void actionPerformed(ActionEvent e) {
131 search(supportType, keywordsField.getText(), page, 0);
132 }
133 });
134
135 return byName;
136 }
137
138 private JPanel createByTagSearchPanel() {
139 JPanel byTag = new JPanel();
140 JPanel searchBars = new JPanel();
141 add(searchBars, BorderLayout.NORTH);
142
143 return byTag;
144 }
145
146 private void updateSupportType(SupportType supportType) {
147 if (supportType != this.supportType) {
148 this.supportType = supportType;
149 comboSupportTypes.setSelectedItem(supportType);
150 books.clear();
151 // TODO: reset all tags
152 }
153 }
154
155 private void updateSearchBy(final boolean byTag) {
156 if (byTag != this.searchByTags) {
157 inUi(new Runnable() {
158 @Override
159 public void run() {
160 if (!byTag) {
161 searchTabs.setSelectedIndex(0);
162 } else {
163 searchTabs.setSelectedIndex(1);
164 }
165 }
166 });
167 }
168 }
169
170 private void updatePages(final int page, final Integer maxPage) {
171 inUi(new Runnable() {
172 @Override
173 public void run() {
174 GuiReaderSearch.this.page = page;
175 GuiReaderSearch.this.maxPage = maxPage;
176 // TODO: gui
177 System.out.println("page: " + page);
178 System.out.println("max page: " + maxPage);
179 }
180 });
181 }
182
183 // cannot be NULL
184 private void updateKeywords(final String keywords) {
185 if (!keywords.equals(this.keywords)) {
186 inUi(new Runnable() {
187 @Override
188 public void run() {
189 GuiReaderSearch.this.keywords = keywords;
190 keywordsField.setText(keywords);
191 }
192 });
193 }
194 }
195
196 // can be NULL
197 private void updateTags(final SearchableTag tag) {
198 inUi(new Runnable() {
199 @Override
200 public void run() {
201 // TODO
202 }
203 });
204 }
205
206 private void updateBooks(final List<GuiReaderBookInfo> infos) {
207 setWaitingScreen(true);
208 inUi(new Runnable() {
209 @Override
210 public void run() {
211 books.refreshBooks(infos, seeWordcount);
212 setWaitingScreen(false);
213 }
214 });
215 }
216
217 // item 0 = no selection, else = default selection
218 public void search(final SupportType searchOn, final String keywords,
219 final int page, final int item) {
220
221 setWaitingScreen(true);
222
223 updateSupportType(searchOn);
224 updateSearchBy(false);
225 updateKeywords(keywords);
226 updatePages(page, maxPage);
227
228 new Thread(new Runnable() {
229 @Override
230 public void run() {
231 BasicSearchable search = BasicSearchable
232 .getSearchable(searchOn);
233
234 if (page <= 0) {
235 int maxPage = -1;
236 try {
237 maxPage = search.searchPages(keywords);
238 } catch (IOException e) {
239 Instance.getTraceHandler().error(e);
240 }
241 updateBooks(new ArrayList<GuiReaderBookInfo>());
242 updatePages(0, maxPage);
243 } else {
244 List<GuiReaderBookInfo> infos = new ArrayList<GuiReaderBookInfo>();
245 try {
246 for (MetaData meta : search.search(keywords, page)) {
247 infos.add(GuiReaderBookInfo.fromMeta(meta));
248 }
249 } catch (IOException e) {
250 Instance.getTraceHandler().error(e);
251 }
252
253 updateBooks(infos);
254 updatePages(page, maxPage);
255
256 // ! 1-based index !
257 if (item > 0 && item <= books.getBooksCount()) {
258 // TODO: "click" on item ITEM
259 }
260 }
261
262 setWaitingScreen(false);
263 }
264 }).start();
265 }
266
267 public void searchTag(final SupportType searchOn, final int page,
268 final int item, final SearchableTag tag) {
269
270 setWaitingScreen(true);
271
272 updateSupportType(searchOn);
273 updateSearchBy(true);
274 updateTags(tag);
275 updatePages(page, maxPage);
276
277 new Thread(new Runnable() {
278 @Override
279 public void run() {
280 BasicSearchable search = BasicSearchable
281 .getSearchable(searchOn);
282
283 if (tag != null) {
284 int maxPage = 0;
285 try {
286 maxPage = search.searchPages(tag);
287 } catch (IOException e) {
288 Instance.getTraceHandler().error(e);
289 }
290
291 updatePages(page, maxPage);
292
293 if (page > 0) {
294 List<MetaData> metas = null;
295 List<SearchableTag> subtags = null;
296 int count;
297
298 if (tag.isLeaf()) {
299 try {
300 metas = search.search(tag, page);
301 } catch (IOException e) {
302 metas = new ArrayList<MetaData>();
303 Instance.getTraceHandler().error(e);
304 }
305 count = metas.size();
306 } else {
307 subtags = tag.getChildren();
308 count = subtags.size();
309 }
310
311 if (item > 0) {
312 if (item <= count) {
313 if (metas != null) {
314 MetaData meta = metas.get(item - 1);
315 // TODO: select story
316 } else {
317 SearchableTag subtag = subtags
318 .get(item - 1);
319 // TODO: search on tag
320 }
321 }
322 }
323 }
324 }
325 setWaitingScreen(false);
326 }
327 }).start();
328 }
329
330 /**
331 * Process the given action in the main Swing UI thread.
332 * <p>
333 * The code will make sure the current thread is the main UI thread and, if
334 * not, will switch to it before executing the runnable.
335 * <p>
336 * Synchronous operation.
337 *
338 * @param run
339 * the action to run
340 */
341 private void inUi(final Runnable run) {
342 if (EventQueue.isDispatchThread()) {
343 run.run();
344 } else {
345 try {
346 EventQueue.invokeAndWait(run);
347 } catch (InterruptedException e) {
348 Instance.getTraceHandler().error(e);
349 } catch (InvocationTargetException e) {
350 Instance.getTraceHandler().error(e);
351 }
352 }
353 }
354
355 private void setWaitingScreen(final boolean waiting) {
356 inUi(new Runnable() {
357 @Override
358 public void run() {
359 GuiReaderSearch.this.setEnabled(!waiting);
360 books.setEnabled(!waiting);
361 submitKeywords.setEnabled(!waiting);
362 }
363 });
364 }
365 }