package prefuse.util.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import prefuse.Visualization;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.search.PrefixSearchTupleSet;
import prefuse.data.search.SearchTupleSet;
import prefuse.data.tuple.TupleSet;
import prefuse.util.ColorLib;
/**
* Swing component that enables keyword search over prefuse data tuples.
*
* @author jeffrey heer
* @see prefuse.data.query.SearchQueryBinding
*/
public class JSearchPanel extends JPanel
implements DocumentListener, ActionListener
{
private Object m_lock;
private SearchTupleSet m_searcher;
private JTextField m_queryF = new JTextField(15);
private JLabel m_resultL = new JLabel(" ");
private JLabel m_searchL = new JLabel("search >> ");
private Box m_sbox = new Box(BoxLayout.X_AXIS);
private String[] m_fields;
private boolean m_includeHitCount = false;
private boolean m_monitorKeys = false;
private boolean m_autoIndex = true;
private boolean m_showBorder = true;
private boolean m_showCancel = true;
// ------------------------------------------------------------------------
// Free form constructors
/**
* Create a new JSearchPanel.
* @param search the search tuple set conducting the searches
* @param field the data field being searched
*/
public JSearchPanel(SearchTupleSet search, String field) {
this(search, field, false);
}
/**
* Create a new JSearchPanel.
* @param search the search tuple set conducting the searches
* @param field the data field being searched
* @param monitorKeystrokes indicates if each keystroke event should result
* in a new search being issued (true) or if searches should only be
* initiated by hitting the enter key (false)
*/
public JSearchPanel(SearchTupleSet search, String field,
boolean monitorKeystrokes)
{
this(null, search, new String[] {field}, false, monitorKeystrokes);
}
/**
* Create a new JSearchPanel.
* @param source the source set of tuples that should be searched over
* @param search the search tuple set conducting the searches
* @param fields the data fields being searched
* @param monitorKeystrokes indicates if each keystroke event should result
* in a new search being issued (true) or if searches should only be
* initiated by hitting the enter key (false)
*/
public JSearchPanel(TupleSet source, SearchTupleSet search,
String[] fields, boolean autoIndex, boolean monitorKeystrokes)
{
m_lock = new Object();
m_fields = fields;
m_autoIndex = autoIndex;
m_monitorKeys = monitorKeystrokes;
m_searcher = ( search != null ? search : new PrefixSearchTupleSet() );
init(source);
}
// ------------------------------------------------------------------------
// Visualization-based constructors
/**
* Create a new JSearchPanel. The default search tuple set for the
* visualization will be used.
* @param vis the Visualization to search over
* @param field the data field being searched
*/
public JSearchPanel(Visualization vis, String field) {
this(vis, Visualization.ALL_ITEMS, field, true);
}
/**
* Create a new JSearchPanel. The default search tuple set for the
* visualization will be used.
* @param vis the Visualization to search over
* @param group the particular data group to search over
* @param field the data field being searched
*/
public JSearchPanel(Visualization vis, String group, String field) {
this(vis, group, field, true);
}
/**
* Create a new JSearchPanel. The default search tuple set for the
* visualization will be used.
* @param vis the Visualization to search over
* @param group the particular data group to search over
* @param field the data field being searched
* @param autoIndex indicates if items should be automatically
* indexed and unindexed as their membership in the source group
* changes.
*/
public JSearchPanel(Visualization vis, String group, String field,
boolean autoIndex)
{
this(vis, group, Visualization.SEARCH_ITEMS,
new String[] {field}, autoIndex, false);
}
/**
* Create a new JSearchPanel. The default search tuple set for the
* visualization will be used.
* @param vis the Visualization to search over
* @param group the particular data group to search over
* @param field the data field being searched
* @param autoIndex indicates if items should be automatically
* indexed and unindexed as their membership in the source group
* changes.
* @param monitorKeystrokes indicates if each keystroke event should result
* in a new search being issued (true) or if searches should only be
* initiated by hitting the enter key (false)
*/
public JSearchPanel(Visualization vis, String group, String field,
boolean autoIndex, boolean monitorKeystrokes)
{
this(vis, group, Visualization.SEARCH_ITEMS,
new String[] {field}, autoIndex, true);
}
/**
* Create a new JSearchPanel.
* @param vis the Visualization to search over
* @param group the particular data group to search over
* @param searchGroup the group name that resolves to the SearchTupleSet
* to use
* @param field the data field being searched
* @param autoIndex indicates if items should be automatically
* indexed and unindexed as their membership in the source group
* changes.
* @param monitorKeystrokes indicates if each keystroke event should result
* in a new search being issued (true) or if searches should only be
* initiated by hitting the enter key (false)
*/
public JSearchPanel(Visualization vis, String group, String searchGroup,
String field, boolean autoIndex, boolean monitorKeystrokes)
{
this(vis, group, searchGroup, new String[] {field}, autoIndex,
monitorKeystrokes);
}
/**
* Create a new JSearchPanel.
* @param vis the Visualization to search over
* @param group the particular data group to search over
* @param searchGroup the group name that resolves to the SearchTupleSet
* to use
* @param fields the data fields being searched
* @param autoIndex indicates if items should be automatically
* indexed and unindexed as their membership in the source group
* changes.
* @param monitorKeystrokes indicates if each keystroke event should result
* in a new search being issued (true) or if searches should only be
* initiated by hitting the enter key (false)
*/
public JSearchPanel(Visualization vis, String group, String searchGroup,
String[] fields, boolean autoIndex, boolean monitorKeystrokes)
{
m_lock = vis;
m_fields = fields;
m_autoIndex = autoIndex;
m_monitorKeys = monitorKeystrokes;
TupleSet search = vis.getGroup(searchGroup);
if ( search != null ) {
if ( search instanceof SearchTupleSet ) {
m_searcher = (SearchTupleSet)search;
} else {
throw new IllegalStateException(
"Search focus set not instance of SearchTupleSet!");
}
} else {
m_searcher = new PrefixSearchTupleSet();
vis.addFocusGroup(searchGroup, m_searcher);
}
init(vis.getGroup(group));
}
// ------------------------------------------------------------------------
// Initialization
private void init(TupleSet source) {
if ( m_autoIndex && source != null ) {
// index everything already there
for ( int i=0; i < m_fields.length; i++ )
m_searcher.index(source.tuples(), m_fields[i]);
// add a listener to dynamically build search index
source.addTupleSetListener(new TupleSetListener() {
public void tupleSetChanged(TupleSet tset,
Tuple[] add, Tuple[] rem)
{
if ( add != null ) {
for ( int i=0; i