make it subtree
[fanfix.git] / reader / ui / GuiReaderSearchByNamePanel.java
CommitLineData
741e8467
NR
1package be.nikiroo.fanfix.reader.ui;
2
3import java.awt.BorderLayout;
741e8467
NR
4import java.awt.event.ActionEvent;
5import java.awt.event.ActionListener;
7cc1e743
NR
6import java.awt.event.KeyAdapter;
7import java.awt.event.KeyEvent;
741e8467
NR
8import java.io.IOException;
9import java.util.ArrayList;
10import java.util.List;
11
741e8467 12import javax.swing.JButton;
741e8467 13import javax.swing.JPanel;
741e8467 14import javax.swing.JTextField;
741e8467
NR
15
16import be.nikiroo.fanfix.data.MetaData;
dc3b0033 17import be.nikiroo.fanfix.reader.ui.GuiReaderSearchByPanel.Waitable;
741e8467 18import 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
26public 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}