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  import java.awt.event.ActionListener;
28  import java.awt.event.ItemEvent;
29  import java.awt.event.ItemListener;
30  import java.util.Hashtable;
31  import javax.swing.*;
32  import javax.swing.Action;
33  import javax.swing.ComboBoxModel;
34  import javax.swing.JCheckBox;
35  import javax.swing.JComboBox;
36  import javax.swing.JPanel;
37  import org.xnap.XNap;
38  import org.xnap.gui.component.EraseButton;
39  import org.xnap.gui.component.HistoryComboBox;
40  import org.xnap.gui.component.RangeBox;
41  import org.xnap.gui.component.XNapButton;
42  import org.xnap.gui.list.SearchProviderListCellRenderer;
43  import org.xnap.gui.shortcut.ShortcutManager;
44  import org.xnap.gui.util.FocusHandler;
45  import org.xnap.gui.util.GUIHelper;
46  import org.xnap.gui.util.GridBagHelper;
47  import org.xnap.gui.util.IconHelper;
48  import org.xnap.search.DefaultSearchFilter;
49  import org.xnap.search.LibrarySearch;
50  import org.xnap.search.MediaType;
51  import org.xnap.search.Search;
52  import org.xnap.search.SearchContainer;
53  import org.xnap.search.SearchFilter;
54  import org.xnap.search.SearchManager;
55  import org.xnap.search.SearchProvider;
56  import org.xnap.search.SearchProviderContainer;
57  import org.xnap.util.Preferences;
58  
59  /***
60   * This class provides a panel with widgets to enter search queries
61   * and to set search options. The queries are passed to the 
62   * {@link xnap.search.SearchManager SearchManager}.
63   */
64  public class AdvancedSearchQueryPanel extends JPanel
65  {
66  
67      //--- Constant(s) ---
68  
69      public static final int DEFAULT_UNIT_INDEX = 2;
70  
71      public static final String UNITS[] = { 
72  		XNap.tr("B"), XNap.tr("KB"), XNap.tr("MB"), XNap.tr("GB") 
73      };
74  
75      public static final long FACTORS[] = { 
76  		1, 1024, 1024 * 1024, 1024 * 1024 * 1024
77      };
78  
79  
80      //--- Data field(s) ---
81  
82      protected static Preferences prefs = Preferences.getInstance();
83  
84      private HistoryComboBox jcbSearch;
85      private JComboBox jcbMediaType;
86      private RangeBox rbFilesize;
87      private JComboBox jcbFilesizeUnit;
88      private JComboBox jcbNetwork;
89      private JCheckBox jcbLocal;
90  
91  	private JPanel jpOptions;
92  
93      private SearchProviderContainer spcNone;
94      private SearchProviderContainer spcAll;
95  
96      private QueryAction acQuery = new QueryAction();
97  
98      /***
99       * Maps {@link SearchProvider} objets to extra {@link JPanel}
100      * objects for additonal options.
101      */
102     private Hashtable panelByProvider = new Hashtable();
103 
104     private SearchOptionsPanel currentOptionsPanel;
105 
106     //--- Constructor(s) ---
107     
108     /***
109      * Constructs the search query panel.
110 	 *
111 	 * @param model model used by the HistoryComboBox
112      */
113     public AdvancedSearchQueryPanel(ComboBoxModel model) 
114     {
115 		setLayout(new GridBagLayout());
116 		//setBorder(GUIHelper.createDefaultBorder(XNap.tr("Advanced Search")));
117 
118 		GridBagHelper.add(this, GUIHelper.createSeparator(XNap.tr("Text")));
119 
120 //  		FormLayout layout = new FormLayout
121 //  			("right:pref, 3dlu, pref",
122 //  			 "p, 3dlu, p, 9dlu, p, 3dlu, p, 3dlu, p, 9dlu, 3dl, p, 9dlu, p");
123 
124 		// erase button
125 		EraseButton jbErase = new EraseButton();
126 		GridBagHelper.addComponent(this, jbErase, GridBagConstraints.WEST);
127 		ShortcutManager.getInstance().add(jbErase.getAction(), this);
128 	    
129 		// search field
130 		jcbSearch = new HistoryComboBox(model, 20);
131 		jcbSearch.setPreferences("search");
132 		jcbSearch.addItemListener(new HistorySelectionListener());
133 		GUIHelper.bindEnterKeyLocally(jcbSearch.getTextField(), acQuery);
134 		jbErase.setTextField(jcbSearch.getTextField());
135 		GridBagHelper.add(this, jcbSearch);
136 
137 		// options
138 		jpOptions = new JPanel(new GridBagLayout());
139 		jpOptions.setBorder
140 			(GUIHelper.createDefaultBorder(XNap.tr("Options")));
141 		GridBagHelper.add(this, jpOptions);
142 
143 		// media type
144 		JLabel l = GridBagHelper.addLabel(jpOptions, XNap.tr("Media Type"));
145 		jcbMediaType = new JComboBox();
146 		l.setLabelFor(jcbMediaType);
147 		GridBagHelper.add(jpOptions, jcbMediaType);
148 
149 		// network
150 		l = GridBagHelper.addLabel(jpOptions, XNap.tr("Network"));
151 		jcbNetwork = new JComboBox();
152 		l.setLabelFor(jcbNetwork);
153 		jcbNetwork.setRenderer(new SearchProviderListCellRenderer());
154 		spcNone = new SearchProviderContainer(XNap.tr("None"));
155 		jcbNetwork.addItem(spcNone);
156 		spcAll = new SearchProviderContainer(XNap.tr("All"));
157 		jcbNetwork.addItem(spcAll);
158 		jcbNetwork.setSelectedIndex(1);
159 		jcbNetwork.addActionListener(new ProviderListener());
160 		GridBagHelper.add(jpOptions, jcbNetwork);
161 
162 		// local
163 		jcbLocal = new JCheckBox(XNap.tr("Local Library"));
164 		jcbLocal.addActionListener(new QueryButtonListener());
165 		GridBagHelper.add(jpOptions, jcbLocal);
166 
167 		// file size
168 		JPanel jpFilesize = new JPanel(new GridBagLayout());
169 		jpFilesize.setBorder
170 			(GUIHelper.createDefaultBorder(XNap.tr("File size")));
171 		GridBagHelper.add(this, jpFilesize);
172 
173 		rbFilesize = new RangeBox();
174 		jcbFilesizeUnit = new JComboBox(UNITS);
175 		jcbFilesizeUnit.setSelectedIndex(DEFAULT_UNIT_INDEX);
176 		rbFilesize.setUnitComponent(jcbFilesizeUnit);
177 		GridBagHelper.add(jpFilesize, rbFilesize, false);
178 
179 		// options
180 		jpOptions = new JPanel(new BorderLayout());
181 		GridBagHelper.add(this, jpOptions);
182 
183 		// query button
184 		JPanel jpButtons = new JPanel(new FlowLayout(FlowLayout.CENTER));
185 		jpButtons.add(new XNapButton(acQuery)); 
186 		GridBagHelper.add(this, jpButtons);
187 
188 		GridBagHelper.addVerticalSpacer(this);
189 
190 		acQuery.update();
191 		
192 		addComponentListener(new FocusHandler(jcbSearch.getTextField()));
193 		GUIHelper.setMnemonics(this);
194     }
195 
196     //--- Method(s) ---
197 
198     public void addOptionsPanel(SearchProvider provider, 
199 								SearchOptionsPanel panel)
200     {
201 		panelByProvider.put(provider, panel);
202     }
203 
204     public void removeOptionsPanel(SearchProvider provider)
205     {
206 		panelByProvider.remove(provider);
207     }
208 
209 	public Object[] getHistoryItems()
210 	{
211 		return jcbSearch.getHistoryItems();
212 	}
213 
214     void addToHistory(SearchFilter filter)
215     {
216 		jcbSearch.addDistinctItemAtTop(filter);
217     }
218 
219     DefaultSearchFilter getSearchFilter()
220     {
221 		DefaultSearchFilter f = new DefaultSearchFilter();
222 
223 		f.put(SearchFilter.TEXT, jcbSearch.getText());
224 		f.put(SearchFilter.MEDIA_TYPE, jcbMediaType.getSelectedItem());
225 		long factor = FACTORS[jcbFilesizeUnit.getSelectedIndex()];
226 		f.put(SearchFilter.MIN_FILESIZE, 
227 			  (rbFilesize.getMinValue() != -1) 
228 			  ? new Long(rbFilesize.getMinValue() * factor)
229 				  : null);
230 		f.put(SearchFilter.MAX_FILESIZE, 
231 			  (rbFilesize.getMaxValue() != -1)
232 			  ? new Long(rbFilesize.getMaxValue() * factor)
233 				  : null);
234 	
235 		if (currentOptionsPanel != null) {
236 			currentOptionsPanel.setOptions(f);
237 		}
238 
239 		// save search provider's name
240 		SearchProvider p = (SearchProvider)jcbNetwork.getSelectedItem();
241 		f.put("searchProvider", p.getName());
242 		
243 		return f;
244     }
245 
246     void setSearchFilter(SearchFilter f)
247     {
248 		setSearchFilter(f, true);
249 	}
250 
251 	void setSearchFilter(SearchFilter f, boolean setText)
252 	{
253 		String name = (String)f.get("searchProvider");
254 		if (name != null) {
255 			boolean found = false;
256 			for (int i = 0; i < jcbNetwork.getItemCount() && !found; i++) {
257 				SearchProvider sp = (SearchProvider)jcbNetwork.getItemAt(i);
258 				if (sp.getName().equals(name)) {
259 					jcbNetwork.setSelectedItem(sp);
260 					found = true;
261 				}
262 			}
263 			if (!found) {
264 				jcbNetwork.setSelectedItem(spcAll);
265 			}
266 		}
267 		
268 		if (setText) {
269 			jcbSearch.setText((String)f.get(SearchFilter.TEXT));
270 		}
271 		jcbMediaType.setSelectedItem(f.get(SearchFilter.MEDIA_TYPE));
272     }
273 
274     /***
275      * Invoked by the {@link SearchPanel} when a {@link
276      * SearchProvider} has been added.  
277      */
278     void providerAdded(SearchProvider provider)
279     {
280 		spcAll.add(provider);
281 		jcbNetwork.addItem(provider);
282 
283 		providerChanged();
284     }
285 
286     /***
287      * Invoked by the {@link SearchPanel} when a {@link
288      * SearchProvider} has been removed.  
289      */
290     void providerRemoved(SearchProvider provider)
291     {
292 		spcAll.remove(provider);
293 		jcbNetwork.removeItem(provider);
294 
295 		providerChanged();
296     }
297 
298     private void providerChanged()
299     {
300 		SearchProvider p = (SearchProvider)jcbNetwork.getSelectedItem();
301 
302 		jcbMediaType.removeAllItems();
303 		MediaType[] types = p.getSupportedMediaTypes();
304 		if (types != null) {
305 			for (int i = 0; i < types.length; i++) {
306 				jcbMediaType.addItem(types[i]);
307 			}
308 		}
309 
310 		if (currentOptionsPanel != null) {
311 			jpOptions.remove(currentOptionsPanel.getComponent());
312 		}
313 
314 		currentOptionsPanel = (SearchOptionsPanel)panelByProvider.get(p);
315 		if (currentOptionsPanel != null) {
316 			jpOptions.add(currentOptionsPanel.getComponent(), 
317 						  BorderLayout.CENTER);
318 		}
319 
320 		acQuery.update();
321     }
322 
323     /***
324      * Performs a search.
325      */
326     private class QueryAction extends AbstractAction {
327 
328         public QueryAction() 
329 		{
330             putValue(Action.NAME, XNap.tr("Query"));
331             putValue(Action.SHORT_DESCRIPTION, XNap.tr("Performs a search."));
332 			putValue(IconHelper.XNAP_ICON, "filefind.png");
333         }
334 
335         public void actionPerformed(ActionEvent event) 
336 		{
337 			DefaultSearchFilter filter = getSearchFilter();
338 	    
339 			try {
340 				filter.validate();
341 			}
342 			catch (Exception e) {
343 				StatusBar.setText(e.getMessage());
344 				return;
345 			}
346 
347 			SearchProvider p = (SearchProvider)jcbNetwork.getSelectedItem();
348 			Search search = p.search(filter);
349 
350 			if (jcbLocal.isSelected()) {
351 				SearchContainer c = new SearchContainer(filter);
352 				c.add(search);
353 				c.add(new LibrarySearch(filter));
354 				SearchManager.getInstance().handle(c);
355 			}
356 			else {
357 				SearchManager.getInstance().handle(search);
358 			}
359         }
360 
361 		public void update()
362 		{
363 			setEnabled(jcbLocal.isSelected() 
364 					   || (spcAll.size() > 0 
365 						   && jcbNetwork.getSelectedItem() != spcNone));
366 		}
367 
368     }
369 
370     /***
371      * Updates the media types when a network is selected.
372      */
373     private class ProviderListener implements ActionListener 
374     {
375 		public void actionPerformed(ActionEvent event)
376 		{
377 			providerChanged();
378 		}
379 
380     }
381 
382     /***
383      * Switches between advanced and simple view.
384      */
385     private class ShowSimpleAction extends AbstractAction {
386 	
387         public ShowSimpleAction() 
388 		{
389             putValue(Action.NAME, XNap.tr("Less Options"));
390             putValue(Action.SHORT_DESCRIPTION, 
391 					 XNap.tr("Displays simple search options panel."));
392 			putValue(IconHelper.XNAP_ICON, "2uparrow.png");
393         }
394 	
395         public void actionPerformed(ActionEvent event) 
396 		{
397 			prefs.set("showAdvancedSearchOptions", "true");
398         }
399     }
400 
401 	/***
402 	 * Update the settings when the selected search filter object
403 	 * changes.
404 	 */
405 	private class HistorySelectionListener implements ItemListener
406 	{
407 		public void itemStateChanged(ItemEvent event)
408 		{
409 			if (event.getStateChange() == ItemEvent.SELECTED) {
410 				if (event.getItem() instanceof SearchFilter) {
411 					setSearchFilter((SearchFilter)event.getItem(), false);
412 				}
413 			}
414 		}
415 	}
416 
417 	/***
418 	 * Disables the query button when no network is availabe and local search
419 	 * is not selected.
420 	 */
421 	private class QueryButtonListener implements ActionListener
422 	{
423 		public void actionPerformed(ActionEvent event)
424 		{
425 			acQuery.update();
426 		}
427 
428 	}
429 
430 }
431