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