1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui;
21
22 import java.awt.BorderLayout;
23 import java.awt.FlowLayout;
24 import java.awt.GridBagConstraints;
25 import java.awt.GridBagLayout;
26 import java.awt.event.ActionEvent;
27
28 import javax.swing.AbstractAction;
29 import javax.swing.Action;
30 import javax.swing.Box;
31 import javax.swing.JComboBox;
32 import javax.swing.JLabel;
33 import javax.swing.JPanel;
34 import javax.swing.JScrollPane;
35 import javax.swing.JTextField;
36 import javax.swing.border.EmptyBorder;
37 import javax.swing.event.TreeModelEvent;
38 import javax.swing.event.TreeModelListener;
39 import javax.swing.tree.TreePath;
40
41 import org.xnap.XNap;
42 import org.xnap.event.StatusListener;
43 import org.xnap.gui.component.EraseButton;
44 import org.xnap.gui.component.XNapButton;
45 import org.xnap.gui.event.DoubleClickListener;
46 import org.xnap.gui.event.PopupListener;
47 import org.xnap.gui.table.JTreeTable;
48 import org.xnap.gui.table.SearchTableModel;
49 import org.xnap.gui.table.TreeTableNode;
50 import org.xnap.gui.tree.*;
51 import org.xnap.gui.util.GUIHelper;
52 import org.xnap.gui.util.GridBagHelper;
53 import org.xnap.gui.util.IconHelper;
54 import org.xnap.search.DefaultSearchFilter;
55 import org.xnap.search.MediaType;
56 import org.xnap.search.Search;
57 import org.xnap.search.SearchController;
58 import org.xnap.search.SearchFilter;
59 import org.xnap.search.SearchManager;
60 import org.xnap.search.SearchResult;
61 import org.xnap.util.Preferences;
62
63 /***
64 * This class provides a panel with a {@link JTreeTable} that displays
65 * search results.
66 */
67 public class SearchResultPanel extends JPanel implements StatusListener {
68
69
70
71 protected SearchPanel parent;
72
73 private JLabel jlStatus;
74 private JTextField jtFilter;
75 private JComboBox jcbMediaType;
76
77 private JTreeTable jta;
78 protected SearchTableModel tmResults;
79 private JScrollPane jspSearch;
80
81 private SearchController controller;
82
83 private Preferences prefs = Preferences.getInstance();
84
85
86
87 /***
88 * Invoked by {@link SearchResultTreePanel}.
89 */
90 protected SearchResultPanel(SearchPanel parent, Search search,
91 String table, boolean addTable)
92 {
93 this.parent = parent;
94
95 initialize(search, table, addTable);
96
97 controller = new SearchController
98 (search, tmResults.getListListener(), this);
99
100 if (controller.getFilter() != null && prefs.getFilterResults()) {
101 setFilter(controller.getFilter());
102 }
103 }
104
105 public SearchResultPanel(SearchPanel parent, Search search)
106 {
107 this(parent, search, "search", true);
108
109 controller.start();
110 setStatus(controller.getStatus());
111 }
112
113
114
115 protected void initialize(Search search, String table, boolean addTable)
116 {
117 JPanel jpTop = new JPanel(new BorderLayout());
118 jpTop.setBorder(new EmptyBorder(2, 2, 2, 2));
119
120
121 Action[] actions = search.getActions();
122 if (actions != null && actions.length > 0) {
123 JPanel jpActions = new JPanel(new FlowLayout(FlowLayout.LEFT));
124 for (int i = 0; i < actions.length; i++) {
125 jpActions.add(new XNapButton(actions[i]));
126 }
127 jpTop.add(jpActions, BorderLayout.WEST);
128 }
129
130
131 JPanel jpFilter = new JPanel(new GridBagLayout());
132 jpTop.add(jpFilter, BorderLayout.EAST);
133
134 jcbMediaType = new JComboBox(SearchManager.DEFAULT_MEDIA_TYPES);
135 GridBagHelper.addComponent(jpFilter, jcbMediaType,
136 GridBagConstraints.WEST);
137
138 GridBagHelper.addComponent(jpFilter,Box.createHorizontalStrut(5));
139
140
141 EraseButton jbErase = new EraseButton();
142 GridBagHelper.addComponent(jpFilter, jbErase, GridBagConstraints.WEST);
143
144
145 JLabel filterByLabel = new JLabel(XNap.tr("Filter By"));
146 GridBagHelper.addComponent(jpFilter, filterByLabel,
147 GridBagConstraints.WEST);
148
149 jtFilter = new JTextField("", 10);
150 ApplyOptionsAction acApply = new ApplyOptionsAction();
151 GUIHelper.bindEnterKey(jtFilter, acApply);
152 jbErase.setTextField(jtFilter);
153 GridBagHelper.addComponent(jpFilter, jtFilter,
154 GridBagConstraints.WEST);
155
156 GridBagHelper.addComponent(jpFilter, new XNapButton(acApply),
157 GridBagConstraints.WEST);
158
159
160
161
162
163
164
165
166 tmResults = new SearchTableModel();
167 jta = tmResults.createTreeTable(prefs, table);
168
169 jta.getTree().setCellRenderer(new SearchTreeCellRenderer(tmResults));
170
171 DoubleClickListener.install(jta, new DoubleClickAction());
172
173 PopupListener pl = new PopupListener(parent.getPopupMenu());
174 jta.addMouseListener(pl);
175 jspSearch = new JScrollPane(jta);
176
177
178 jta.getTree().getModel().addTreeModelListener(new TitleUpdater());
179
180
181 jlStatus = new JLabel(" ");
182
183
184 setLayout(new BorderLayout());
185 add(jpTop, BorderLayout.NORTH);
186 if (addTable) {
187 add(jspSearch, BorderLayout.CENTER);
188 }
189 add(jlStatus, BorderLayout.SOUTH);
190 }
191
192 /***
193 * Returns the currently selected search result.
194 *
195 * @return null, if nothing is selected; the result, otherwise
196 */
197 public SearchResult getSelectedResult()
198 {
199 TreePath path = jta.getTree().getSelectionPath();
200 if (path != null) {
201 TreeTableNode node = (TreeTableNode)path.getLastPathComponent();
202 return (SearchResult)node.getData();
203 }
204
205 return null;
206 }
207
208 /***
209 * Returns the currently selected search results.
210 *
211 * @return null, if nothing is selected; the results, otherwise
212 */
213 public SearchResult[] getSelectedResults()
214 {
215 TreePath[] paths = jta.getTree().getSelectionPaths();
216 if (paths != null) {
217 SearchResult[] results = new SearchResult[paths.length];
218 for (int i = 0; i < results.length; i++) {
219 TreeTableNode node
220 = (TreeTableNode)paths[i].getLastPathComponent();
221 results[i] = (SearchResult)node.getData();
222 }
223 return results;
224 }
225 return null;
226 }
227
228 public SearchController getController()
229 {
230 return controller;
231 }
232
233 public SearchFilter getFilter()
234 {
235 SearchFilter filter = new DefaultSearchFilter();
236
237 filter.put(SearchFilter.TEXT, jtFilter.getText());
238 filter.put(SearchFilter.MEDIA_TYPE, jcbMediaType.getSelectedItem());
239
240 return filter;
241 }
242
243 protected void setFilter(SearchFilter filter)
244 {
245 jtFilter.setText((String)filter.get(SearchFilter.TEXT));
246 jcbMediaType.setSelectedItem
247 ((MediaType)filter.get(SearchFilter.MEDIA_TYPE));
248
249 controller.setFilter(filter);
250 }
251
252 public void setStatus(String newValue)
253 {
254 jlStatus.setText(newValue);
255 jlStatus.setToolTipText(newValue);
256
257 parent.updateActions();
258 updateTitle();
259 }
260
261 public String getTitle()
262 {
263 StringBuffer sb = new StringBuffer();
264 sb.append(controller.getSearch().getName());
265 if (controller.isDone()) {
266 sb.append(" [");
267 sb.append(tmResults.size());
268 sb.append("]");
269 }
270 else if (tmResults.size() > 0) {
271 sb.append(" (");
272 sb.append(tmResults.size());
273 sb.append(")");
274 }
275 return sb.toString();
276 }
277
278 public void removeNotify()
279 {
280
281 controller.stop();
282 super.removeNotify();
283 }
284
285 protected void updateTitle()
286 {
287 parent.setTabTitle(this, getTitle());
288 }
289
290 protected JScrollPane getSearchPanel()
291 {
292 return jspSearch;
293 }
294
295
296
297 /***
298 *
299 */
300 private class ApplyOptionsAction extends AbstractAction {
301
302 public ApplyOptionsAction()
303 {
304 putValue(Action.NAME, XNap.tr("Go"));
305 putValue(IconHelper.XNAP_ICON, "filter.png");
306 putValue(Action.SHORT_DESCRIPTION,
307 XNap.tr("Filters search results."));
308 }
309
310 public void actionPerformed(ActionEvent event)
311 {
312 tmResults.setFilter(getFilter());
313 }
314
315 }
316
317 private class DoubleClickAction extends AbstractAction
318 {
319
320 public void actionPerformed(ActionEvent event)
321 {
322 SearchResult result = getSelectedResult();
323 if (result != null) {
324 Action[] actions = result.getActions();
325 if (actions != null && actions.length > 0
326 && actions[0].isEnabled()) {
327 actions[0].actionPerformed(event);
328 }
329 }
330 }
331
332 }
333
334 public class TitleUpdater implements TreeModelListener
335 {
336
337
338
339 public void treeNodesChanged(TreeModelEvent e)
340 {
341 }
342
343 public void treeNodesInserted(TreeModelEvent e)
344 {
345 SearchResultPanel.this.updateTitle();
346 }
347
348 public void treeNodesRemoved(TreeModelEvent e)
349 {
350 SearchResultPanel.this.updateTitle();
351 }
352
353 public void treeStructureChanged(TreeModelEvent e)
354 {
355 SearchResultPanel.this.updateTitle();
356 }
357
358 }
359
360 }
361