GUI search: forgot 2 filesgit add src/be/nikiroo/fanfix/reader/ui/GuiReaderSearchBy*!
[fanfix.git] / src / be / nikiroo / fanfix / reader / ui / GuiReaderSearchByPanel.java
1 package be.nikiroo.fanfix.reader.ui;
2
3 import java.awt.BorderLayout;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.util.ArrayList;
7 import java.util.List;
8
9 import javax.swing.JPanel;
10 import javax.swing.JTabbedPane;
11 import javax.swing.event.ChangeEvent;
12 import javax.swing.event.ChangeListener;
13
14 import be.nikiroo.fanfix.data.MetaData;
15 import be.nikiroo.fanfix.searchable.BasicSearchable;
16 import be.nikiroo.fanfix.searchable.SearchableTag;
17 import be.nikiroo.fanfix.supported.SupportType;
18
19 /**
20 * This panel represents a search panel that works for keywords and tags based
21 * searches.
22 *
23 * @author niki
24 */
25 public class GuiReaderSearchByPanel extends JPanel {
26 private static final long serialVersionUID = 1L;
27
28 private int actionEventId = ActionEvent.ACTION_FIRST;
29
30 private Waitable waitable;
31
32 private boolean searchByTags;
33
34 private JTabbedPane searchTabs;
35 private GuiReaderSearchByNamePanel byName;
36 private GuiReaderSearchByTagPanel byTag;
37
38 private List<ActionListener> actions = new ArrayList<ActionListener>();
39
40 public interface Waitable {
41 public void setWaiting(boolean waiting);
42 }
43
44 // will throw illegalArgEx if bad support type, NULL allowed
45 public GuiReaderSearchByPanel(final SupportType supportType,
46 Waitable waitable) {
47 setLayout(new BorderLayout());
48
49 this.waitable = waitable;
50 searchByTags = false;
51
52 Runnable fireEvent = new Runnable() {
53 @Override
54 public void run() {
55 fireAction();
56 }
57 };
58
59 byName = new GuiReaderSearchByNamePanel(fireEvent);
60 byTag = new GuiReaderSearchByTagPanel(fireEvent);
61
62 searchTabs = new JTabbedPane();
63 searchTabs.addTab("By name", byName);
64 searchTabs.addTab("By tags", byTag);
65 searchTabs.addChangeListener(new ChangeListener() {
66 @Override
67 public void stateChanged(ChangeEvent e) {
68 searchByTags = (searchTabs.getSelectedComponent() == byTag);
69 }
70 });
71
72 add(searchTabs, BorderLayout.CENTER);
73 updateSearchBy(searchByTags);
74 setSupportType(supportType);
75 }
76
77 public void setSupportType(SupportType supportType) {
78 BasicSearchable searchable = BasicSearchable.getSearchable(supportType);
79 if (searchable == null && supportType != null) {
80 throw new java.lang.IllegalArgumentException(
81 "Unupported support type: " + supportType);
82 }
83
84 byName.setSearchable(searchable);
85 byTag.setSearchable(searchable);
86 }
87
88 public int getPage() {
89 if (!searchByTags) {
90 return byName.getPage();
91 }
92
93 return byTag.getPage();
94 }
95
96 public int getMaxPage() {
97 if (!searchByTags) {
98 return byName.getMaxPage();
99 }
100
101 return byTag.getMaxPage();
102 }
103
104 // throw outOfBounds if needed
105 public void setPage(int page) {
106 if (searchByTags) {
107 searchTag(byTag.getCurrentTag(), page, 0);
108 } else {
109 search(byName.getCurrentKeywords(), page, 0);
110 }
111 }
112
113 // actions will be fired in UIthread
114 public void addActionListener(ActionListener action) {
115 actions.add(action);
116 }
117
118 public boolean removeActionListener(ActionListener action) {
119 return actions.remove(action);
120 }
121
122 public List<MetaData> getStories() {
123 if (!searchByTags) {
124 return byName.getStories();
125 }
126
127 return byTag.getStories();
128 }
129
130 // selected item or 0 if none ! one-based !
131 public int getStoryItem() {
132 if (!searchByTags) {
133 return byName.getStoryItem();
134 }
135
136 return byTag.getStoryItem();
137 }
138
139 private void fireAction() {
140 GuiReaderSearchFrame.inUi(new Runnable() {
141 @Override
142 public void run() {
143 ActionEvent ae = new ActionEvent(GuiReaderSearchByPanel.this,
144 actionEventId, "stories found");
145
146 actionEventId++;
147 if (actionEventId > ActionEvent.ACTION_LAST) {
148 actionEventId = ActionEvent.ACTION_FIRST;
149 }
150
151 for (ActionListener action : actions) {
152 try {
153 action.actionPerformed(ae);
154 } catch (Exception e) {
155 GuiReaderSearchFrame.error(e);
156 }
157 }
158 }
159 });
160 }
161
162 private void updateSearchBy(final boolean byTag) {
163 GuiReaderSearchFrame.inUi(new Runnable() {
164 @Override
165 public void run() {
166 if (!byTag) {
167 searchTabs.setSelectedIndex(0);
168 } else {
169 searchTabs.setSelectedIndex(1);
170 }
171 }
172 });
173 }
174
175 // slow, start in UI mode
176 public void search(final String keywords, final int page, final int item) {
177 waitable.setWaiting(true);
178 updateSearchBy(false);
179 new Thread(new Runnable() {
180 @Override
181 public void run() {
182 try {
183 byName.search(keywords, page, item);
184 fireAction();
185 } finally {
186 waitable.setWaiting(false);
187 }
188 }
189 }).start();
190 }
191
192 // slow, start in UI mode
193 // tag: null = base tags
194 public void searchTag(final SearchableTag tag, final int page,
195 final int item) {
196 waitable.setWaiting(true);
197 updateSearchBy(true);
198 new Thread(new Runnable() {
199 @Override
200 public void run() {
201 try {
202
203 byTag.searchTag(tag, page, item);
204 fireAction();
205 } finally {
206 waitable.setWaiting(false);
207 }
208 }
209 }).start();
210 }
211
212 /**
213 * Enables or disables this component, depending on the value of the
214 * parameter <code>b</code>. An enabled component can respond to user input
215 * and generate events. Components are enabled initially by default.
216 * <p>
217 * Disabling this component will also affect its children.
218 *
219 * @param b
220 * If <code>true</code>, this component is enabled; otherwise
221 * this component is disabled
222 */
223 @Override
224 public void setEnabled(boolean b) {
225 super.setEnabled(b);
226 searchTabs.setEnabled(b);
227 byName.setEnabled(b);
228 byTag.setEnabled(b);
229 }
230 }