Commit | Line | Data |
---|---|---|
741e8467 NR |
1 | package be.nikiroo.fanfix.reader.ui; |
2 | ||
3 | import java.awt.BorderLayout; | |
741e8467 NR |
4 | import java.awt.event.ActionEvent; |
5 | import java.awt.event.ActionListener; | |
7cc1e743 NR |
6 | import java.awt.event.KeyAdapter; |
7 | import java.awt.event.KeyEvent; | |
741e8467 NR |
8 | import java.io.IOException; |
9 | import java.util.ArrayList; | |
10 | import java.util.List; | |
11 | ||
741e8467 | 12 | import javax.swing.JButton; |
741e8467 | 13 | import javax.swing.JPanel; |
741e8467 | 14 | import javax.swing.JTextField; |
741e8467 NR |
15 | |
16 | import be.nikiroo.fanfix.data.MetaData; | |
dc3b0033 | 17 | import be.nikiroo.fanfix.reader.ui.GuiReaderSearchByPanel.Waitable; |
741e8467 | 18 | import be.nikiroo.fanfix.searchable.BasicSearchable; |
741e8467 NR |
19 | |
20 | /** | |
21 | * This panel represents a search panel that works for keywords and tags based | |
22 | * searches. | |
23 | * | |
24 | * @author niki | |
25 | */ | |
741e8467 NR |
26 | public class GuiReaderSearchByNamePanel extends JPanel { |
27 | private static final long serialVersionUID = 1L; | |
28 | ||
741e8467 | 29 | private BasicSearchable searchable; |
7cc1e743 | 30 | |
741e8467 NR |
31 | private JTextField keywordsField; |
32 | private JButton submitKeywords; | |
33 | ||
9c598207 NR |
34 | private int page; |
35 | private int maxPage; | |
741e8467 NR |
36 | private List<MetaData> stories = new ArrayList<MetaData>(); |
37 | private int storyItem; | |
38 | ||
dc3b0033 | 39 | public GuiReaderSearchByNamePanel(final Waitable waitable) { |
9c598207 | 40 | super(new BorderLayout()); |
741e8467 NR |
41 | |
42 | keywordsField = new JTextField(); | |
9c598207 | 43 | add(keywordsField, BorderLayout.CENTER); |
741e8467 NR |
44 | |
45 | submitKeywords = new JButton("Search"); | |
9c598207 | 46 | add(submitKeywords, BorderLayout.EAST); |
741e8467 | 47 | |
dc3b0033 NR |
48 | // should be done out of UI |
49 | final Runnable go = new Runnable() { | |
50 | @Override | |
51 | public void run() { | |
52 | waitable.setWaiting(true); | |
53 | try { | |
54 | search(keywordsField.getText(), 1, 0); | |
55 | waitable.fireEvent(); | |
56 | } finally { | |
57 | waitable.setWaiting(false); | |
58 | } | |
59 | } | |
60 | }; | |
61 | ||
7cc1e743 NR |
62 | keywordsField.addKeyListener(new KeyAdapter() { |
63 | @Override | |
64 | public void keyReleased(KeyEvent e) { | |
65 | if (e.getKeyCode() == KeyEvent.VK_ENTER) { | |
dc3b0033 | 66 | new Thread(go).start(); |
7cc1e743 NR |
67 | } else { |
68 | super.keyReleased(e); | |
69 | } | |
70 | } | |
71 | }); | |
72 | ||
741e8467 NR |
73 | submitKeywords.addActionListener(new ActionListener() { |
74 | @Override | |
75 | public void actionPerformed(ActionEvent e) { | |
dc3b0033 | 76 | new Thread(go).start(); |
741e8467 NR |
77 | } |
78 | }); | |
79 | ||
9c598207 | 80 | setSearchable(null); |
741e8467 NR |
81 | } |
82 | ||
9c598207 NR |
83 | /** |
84 | * The {@link BasicSearchable} object use for the searches themselves. | |
85 | * <p> | |
86 | * Can be NULL, but no searches will work. | |
87 | * | |
88 | * @param searchable | |
89 | * the new searchable | |
90 | */ | |
91 | public void setSearchable(BasicSearchable searchable) { | |
741e8467 | 92 | this.searchable = searchable; |
cf032e29 | 93 | page = 0; |
9c598207 NR |
94 | maxPage = -1; |
95 | storyItem = 0; | |
96 | stories = new ArrayList<MetaData>(); | |
97 | updateKeywords(""); | |
741e8467 NR |
98 | } |
99 | ||
dc3b0033 NR |
100 | /** |
101 | * The currently displayed page of result for the current search (see the | |
102 | * <tt>page</tt> parameter of | |
103 | * {@link GuiReaderSearchByNamePanel#search(String, int, int)}). | |
104 | * | |
105 | * @return the currently displayed page of results | |
106 | */ | |
741e8467 NR |
107 | public int getPage() { |
108 | return page; | |
109 | } | |
110 | ||
dc3b0033 NR |
111 | /** |
112 | * The number of pages of result for the current search (see the | |
113 | * <tt>page</tt> parameter of | |
114 | * {@link GuiReaderSearchByPanel#search(String, int, int)}). | |
115 | * <p> | |
116 | * For an unknown number or when not applicable, -1 is returned. | |
117 | * | |
118 | * @return the number of pages of results or -1 | |
119 | */ | |
7cc1e743 NR |
120 | public int getMaxPage() { |
121 | return maxPage; | |
122 | } | |
123 | ||
dc3b0033 NR |
124 | /** |
125 | * Return the keywords used for the current search. | |
126 | * | |
127 | * @return the keywords | |
128 | */ | |
9c598207 NR |
129 | public String getCurrentKeywords() { |
130 | return keywordsField.getText(); | |
741e8467 NR |
131 | } |
132 | ||
dc3b0033 NR |
133 | /** |
134 | * The currently loaded stories (the result of the latest search). | |
135 | * | |
136 | * @return the stories | |
137 | */ | |
741e8467 NR |
138 | public List<MetaData> getStories() { |
139 | return stories; | |
140 | } | |
141 | ||
dc3b0033 NR |
142 | /** |
143 | * Return the currently selected story (the <tt>item</tt>) if it was | |
144 | * specified in the latest, or 0 if not. | |
145 | * <p> | |
146 | * Note: this is thus a 1-based index, <b>not</b> a 0-based index. | |
147 | * | |
148 | * @return the item | |
149 | */ | |
741e8467 NR |
150 | public int getStoryItem() { |
151 | return storyItem; | |
152 | } | |
153 | ||
dc3b0033 NR |
154 | /** |
155 | * Update the keywords displayed on screen. | |
156 | * | |
157 | * @param keywords | |
158 | * the keywords | |
159 | */ | |
741e8467 | 160 | private void updateKeywords(final String keywords) { |
7cc1e743 | 161 | if (!keywords.equals(keywordsField.getText())) { |
741e8467 NR |
162 | GuiReaderSearchFrame.inUi(new Runnable() { |
163 | @Override | |
164 | public void run() { | |
741e8467 NR |
165 | keywordsField.setText(keywords); |
166 | } | |
167 | }); | |
168 | } | |
169 | } | |
170 | ||
dc3b0033 NR |
171 | /** |
172 | * Search for the given terms on the currently selected searchable. | |
173 | * <p> | |
174 | * This operation can be long and should be run outside the UI thread. | |
175 | * | |
176 | * @param keywords | |
177 | * the keywords to search for | |
178 | * @param page | |
179 | * the page of results to load | |
180 | * @param item | |
181 | * the item to select (or 0 for none by default) | |
182 | * | |
183 | * @throw IndexOutOfBoundsException if the page is out of bounds | |
184 | */ | |
9c598207 | 185 | public void search(String keywords, int page, int item) { |
741e8467 NR |
186 | List<MetaData> stories = new ArrayList<MetaData>(); |
187 | int storyItem = 0; | |
188 | ||
741e8467 NR |
189 | updateKeywords(keywords); |
190 | ||
191 | int maxPage = -1; | |
dc3b0033 NR |
192 | if (searchable != null) { |
193 | try { | |
194 | maxPage = searchable.searchPages(keywords); | |
195 | } catch (IOException e) { | |
196 | GuiReaderSearchFrame.error(e); | |
197 | } | |
741e8467 NR |
198 | } |
199 | ||
200 | if (page > 0) { | |
7cc1e743 NR |
201 | if (maxPage >= 0 && (page <= 0 || page > maxPage)) { |
202 | throw new IndexOutOfBoundsException("Page " + page + " out of " | |
203 | + maxPage); | |
204 | } | |
205 | ||
dc3b0033 NR |
206 | if (searchable != null) { |
207 | try { | |
208 | stories = searchable.search(keywords, page); | |
209 | } catch (IOException e) { | |
210 | GuiReaderSearchFrame.error(e); | |
211 | } | |
741e8467 NR |
212 | } |
213 | ||
214 | if (item > 0 && item <= stories.size()) { | |
215 | storyItem = item; | |
216 | } else if (item > 0) { | |
217 | GuiReaderSearchFrame.error(String.format( | |
218 | "Story item does not exist: Search [%s], item %d", | |
219 | keywords, item)); | |
220 | } | |
221 | } | |
222 | ||
7cc1e743 | 223 | this.page = page; |
7cc1e743 | 224 | this.maxPage = maxPage; |
741e8467 NR |
225 | this.stories = stories; |
226 | this.storyItem = storyItem; | |
741e8467 NR |
227 | } |
228 | ||
229 | /** | |
230 | * Enables or disables this component, depending on the value of the | |
231 | * parameter <code>b</code>. An enabled component can respond to user input | |
232 | * and generate events. Components are enabled initially by default. | |
233 | * <p> | |
234 | * Disabling this component will also affect its children. | |
235 | * | |
236 | * @param b | |
237 | * If <code>true</code>, this component is enabled; otherwise | |
238 | * this component is disabled | |
239 | */ | |
240 | @Override | |
9c598207 NR |
241 | public void setEnabled(boolean b) { |
242 | super.setEnabled(b); | |
243 | keywordsField.setEnabled(b); | |
244 | submitKeywords.setEnabled(b); | |
741e8467 NR |
245 | } |
246 | } |