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