1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
5 import java
.awt
.Component
;
6 import java
.awt
.event
.ActionEvent
;
7 import java
.awt
.event
.ActionListener
;
8 import java
.io
.IOException
;
9 import java
.util
.ArrayList
;
10 import java
.util
.List
;
12 import javax
.swing
.BoxLayout
;
13 import javax
.swing
.JButton
;
14 import javax
.swing
.JComboBox
;
15 import javax
.swing
.JList
;
16 import javax
.swing
.JPanel
;
17 import javax
.swing
.JTabbedPane
;
18 import javax
.swing
.JTextField
;
19 import javax
.swing
.ListCellRenderer
;
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
;
27 * This panel represents a search panel that works for keywords and tags based
32 // JCombobox<E> not 1.6 compatible
33 @SuppressWarnings({ "unchecked", "rawtypes" })
34 public class GuiReaderSearchByNamePanel
extends JPanel
{
35 private static final long serialVersionUID
= 1L;
37 private int actionEventId
= ActionEvent
.ACTION_FIRST
;
39 private SupportType supportType
;
40 private BasicSearchable searchable
;
42 private boolean searchByTags
;
44 private String keywords
;
45 private JTabbedPane searchTabs
;
46 private JTextField keywordsField
;
47 private JButton submitKeywords
;
49 private JPanel tagBars
;
50 private List
<JComboBox
> combos
;
51 private JComboBox comboSupportTypes
;
53 private List
<ActionListener
> actions
= new ArrayList
<ActionListener
>();
54 private List
<MetaData
> stories
= new ArrayList
<MetaData
>();
55 private int storyItem
;
57 // will throw illegalArgEx if bad support type
58 public GuiReaderSearchByNamePanel(SupportType supportType
) {
59 setLayout(new BorderLayout());
61 setSupportType(supportType
);
65 searchTabs
= new JTabbedPane();
66 searchTabs
.addTab("By name", createByNameSearchPanel());
67 searchTabs
.addTab("By tags", createByTagSearchPanel());
69 add(searchTabs
, BorderLayout
.CENTER
);
72 private JPanel
createByNameSearchPanel() {
73 JPanel byName
= new JPanel(new BorderLayout());
75 keywordsField
= new JTextField();
76 byName
.add(keywordsField
, BorderLayout
.CENTER
);
78 submitKeywords
= new JButton("Search");
79 byName
.add(submitKeywords
, BorderLayout
.EAST
);
81 // TODO: ENTER -> search
83 submitKeywords
.addActionListener(new ActionListener() {
85 public void actionPerformed(ActionEvent e
) {
86 search(keywordsField
.getText(), 0);
93 private JPanel
createByTagSearchPanel() {
94 combos
= new ArrayList
<JComboBox
>();
96 JPanel byTag
= new JPanel();
97 tagBars
= new JPanel();
98 tagBars
.setLayout(new BoxLayout(tagBars
, BoxLayout
.Y_AXIS
));
99 byTag
.add(tagBars
, BorderLayout
.NORTH
);
104 public SupportType
getSupportType() {
108 public void setSupportType(SupportType supportType
) {
109 BasicSearchable searchable
= BasicSearchable
.getSearchable(supportType
);
110 if (searchable
== null) {
111 throw new java
.lang
.IllegalArgumentException(
112 "Unupported support type: " + supportType
);
115 this.supportType
= supportType
;
116 this.searchable
= searchable
;
119 public int getPage() {
123 public void setPage(int page
) {
124 // TODO: set against maxPage
125 // TODO: update last search?
129 // actions will be fired in UIthread
130 public void addActionListener(ActionListener action
) {
134 public boolean removeActionListener(ActionListener action
) {
135 return actions
.remove(action
);
138 public List
<MetaData
> getStories() {
142 // selected item or 0 if none ! one-based !
143 public int getStoryItem() {
147 private void fireAction() {
148 GuiReaderSearchFrame
.inUi(new Runnable() {
151 ActionEvent ae
= new ActionEvent(
152 GuiReaderSearchByNamePanel
.this, actionEventId
,
156 if (actionEventId
> ActionEvent
.ACTION_LAST
) {
157 actionEventId
= ActionEvent
.ACTION_FIRST
;
160 for (ActionListener action
: actions
) {
162 action
.actionPerformed(ae
);
163 } catch (Exception e
) {
164 GuiReaderSearchFrame
.error(e
);
171 private void updateSearchBy(final boolean byTag
) {
172 if (byTag
!= this.searchByTags
) {
173 GuiReaderSearchFrame
.inUi(new Runnable() {
177 searchTabs
.setSelectedIndex(0);
179 searchTabs
.setSelectedIndex(1);
187 private void updateKeywords(final String keywords
) {
188 if (!keywords
.equals(this.keywords
)) {
189 GuiReaderSearchFrame
.inUi(new Runnable() {
192 GuiReaderSearchByNamePanel
.this.keywords
= keywords
;
193 keywordsField
.setText(keywords
);
199 // update and reset the tagsbar
200 // can be NULL, for base tags
201 private void updateTags(final SearchableTag tag
) {
202 final List
<SearchableTag
> parents
= new ArrayList
<SearchableTag
>();
203 SearchableTag parent
= (tag
== null) ?
null : tag
;
204 while (parent
!= null) {
206 parent
= parent
.getParent();
209 List
<SearchableTag
> rootTags
= null;
210 SearchableTag selectedRootTag
= null;
211 selectedRootTag
= parents
.isEmpty() ?
null : parents
212 .get(parents
.size() - 1);
215 rootTags
= searchable
.getTags();
216 } catch (IOException e
) {
217 GuiReaderSearchFrame
.error(e
);
220 final List
<SearchableTag
> rootTagsF
= rootTags
;
221 final SearchableTag selectedRootTagF
= selectedRootTag
;
223 GuiReaderSearchFrame
.inUi(new Runnable() {
226 tagBars
.invalidate();
229 addTagBar(rootTagsF
, selectedRootTagF
);
231 for (int i
= parents
.size() - 1; i
>= 0; i
--) {
232 SearchableTag selectedChild
= null;
234 selectedChild
= parents
.get(i
- 1);
237 SearchableTag parent
= parents
.get(i
);
238 addTagBar(parent
.getChildren(), selectedChild
);
246 // must be quick and no thread change
247 private void addTagBar(List
<SearchableTag
> tags
,
248 final SearchableTag selected
) {
251 final int comboIndex
= combos
.size();
253 final JComboBox combo
= new JComboBox(
254 tags
.toArray(new SearchableTag
[] {}));
255 combo
.setSelectedItem(selected
);
257 final ListCellRenderer basic
= combo
.getRenderer();
259 combo
.setRenderer(new ListCellRenderer() {
261 public Component
getListCellRendererComponent(JList list
,
262 Object value
, int index
, boolean isSelected
,
263 boolean cellHasFocus
) {
265 Object displayValue
= value
;
266 if (value
instanceof SearchableTag
) {
267 displayValue
= ((SearchableTag
) value
).getName();
269 displayValue
= "Select a tag...";
270 cellHasFocus
= false;
274 Component rep
= basic
.getListCellRendererComponent(list
,
275 displayValue
, index
, isSelected
, cellHasFocus
);
278 rep
.setForeground(Color
.GRAY
);
285 combo
.addActionListener(new ActionListener() {
287 public void actionPerformed(ActionEvent e
) {
288 final SearchableTag tag
= (SearchableTag
) combo
291 while (comboIndex
+ 1 < combos
.size()) {
292 JComboBox combo
= combos
.remove(comboIndex
+ 1);
293 tagBars
.remove(combo
);
296 addTagBar(tag
, new Runnable() {
300 SearchableTag tag
= ((SearchableTag
) combo
302 if (tag
!= null && tag
.isLeaf()) {
303 BasicSearchable searchable
= BasicSearchable
304 .getSearchable(supportType
);
305 List
<MetaData
> metas
= new ArrayList
<MetaData
>();
307 metas
= searchable
.search(tag
, 1);
309 searchable
.searchPages(tag
), 0);
310 } catch (IOException e
) {
315 setWaitingScreen(false);
326 // async, add children of tag, NULL = base tags
327 private void addTagBar(final SearchableTag tag
, final Runnable inUi
) {
328 new Thread(new Runnable() {
331 List
<SearchableTag
> children
= new ArrayList
<SearchableTag
>();
334 List
<SearchableTag
> baseTags
= searchable
.getTags();
336 } catch (IOException e
) {
341 searchable
.fillTag(tag
);
342 } catch (IOException e
) {
347 children
= tag
.getChildren();
353 final List
<SearchableTag
> fchildren
= children
;
354 inUi(new Runnable() {
357 if (fchildren
!= null) {
358 addTagBar(fchildren
, tag
);
370 // item 0 = no selection, else = default selection
372 public int search(String keywords
, int item
) {
373 List
<MetaData
> stories
= new ArrayList
<MetaData
>();
376 updateSearchBy(false);
377 updateKeywords(keywords
);
381 maxPage
= searchable
.searchPages(keywords
);
382 } catch (IOException e
) {
383 GuiReaderSearchFrame
.error(e
);
388 stories
= searchable
.search(keywords
, page
);
389 } catch (IOException e
) {
390 GuiReaderSearchFrame
.error(e
);
391 stories
= new ArrayList
<MetaData
>();
394 if (item
> 0 && item
<= stories
.size()) {
396 } else if (item
> 0) {
397 GuiReaderSearchFrame
.error(String
.format(
398 "Story item does not exist: Search [%s], item %d",
403 this.stories
= stories
;
404 this.storyItem
= storyItem
;
410 // tag: null = base tags
412 public int searchTag(SearchableTag tag
, int item
) {
413 List
<MetaData
> stories
= new ArrayList
<MetaData
>();
416 updateSearchBy(true);
422 searchable
.fillTag(tag
);
425 List
<SearchableTag
> subtags
= tag
.getChildren();
426 if (item
> 0 && item
<= subtags
.size()) {
427 SearchableTag subtag
= subtags
.get(item
- 1);
430 searchable
.fillTag(tag
);
431 } catch (IOException e
) {
432 GuiReaderSearchFrame
.error(e
);
434 } else if (item
> 0) {
435 GuiReaderSearchFrame
.error(String
.format(
436 "Tag item does not exist: Tag [%s], item %d",
437 tag
.getFqName(), item
));
441 maxPage
= searchable
.searchPages(tag
);
445 stories
= searchable
.search(tag
, page
);
446 if (item
> 0 && item
<= stories
.size()) {
448 } else if (item
> 0) {
451 .format("Story item does not exist: Tag [%s], item %d",
452 tag
.getFqName(), item
));
454 } catch (IOException e
) {
455 GuiReaderSearchFrame
.error(e
);
459 } catch (IOException e
) {
460 GuiReaderSearchFrame
.error(e
);
465 this.stories
= stories
;
466 this.storyItem
= storyItem
;
473 * Enables or disables this component, depending on the value of the
474 * parameter <code>b</code>. An enabled component can respond to user input
475 * and generate events. Components are enabled initially by default.
477 * Disabling this component will also affect its children.
480 * If <code>true</code>, this component is enabled; otherwise
481 * this component is disabled
484 public void setEnabled(final boolean waiting
) {
485 GuiReaderSearchFrame
.inUi(new Runnable() {
488 GuiReaderSearchByNamePanel
.super.setEnabled(!waiting
);
489 keywordsField
.setEnabled(!waiting
);
490 submitKeywords
.setEnabled(!waiting
);