/*
* XNap
*
* A pure java file sharing client.
*
* See AUTHORS for copyright information.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package xnap.gui;
import xnap.*;
import xnap.gui.table.*;
import xnap.gui.event.DoubleClickListener;
import xnap.gui.event.KeyAction;
import xnap.gui.event.PopupListener;
import xnap.gui.event.ShowAction;
import xnap.io.*;
import xnap.net.*;
import xnap.net.event.*;
import xnap.plugin.*;
import xnap.util.*;
import xnap.util.event.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class SearchSubPanel extends JPanel
implements PropertyChangeListener, StatusListener, TableModelListener {
//--- Data field(s) ---
protected SearchPanel parent;
//private String status = "";
private JPanel jpOptions;
private JTextField jtFilter;
protected OptionsBox opBox;
private JComboBox jcbGrouper;
private JComboBox jcbMediaType;
protected JTable jta;
protected SearchTableModel stm;
protected JScrollPane jspSearch;
private JCheckBox jcbMaintainSortOrder;
private JPanel jpa;
private JLabel jlMyStatus;
protected SearchFilter originalFilter;
protected String title;
protected ISearchContainer sc;
protected SearchResultCollector srCollector;
private Preferences prefs = Preferences.getInstance();
//--- Constructor(s) ---
public SearchSubPanel(SearchPanel parent, ISearchContainer sc)
{
this.parent = parent;
this.sc = sc;
this.originalFilter = sc.getFilter();
this.title = (originalFilter != null) ?
originalFilter.getSearchText() : sc.toString();
srCollector = new SearchResultCollector(sc.getDefaultGrouper());
initialize();
stm.setFilteredData(srCollector.getGroupedData());
sc.setCollector(srCollector);
sc.setStatusListener(this);
if (sc.getFilter() != null && prefs.getFilterResults()) {
setFilter(sc.getFilter());
}
sc.start();
prefs.addPropertyChangeListener(getTableModelName()
+ "TableMaintainSortOrder", this);
}
//--- Method(s) ---
protected void initialize()
{
initialize(true);
}
protected void initialize(boolean addJspSearch)
{
KeyStroke ksEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
KeyAction ka;
// options panel
Box boxOne = new Box(BoxLayout.X_AXIS);
boxOne.add(new JLabel(XNap.tr("Filter", 0, 1)));
jtFilter = new JTextField("", 20);
boxOne.add(jtFilter);
boxOne.add(new JLabel(XNap.tr("Group by", 1)));
jcbGrouper = new JComboBox(new String[] { ""/*, "Filename"*/
, "Filesize" });
jcbGrouper.setSelectedIndex(1);
boxOne.add(jcbGrouper);
opBox = new OptionsBox(BoxLayout.X_AXIS);
Box boxThree = Box.createHorizontalBox();
ApplyOptionsAction acApplyOptions = new ApplyOptionsAction();
ka = new KeyAction(acApplyOptions, ksEnter, jtFilter);
ka = new KeyAction(acApplyOptions, ksEnter,
opBox.getFilesizeTextField());
boxThree.add(new JLabel(XNap.tr("Media Type", 0, 1)));
jcbMediaType = new JComboBox(SearchFilter.media);
boxThree.add(jcbMediaType);
boxThree.add(new JPanel());
boxThree.add(new JButton(acApplyOptions));
boxThree.add(new JButton(new ResetOptionsAction()));
// jpOptions = new JPanel();
jpOptions = new FocusHandlingJPanel(jtFilter);
BoxLayout bx = new BoxLayout(jpOptions, BoxLayout.Y_AXIS);
jpOptions.setLayout(bx);
jpOptions.setBorder(new TitledBorder(XNap.tr("Options", 1)));
jpOptions.add(boxOne);
jpOptions.add(opBox);
jpOptions.add(boxThree);
jpOptions.setVisible(false);
// status label
JPanel jpStatus = new JPanel(new BorderLayout());
jlMyStatus = new JLabel("");
jpStatus.add(jlMyStatus, BorderLayout.CENTER);
// maintain sort order
JPanel container = new JPanel();
jcbMaintainSortOrder = new JCheckBox(new ToggleSortOrderAction());
jcbMaintainSortOrder.setSelected
(prefs.getTableMaintainSortOrder(getTableModelName()));
container.add(jcbMaintainSortOrder, BorderLayout.CENTER);
// options button
JButton jbOptions = new JButton(new ShowAction(this, jpOptions));
jbOptions.setMargin(new Insets(1, 1, 1, 1));
container.add(jbOptions);
jpStatus.add(container, BorderLayout.EAST);
// top
JPanel jpTop = new JPanel(new BorderLayout());
jpTop.setBorder(new EmptyBorder(2, 2, 2, 2));
jpTop.add(jpStatus, BorderLayout.NORTH);
jpTop.add(jpOptions, BorderLayout.CENTER);
// table
stm = createTableModel();
jta = stm.createJTable();
jta.setShowGrid(false);
stm.addTableModelListener(this);
PopupListener pl = new PopupListener(parent.getPopupMenu());
jta.addMouseListener(pl);
DoubleClickListener dcl
= new DoubleClickListener(parent.getDefaultAction(), jta);
jta.addMouseListener(dcl);
jta.getInputMap().put(ksEnter, parent.getDefaultAction());
jta.getActionMap().put(parent.getDefaultAction(),
parent.getDefaultAction());
jspSearch = new JScrollPane(jta);
// content
setLayout(new BorderLayout());
add(jpTop, BorderLayout.NORTH);
if (addJspSearch) {
add(jspSearch, BorderLayout.CENTER);
}
}
public void abort()
{
if (sc != null) {
sc.abort();
}
}
// public boolean isFinished()
// {
// return sm.isFinished();
// }
public SearchResultCollector getSearchResultCollector()
{
return srCollector;
}
public SearchResultContainer getSelectedResult()
{
int i = jta.getSelectedRow();
if (i != -1) {
return stm.get(i);
}
else {
return null;
}
}
public SearchResultContainer[] getSelectedResults()
{
int[] rows = jta.getSelectedRows();
SearchResultContainer[] results =
new SearchResultContainer[rows.length];
for (int i = 0; i < rows.length; i++) {
results[i] = stm.get(rows[i]);
}
return results;
}
public boolean isAutoDownloadable()
{
return (originalFilter != null);
}
public void applyFilter(SearchFilter filter)
{
srCollector.setFilter(filter);
}
public void applyFilter()
{
applyFilter(getFilter());
}
public SearchFilter getFilter()
{
SearchFilter f = new SearchFilter
(jtFilter.getText(), opBox.getBitrateCompare(),
opBox.getBitrate(), opBox.getFilesizeCompare(),
opBox.getFilesize(), jcbMediaType.getSelectedIndex());
// FIX ME: hack for search path tree
SearchFilter sf = srCollector.getFilter();
if (sf != null) {
f.setPath(sf.getPath());
}
return f;
}
private void setFilter(SearchFilter filter)
{
jtFilter.setText(filter.getSearchText());
opBox.setBitrateCompare(filter.getBitrateCompare());
opBox.setBitrate(SearchFilterHelper.getIndexFromBitrate(filter.getBitrate()));
opBox.setFilesizeCompare(filter.getFilesizeCompare());
opBox.setFilesize(filter.getFilesize());
jcbMediaType.setSelectedIndex(filter.getMediaType());
applyFilter();
}
public SearchFilter getOriginalFilter()
{
return originalFilter;
}
public Grouper getGrouper()
{
if (jcbGrouper.getSelectedItem().equals("Filename")) {
return new FilenameGrouper();
}
else if (jcbGrouper.getSelectedItem().equals("Filesize")) {
return new FilesizeGrouper();
}
else {
return new Grouper();
}
}
public void setStatus(String newValue)
{
jlMyStatus.setText(newValue);
jlMyStatus.setToolTipText(newValue);
parent.updateActions();
}
public String getTitle()
{
StringBuffer sb = new StringBuffer();
sb.append(title);
if (srCollector.getData().size() > 0) {
sb.append(" (");
sb.append(stm.getRowCount());
if (stm.getRowCount() != srCollector.getGroupedData().size()) {
sb.append("/");
sb.append(srCollector.getGroupedData().size());
}
sb.append(")");
}
return sb.toString();
}
/**
* Returns table model to be used. Should be reimplemented by subclasses
* using a different table model. This method should only be called once
* to construct the respective table model for this sub panel.
*/
protected SearchTableModel createTableModel()
{
return new SearchTableModel();
}
/**
* Returns name of the table model to be used. Should be reimplemented by
* subclasses using a different table model. This method is called by pref
* listeners registering for the correct table name.
*/
protected String getTableModelName()
{
return "search";
}
public void propertyChange(PropertyChangeEvent e)
{
jcbMaintainSortOrder.setSelected
(prefs.getTableMaintainSortOrder(getTableModelName()));
}
public void tableChanged(TableModelEvent e)
{
updateTitle();
}
/**
* Make sure all calls are swing synchronized, so we don't get funny
* titles printed all over the place.
*/
protected void updateTitle()
{
parent.setTitle(SearchSubPanel.this, getTitle());
}
public void removeNotify()
{
// somebody has closed the panel, abort search
Debug.log("SearchSubPanel: removed");
abort();
super.removeNotify();
SearchManager.getInstance().remove(sc);
}
// --- Class(es) ---
/**
*
*/
private class ResetOptionsAction extends AbstractAction {
public ResetOptionsAction()
{
putValue(Action.NAME, XNap.tr("Reset"));
putValue(Action.SHORT_DESCRIPTION,
XNap.tr("Show all search results"));
putValue(Action.MNEMONIC_KEY, new Integer('R'));
}
public void actionPerformed(ActionEvent event)
{
jtFilter.setText("");
opBox.setBitrateCompare(SearchFilter.COMPARE_NOT_ACTIVE);
opBox.setFilesizeCompare(SearchFilter.COMPARE_NOT_ACTIVE);
jcbMediaType.setSelectedIndex(SearchFilter.MEDIA_ANYTHING);
applyFilter();
}
}
/**
*
*/
private class ApplyOptionsAction extends AbstractAction {
public ApplyOptionsAction()
{
putValue(Action.NAME, XNap.tr("Apply"));
putValue(Action.SHORT_DESCRIPTION,
XNap.tr("Filter search results"));
putValue(Action.MNEMONIC_KEY, new Integer('A'));
}
public void actionPerformed(ActionEvent event)
{
applyFilter();
srCollector.setGrouper(getGrouper());
}
}
private class ToggleSortOrderAction extends AbstractAction {
public ToggleSortOrderAction()
{
putValue(Action.NAME, XNap.tr("Maintain Sort Order"));
putValue(Action.SHORT_DESCRIPTION,
XNap.tr("If enabled, the sort order will be maintained as newitems are added to the table."));
putValue(Action.MNEMONIC_KEY, new Integer('T'));
}
public void actionPerformed(ActionEvent event)
{
prefs.setTableMaintainSortOrder
(getTableModelName(), jcbMaintainSortOrder.isSelected());
}
}
/**
* A small class which overrides JPanel's <code>setVisible()</code> to set
* the focus correctly when visible. If it's needed somewhere else it
* should me moved to an own file of course.
*/
private class FocusHandlingJPanel extends JPanel
{
private JComponent focusComponent;
/**
* @param focusComponent Component which should receive focus when
* Panel becomes visible.
*/
public FocusHandlingJPanel(JComponent focusComponent)
{
this.focusComponent = focusComponent;
}
/**
* Overrides super method to grab focus for focusComponent.
*/
public void setVisible(boolean visible)
{
super.setVisible(visible);
if (visible) {
focusComponent.grabFocus();
}
}
}
}