1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
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
;
14 import javax
.swing
.BoxLayout
;
15 import javax
.swing
.JButton
;
16 import javax
.swing
.JComboBox
;
17 import javax
.swing
.JFrame
;
18 import javax
.swing
.JLabel
;
19 import javax
.swing
.JList
;
20 import javax
.swing
.JPanel
;
21 import javax
.swing
.JScrollPane
;
22 import javax
.swing
.JTabbedPane
;
23 import javax
.swing
.JTextField
;
24 import javax
.swing
.ListCellRenderer
;
26 import be
.nikiroo
.fanfix
.Instance
;
27 import be
.nikiroo
.fanfix
.data
.MetaData
;
28 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderBook
.BookActionListener
;
29 import be
.nikiroo
.fanfix
.searchable
.BasicSearchable
;
30 import be
.nikiroo
.fanfix
.searchable
.SearchableTag
;
31 import be
.nikiroo
.fanfix
.supported
.SupportType
;
34 * This frame will allow you to search through the supported websites for new
39 public class GuiReaderSearch
extends JFrame
{
40 private static final long serialVersionUID
= 1L;
42 private List
<SupportType
> supportTypes
;
43 private SupportType supportType
;
44 private boolean searchByTags
;
45 private List
<SearchableTag
> tags
;
46 private String keywords
;
50 private JPanel tagBars
;
52 private JComboBox
<SupportType
> comboSupportTypes
;
53 private JTabbedPane searchTabs
;
54 private JTextField keywordsField
;
55 private JButton submitKeywords
;
57 private boolean seeWordcount
;
58 private GuiReaderGroup books
;
60 public GuiReaderSearch(final GuiReader reader
) {
61 super("Browse stories");
62 setLayout(new BorderLayout());
65 tags
= new ArrayList
<SearchableTag
>();
70 supportTypes
= new ArrayList
<SupportType
>();
71 for (SupportType type
: SupportType
.values()) {
72 if (BasicSearchable
.getSearchable(type
) != null) {
73 supportTypes
.add(type
);
76 supportType
= supportTypes
.isEmpty() ?
null : supportTypes
.get(0);
78 comboSupportTypes
= new JComboBox
<SupportType
>(
79 supportTypes
.toArray(new SupportType
[] {}));
80 comboSupportTypes
.addActionListener(new ActionListener() {
82 public void actionPerformed(ActionEvent e
) {
83 updateSupportType((SupportType
) comboSupportTypes
87 JPanel searchSites
= new JPanel(new BorderLayout());
88 searchSites
.add(comboSupportTypes
, BorderLayout
.CENTER
);
89 searchSites
.add(new JLabel(" " + "Website : "), BorderLayout
.WEST
);
91 searchTabs
= new JTabbedPane();
92 searchTabs
.addTab("By name", createByNameSearchPanel());
93 searchTabs
.addTab("By tags", createByTagSearchPanel());
95 JPanel top
= new JPanel(new BorderLayout());
96 top
.add(searchSites
, BorderLayout
.NORTH
);
97 top
.add(searchTabs
, BorderLayout
.CENTER
);
99 add(top
, BorderLayout
.NORTH
);
101 books
= new GuiReaderGroup(reader
, null, null);
102 books
.setActionListener(new BookActionListener() {
104 public void select(GuiReaderBook book
) {
108 public void popupRequested(GuiReaderBook book
, Component target
,
113 public void action(GuiReaderBook book
) {
114 new GuiReaderSearchAction(reader
.getLibrary(), book
.getInfo())
118 JScrollPane scroll
= new JScrollPane(books
);
119 scroll
.getVerticalScrollBar().setUnitIncrement(16);
120 add(scroll
, BorderLayout
.CENTER
);
125 private JPanel
createByNameSearchPanel() {
126 JPanel byName
= new JPanel(new BorderLayout());
128 keywordsField
= new JTextField();
129 byName
.add(keywordsField
, BorderLayout
.CENTER
);
131 submitKeywords
= new JButton("Search");
132 byName
.add(submitKeywords
, BorderLayout
.EAST
);
134 // TODO: ENTER -> search
136 submitKeywords
.addActionListener(new ActionListener() {
138 public void actionPerformed(ActionEvent e
) {
139 search(supportType
, keywordsField
.getText(), page
, 0);
146 private JPanel
createByTagSearchPanel() {
147 JPanel byTag
= new JPanel();
148 tagBars
= new JPanel();
149 tagBars
.setLayout(new BoxLayout(tagBars
, BoxLayout
.Y_AXIS
));
150 byTag
.add(tagBars
, BorderLayout
.NORTH
);
155 private void updateSupportType(SupportType supportType
) {
156 if (supportType
!= this.supportType
) {
157 this.supportType
= supportType
;
158 comboSupportTypes
.setSelectedItem(supportType
);
164 private void updateSearchBy(final boolean byTag
) {
165 if (byTag
!= this.searchByTags
) {
166 inUi(new Runnable() {
170 searchTabs
.setSelectedIndex(0);
172 searchTabs
.setSelectedIndex(1);
179 private void updatePages(final int page
, final Integer maxPage
) {
180 inUi(new Runnable() {
183 GuiReaderSearch
.this.page
= page
;
184 GuiReaderSearch
.this.maxPage
= maxPage
;
186 System
.out
.println("page: " + page
);
187 System
.out
.println("max page: " + maxPage
);
193 private void updateKeywords(final String keywords
) {
194 if (!keywords
.equals(this.keywords
)) {
195 inUi(new Runnable() {
198 GuiReaderSearch
.this.keywords
= keywords
;
199 keywordsField
.setText(keywords
);
205 // can be NULL, for base tags
206 private void updateTags(final SearchableTag tag
) {
207 final List
<SearchableTag
> parents
= new ArrayList
<SearchableTag
>();
208 SearchableTag parent
= (tag
== null) ?
null : tag
;
209 while (parent
!= null) {
211 parent
= parent
.getParent();
214 inUi(new Runnable() {
217 tagBars
.invalidate();
221 // TODO: select the right one
223 addTagBar(BasicSearchable
.getSearchable(supportType
)
225 } catch (IOException e
) {
229 for (int i
= parents
.size() - 1; i
>= 0; i
--) {
230 SearchableTag parent
= parents
.get(i
);
231 addTagBar(parent
.getChildren(), parent
);
239 private void updateBooks(final List
<GuiReaderBookInfo
> infos
) {
240 setWaitingScreen(true);
241 inUi(new Runnable() {
244 books
.refreshBooks(infos
, seeWordcount
);
245 setWaitingScreen(false);
250 private void addTagBar(List
<SearchableTag
> tags
,
251 final SearchableTag selected
) {
254 final JComboBox
<SearchableTag
> combo
= new JComboBox
<SearchableTag
>(
255 tags
.toArray(new SearchableTag
[] {}));
256 combo
.setSelectedItem(selected
);
258 // We want to pass it a String
259 @SuppressWarnings({ "rawtypes" })
260 final ListCellRenderer basic
= combo
.getRenderer();
262 combo
.setRenderer(new ListCellRenderer
<SearchableTag
>() {
264 public Component
getListCellRendererComponent(
265 JList
<?
extends SearchableTag
> list
, SearchableTag value
,
266 int index
, boolean isSelected
, boolean cellHasFocus
) {
268 Object displayValue
= value
;
270 displayValue
= "Select a tag...";
271 cellHasFocus
= false;
274 displayValue
= value
.getName();
277 // We willingly pass a String here
278 @SuppressWarnings("unchecked")
279 Component rep
= basic
.getListCellRendererComponent(list
,
280 displayValue
, index
, isSelected
, cellHasFocus
);
283 rep
.setForeground(Color
.GRAY
);
290 combo
.addActionListener(new ActionListener() {
292 public void actionPerformed(ActionEvent e
) {
293 final SearchableTag tag
= (SearchableTag
) combo
296 addTagBar(tag
, new Runnable() {
299 // TODO: stories if needed
300 setWaitingScreen(false);
310 // async, add children of tag, NULL = base tags
311 private void addTagBar(final SearchableTag tag
, final Runnable inUi
) {
312 new Thread(new Runnable() {
315 BasicSearchable searchable
= BasicSearchable
316 .getSearchable(supportType
);
318 List
<SearchableTag
> children
= new ArrayList
<SearchableTag
>();
321 List
<SearchableTag
> baseTags
= searchable
.getTags();
323 } catch (IOException e
) {
328 searchable
.fillTag(tag
);
329 } catch (IOException e
) {
334 children
= tag
.getChildren();
341 final List
<SearchableTag
> fchildren
= children
;
342 inUi(new Runnable() {
345 if (fchildren
!= null) {
346 addTagBar(fchildren
, tag
);
358 // item 0 = no selection, else = default selection
359 public void search(final SupportType searchOn
, final String keywords
,
360 final int page
, final int item
) {
362 setWaitingScreen(true);
364 updateSupportType(searchOn
);
365 updateSearchBy(false);
366 updateKeywords(keywords
);
367 updatePages(page
, maxPage
);
369 new Thread(new Runnable() {
372 BasicSearchable search
= BasicSearchable
373 .getSearchable(searchOn
);
377 maxPage
= search
.searchPages(keywords
);
378 } catch (IOException e
) {
383 updateBooks(new ArrayList
<GuiReaderBookInfo
>());
384 updatePages(0, maxPage
);
386 List
<MetaData
> results
;
388 results
= search
.search(keywords
, page
);
389 } catch (IOException e
) {
391 results
= new ArrayList
<MetaData
>();
394 search(results
, page
, maxPage
, item
);
397 if (item
> 0 && item
<= books
.getBooksCount()) {
398 // TODO: "click" on item ITEM
402 setWaitingScreen(false);
407 // tag: must be filled (or NULL for base tags)
408 public void searchTag(final SupportType searchOn
, final int page
,
409 final int item
, final SearchableTag tag
) {
411 setWaitingScreen(true);
413 updateSupportType(searchOn
);
414 updateSearchBy(true);
416 updatePages(page
, maxPage
);
418 new Thread(new Runnable() {
421 BasicSearchable search
= BasicSearchable
422 .getSearchable(searchOn
);
427 maxPage
= search
.searchPages(tag
);
428 } catch (IOException e
) {
432 updatePages(page
, maxPage
);
435 List
<MetaData
> metas
= new ArrayList
<MetaData
>();
439 metas
= search
.search(tag
, page
);
440 } catch (IOException e
) {
444 List
<SearchableTag
> subtags
= tag
.getChildren();
445 if (item
> 0 && item
<= subtags
.size()) {
446 SearchableTag subtag
= subtags
.get(item
- 1);
448 metas
= search
.search(subtag
, page
);
449 maxPage
= subtag
.getPages();
450 } catch (IOException e
) {
456 updatePages(page
, maxPage
);
457 search(metas
, page
, maxPage
, item
);
461 setWaitingScreen(false);
466 // item 0 = no selection, else = default selection
467 public void search(final List
<MetaData
> results
, final int page
,
468 final int maxPage
, final int item
) {
470 updatePages(page
, maxPage
);
473 updateBooks(new ArrayList
<GuiReaderBookInfo
>());
474 updatePages(0, maxPage
);
476 List
<GuiReaderBookInfo
> infos
= new ArrayList
<GuiReaderBookInfo
>();
477 for (MetaData meta
: results
) {
478 infos
.add(GuiReaderBookInfo
.fromMeta(meta
));
484 if (item
> 0 && item
<= books
.getBooksCount()) {
485 // TODO: "click" on item ITEM
491 * Process the given action in the main Swing UI thread.
493 * The code will make sure the current thread is the main UI thread and, if
494 * not, will switch to it before executing the runnable.
496 * Synchronous operation.
501 private void inUi(final Runnable run
) {
502 if (EventQueue
.isDispatchThread()) {
506 EventQueue
.invokeAndWait(run
);
507 } catch (InterruptedException e
) {
509 } catch (InvocationTargetException e
) {
515 private void error(Exception e
) {
516 Instance
.getTraceHandler().error(e
);
519 private void setWaitingScreen(final boolean waiting
) {
520 inUi(new Runnable() {
523 GuiReaderSearch
.this.setEnabled(!waiting
);
524 books
.setEnabled(!waiting
);
525 submitKeywords
.setEnabled(!waiting
);