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