Commit | Line | Data |
---|---|---|
6628131d NR |
1 | package be.nikiroo.fanfix.reader.ui; |
2 | ||
3 | import java.awt.BorderLayout; | |
6628131d NR |
4 | import java.util.List; |
5 | ||
6 | import javax.swing.JPanel; | |
7 | import javax.swing.JTabbedPane; | |
8 | import javax.swing.event.ChangeEvent; | |
9 | import javax.swing.event.ChangeListener; | |
10 | ||
11 | import be.nikiroo.fanfix.data.MetaData; | |
12 | import be.nikiroo.fanfix.searchable.BasicSearchable; | |
13 | import be.nikiroo.fanfix.searchable.SearchableTag; | |
14 | import be.nikiroo.fanfix.supported.SupportType; | |
15 | ||
16 | /** | |
17 | * This panel represents a search panel that works for keywords and tags based | |
18 | * searches. | |
19 | * | |
20 | * @author niki | |
21 | */ | |
22 | public class GuiReaderSearchByPanel extends JPanel { | |
23 | private static final long serialVersionUID = 1L; | |
24 | ||
6628131d NR |
25 | private Waitable waitable; |
26 | ||
27 | private boolean searchByTags; | |
6628131d NR |
28 | private JTabbedPane searchTabs; |
29 | private GuiReaderSearchByNamePanel byName; | |
30 | private GuiReaderSearchByTagPanel byTag; | |
31 | ||
dc3b0033 NR |
32 | /** |
33 | * This interface represents an item that wan be put in "wait" mode. It is | |
34 | * supposed to be used for long running operations during which we want to | |
35 | * disable UI interactions. | |
36 | * <p> | |
37 | * It also allows reporting an event to the item. | |
38 | * | |
39 | * @author niki | |
40 | */ | |
6628131d | 41 | public interface Waitable { |
dc3b0033 NR |
42 | /** |
43 | * Set the item in wait mode, blocking it from accepting UI input. | |
44 | * | |
45 | * @param waiting | |
46 | * TRUE for wait more, FALSE to restore normal mode | |
47 | */ | |
6628131d | 48 | public void setWaiting(boolean waiting); |
dc3b0033 NR |
49 | |
50 | /** | |
51 | * Notify the {@link Waitable} that an event occured (i.e., new stories | |
52 | * were found). | |
53 | */ | |
54 | public void fireEvent(); | |
6628131d NR |
55 | } |
56 | ||
dc3b0033 NR |
57 | /** |
58 | * Create a new {@link GuiReaderSearchByPanel}. | |
59 | * | |
60 | * @param waitable | |
61 | * the waitable we can wait on for long UI operations | |
62 | */ | |
63 | public GuiReaderSearchByPanel(Waitable waitable) { | |
6628131d NR |
64 | setLayout(new BorderLayout()); |
65 | ||
66 | this.waitable = waitable; | |
67 | searchByTags = false; | |
68 | ||
dc3b0033 NR |
69 | byName = new GuiReaderSearchByNamePanel(waitable); |
70 | byTag = new GuiReaderSearchByTagPanel(waitable); | |
6628131d NR |
71 | |
72 | searchTabs = new JTabbedPane(); | |
73 | searchTabs.addTab("By name", byName); | |
74 | searchTabs.addTab("By tags", byTag); | |
75 | searchTabs.addChangeListener(new ChangeListener() { | |
76 | @Override | |
77 | public void stateChanged(ChangeEvent e) { | |
78 | searchByTags = (searchTabs.getSelectedComponent() == byTag); | |
79 | } | |
80 | }); | |
81 | ||
82 | add(searchTabs, BorderLayout.CENTER); | |
83 | updateSearchBy(searchByTags); | |
6628131d NR |
84 | } |
85 | ||
dc3b0033 NR |
86 | /** |
87 | * Set the new {@link SupportType}. | |
88 | * <p> | |
89 | * This operation can be long and should be run outside the UI thread. | |
90 | * <p> | |
91 | * Note that if a non-searchable {@link SupportType} is used, an | |
92 | * {@link IllegalArgumentException} will be thrown. | |
93 | * | |
94 | * @param supportType | |
95 | * the support mode, must be searchable or NULL | |
96 | * | |
97 | * @throws IllegalArgumentException | |
98 | * if the {@link SupportType} is not NULL but not searchable | |
99 | * (see {@link BasicSearchable#getSearchable(SupportType)}) | |
100 | */ | |
6628131d NR |
101 | public void setSupportType(SupportType supportType) { |
102 | BasicSearchable searchable = BasicSearchable.getSearchable(supportType); | |
103 | if (searchable == null && supportType != null) { | |
dc3b0033 NR |
104 | throw new IllegalArgumentException("Unupported support type: " |
105 | + supportType); | |
6628131d NR |
106 | } |
107 | ||
108 | byName.setSearchable(searchable); | |
109 | byTag.setSearchable(searchable); | |
110 | } | |
111 | ||
dc3b0033 NR |
112 | /** |
113 | * The currently displayed page of result for the current search (see the | |
114 | * <tt>page</tt> parameter of | |
115 | * {@link GuiReaderSearchByPanel#search(String, int, int)} or | |
116 | * {@link GuiReaderSearchByPanel#searchTag(SupportType, int, int, SearchableTag)} | |
117 | * ). | |
118 | * | |
119 | * @return the currently displayed page of results | |
120 | */ | |
6628131d NR |
121 | public int getPage() { |
122 | if (!searchByTags) { | |
123 | return byName.getPage(); | |
124 | } | |
125 | ||
126 | return byTag.getPage(); | |
127 | } | |
128 | ||
dc3b0033 NR |
129 | /** |
130 | * The number of pages of result for the current search (see the | |
131 | * <tt>page</tt> parameter of | |
132 | * {@link GuiReaderSearchByPanel#search(String, int, int)} or | |
133 | * {@link GuiReaderSearchByPanel#searchTag(SupportType, int, int, SearchableTag)} | |
134 | * ). | |
135 | * <p> | |
136 | * For an unknown number or when not applicable, -1 is returned. | |
137 | * | |
138 | * @return the number of pages of results or -1 | |
139 | */ | |
6628131d NR |
140 | public int getMaxPage() { |
141 | if (!searchByTags) { | |
142 | return byName.getMaxPage(); | |
143 | } | |
144 | ||
145 | return byTag.getMaxPage(); | |
146 | } | |
147 | ||
dc3b0033 NR |
148 | /** |
149 | * Set the page of results to display for the current search. This will | |
150 | * cause {@link Waitable#fireEvent()} to be called if needed. | |
151 | * <p> | |
152 | * This operation can be long and should be run outside the UI thread. | |
153 | * | |
154 | * @param page | |
155 | * the page of results to set | |
156 | * | |
157 | * @throw IndexOutOfBoundsException if the page is out of bounds | |
158 | */ | |
6628131d NR |
159 | public void setPage(int page) { |
160 | if (searchByTags) { | |
161 | searchTag(byTag.getCurrentTag(), page, 0); | |
162 | } else { | |
163 | search(byName.getCurrentKeywords(), page, 0); | |
164 | } | |
165 | } | |
166 | ||
dc3b0033 NR |
167 | /** |
168 | * The currently loaded stories (the result of the latest search). | |
169 | * | |
170 | * @return the stories | |
171 | */ | |
6628131d NR |
172 | public List<MetaData> getStories() { |
173 | if (!searchByTags) { | |
174 | return byName.getStories(); | |
175 | } | |
176 | ||
177 | return byTag.getStories(); | |
178 | } | |
179 | ||
dc3b0033 NR |
180 | /** |
181 | * Return the currently selected story (the <tt>item</tt>) if it was | |
182 | * specified in the latest, or 0 if not. | |
183 | * <p> | |
184 | * Note: this is thus a 1-based index, <b>not</b> a 0-based index. | |
185 | * | |
186 | * @return the item | |
187 | */ | |
6628131d NR |
188 | public int getStoryItem() { |
189 | if (!searchByTags) { | |
190 | return byName.getStoryItem(); | |
191 | } | |
192 | ||
193 | return byTag.getStoryItem(); | |
194 | } | |
195 | ||
dc3b0033 NR |
196 | /** |
197 | * Update the kind of searches to make: search by keywords or search by tags | |
198 | * (it will impact what the user can see and interact with on the UI). | |
199 | * | |
200 | * @param byTag | |
201 | * TRUE for tag-based searches, FALSE for keywords-based searches | |
202 | */ | |
6628131d NR |
203 | private void updateSearchBy(final boolean byTag) { |
204 | GuiReaderSearchFrame.inUi(new Runnable() { | |
205 | @Override | |
206 | public void run() { | |
207 | if (!byTag) { | |
208 | searchTabs.setSelectedIndex(0); | |
209 | } else { | |
210 | searchTabs.setSelectedIndex(1); | |
211 | } | |
212 | } | |
213 | }); | |
214 | } | |
215 | ||
dc3b0033 NR |
216 | /** |
217 | * Search for the given terms on the currently selected searchable. This | |
218 | * will cause {@link Waitable#fireEvent()} to be called if needed. | |
219 | * <p> | |
220 | * This operation can be long and should be run outside the UI thread. | |
221 | * | |
222 | * @param keywords | |
223 | * the keywords to search for | |
224 | * @param page | |
225 | * the page of results to load | |
226 | * @param item | |
227 | * the item to select (or 0 for none by default) | |
228 | * | |
229 | * @throw IndexOutOfBoundsException if the page is out of bounds | |
230 | */ | |
6628131d | 231 | public void search(final String keywords, final int page, final int item) { |
6628131d | 232 | updateSearchBy(false); |
dc3b0033 NR |
233 | byName.search(keywords, page, item); |
234 | waitable.fireEvent(); | |
6628131d NR |
235 | } |
236 | ||
dc3b0033 NR |
237 | /** |
238 | * Search for the given tag on the currently selected searchable. This will | |
239 | * cause {@link Waitable#fireEvent()} to be called if needed. | |
240 | * <p> | |
241 | * If the tag contains children tags, those will be displayed so you can | |
242 | * select them; if the tag is a leaf tag, the linked stories will be | |
243 | * displayed. | |
244 | * <p> | |
245 | * This operation can be long and should be run outside the UI thread. | |
246 | * | |
247 | * @param tag | |
248 | * the tag to search for, or NULL for base tags | |
249 | * @param page | |
250 | * the page of results to load | |
251 | * @param item | |
252 | * the item to select (or 0 for none by default) | |
253 | * | |
254 | * @throw IndexOutOfBoundsException if the page is out of bounds | |
255 | */ | |
6628131d NR |
256 | public void searchTag(final SearchableTag tag, final int page, |
257 | final int item) { | |
6628131d | 258 | updateSearchBy(true); |
dc3b0033 NR |
259 | byTag.searchTag(tag, page, item); |
260 | waitable.fireEvent(); | |
6628131d NR |
261 | } |
262 | ||
263 | /** | |
264 | * Enables or disables this component, depending on the value of the | |
265 | * parameter <code>b</code>. An enabled component can respond to user input | |
266 | * and generate events. Components are enabled initially by default. | |
267 | * <p> | |
268 | * Disabling this component will also affect its children. | |
269 | * | |
270 | * @param b | |
271 | * If <code>true</code>, this component is enabled; otherwise | |
272 | * this component is disabled | |
273 | */ | |
274 | @Override | |
275 | public void setEnabled(boolean b) { | |
276 | super.setEnabled(b); | |
277 | searchTabs.setEnabled(b); | |
278 | byName.setEnabled(b); | |
279 | byTag.setEnabled(b); | |
280 | } | |
281 | } |