View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      //--- Data field(s) ---
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      //--- Constructor(s) ---
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     //--- Method(s) ---
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 		// actions
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 		// filter panel
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 		// erase button
141 		EraseButton jbErase = new EraseButton();
142 		GridBagHelper.addComponent(jpFilter, jbErase, GridBagConstraints.WEST);
143 
144 		// filter text
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 		// maintain sort order
161 //  		JCheckBox jcbMaintainSortOrder 
162 //  			= new XNapCheckBox(new MaintainSortOrderAction(table));
163 //  		GridBagHelper.addComponent(jpFilter, jcbMaintainSortOrder);
164 	
165 		// tree table
166         tmResults = new SearchTableModel();
167 		jta = tmResults.createTreeTable(prefs, table);
168 		// jta.getTree().setCellRenderer(new IconTreeCellRenderer(tmResults));
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 		//jspSearch.setBorder(GUIHelper.createEmptyBorder());
177 
178 		jta.getTree().getModel().addTreeModelListener(new TitleUpdater());
179 
180 		// status label
181 		jlStatus = new JLabel(" ");
182 
183 		// content
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 		// somebody has closed the panel, abort search
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 	//--- Inner Class(es) ---
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 		//--- Method(s) ---
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