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