/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2014 University of Dundee. All rights reserved.
*
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.dataBrowser.view;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import info.clearthought.layout.TableLayout;
import omero.gateway.model.SearchParameters;
import org.jdesktop.swingx.JXBusyLabel;
import org.openmicroscopy.shoola.agents.treeviewer.view.SearchEvent;
import org.openmicroscopy.shoola.env.LookupNames;
import org.openmicroscopy.shoola.util.ui.SeparatorPane;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.search.GroupContext;
import org.openmicroscopy.shoola.util.ui.search.SearchContext;
import org.openmicroscopy.shoola.util.ui.search.SearchObject;
import omero.gateway.model.GroupData;
import org.openmicroscopy.shoola.agents.dataBrowser.DataBrowserAgent;
/**
* Component with advanced search options.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since OME3.0
*/
public class SearchComponent
extends JPanel
implements ActionListener
{
/** Identifies the text to filter by untagged data. */
public static final String UNTAGGED_TEXT = "Untagged";
/** Identifies the text to filter by tagged data. */
public static final String TAGGED_TEXT = "Tagged";
/** Identifies the text to filter by commented data. */
public static final String COMMENTED_TEXT = "Commented";
/** Identifies the text to filter by uncommented data. */
public static final String UNCOMMENTED_TEXT = "Uncommented";
/** Identifies the text to filter by unrated data. */
public static final String UNRATED = "Unrated";
/** Identifies the text for searching for name. */
public static final String NAME_TEXT = "Name";
/** Identifies the text for searching for name. */
public static final String NAME_DESCRIPTION = "Description";
/** Identifies the text for searching for tags. */
public static final String NAME_TAGS = "Tags";
/** Identifies the text for searching for comments. */
public static final String NAME_COMMENTS = "Comments";
/** Identifies the text for searching for ROIs. */
public static final String NAME_ROIS = "ROIs";
/** Identifies the text for searching for URLs. */
public static final String NAME_URL = "URL";
/** Identifies the text for searching for attachments. */
public static final String NAME_ATTACHMENT = "Attachments";
/** Identifies the text for searching for annotations. */
public static final String NAME_ANNOTATION = "Annotations";
/** Identifies the text for searching for rate. */
public static final String NAME_RATE = "Rate";
/** Identifies the text for searching for time. */
public static final String NAME_TIME = "Time";
/** Identifies the text for searching for time. */
public static final String NAME_CUSTOMIZED = "Custom";
/** Identifies the text to filter by ROIs. */
public static final String HAS_ROIS_TEXT = "Has ROIs";
/** Identifies the text to filter by ROIs. */
public static final String NO_ROIS_TEXT = "No ROIs";
/** Bound property indicating to search. */
public static final String SEARCH_PROPERTY = "search";
/** Bound property indicating to cancel the search. */
public static final String CANCEL_SEARCH_PROPERTY = "cancelSearch";
/** Bound property indicating that nodes are expanded. */
public static final String NODES_EXPANDED_PROPERTY = "nodesExpanded";
/** Action command ID indicating to cancel. */
public static final int CANCEL = 0;
/** Action command ID indicating to search. */
public static final int SEARCH = 1;
/** Action command ID indicating to search. */
public static final int COLLAPSE = 2;
/** Action command ID indicating to search. */
public static final int HELP = 3;
/** Action command ID indicating to set the date. */
static final int DATE = 4;
/**
* Action command ID indicating to reset the date fields
*/
static final int RESET_DATE = 7;
/**
* The size of the invisible components used to separate buttons
* horizontally.
*/
static final Dimension H_SPACER_SIZE = new Dimension(5, 10);
/** The UI with all the search fields. */
protected SearchPanel uiDelegate;
/** Button to close the dialog. */
private JButton searchButton;
/** Component indicating the progress of the search. */
private JXBusyLabel busyLabel;
/** Displays the search message. */
private JLabel progressLabel;
/** The available nodes. */
private List<SearchObject> nodes;
/** The possible types. */
private List<SearchObject> types;
/** The default search context. */
private SearchContext searchContext;
/** The UI component hosting the result if any. */
private JComponent resultPane;
/** The list of groups.*/
protected Collection<GroupData> groups;
/** The groups to handle.*/
protected List<GroupContext> groupsContext;
/**
* Initializes the components composing the display.
*/
private void initComponents()
{
uiDelegate = new SearchPanel(this);
searchButton = new JButton("Search");
searchButton.setToolTipText("Search");
searchButton.setActionCommand(""+SEARCH);
searchButton.addActionListener(this);
searchButton.setBackground(UIUtilities.BACKGROUND_COLOR);
busyLabel = new JXBusyLabel();
busyLabel.setEnabled(false);
progressLabel = new JLabel("");
progressLabel.setEnabled(false);
progressLabel.setBackground(UIUtilities.BACKGROUND_COLOR);
}
/**
* Builds and lays out the tool bar.
*
* @return See above.
*/
private JPanel buildToolBar()
{
JPanel bar = new JPanel();
bar.setBackground(UIUtilities.BACKGROUND_COLOR);
bar.setBorder(null);
bar.add(searchButton);
JPanel p = UIUtilities.buildComponentPanel(bar);
p.setBackground(UIUtilities.BACKGROUND_COLOR);
return p;
}
/**
* Lays out the components indicating the status.
*
* @return See above.
*/
private JPanel buildStatusBar()
{
JPanel bar = new JPanel();
bar.setBackground(UIUtilities.BACKGROUND_COLOR);
bar.setLayout(new BoxLayout(bar, BoxLayout.X_AXIS));
bar.add(progressLabel);
JPanel p = UIUtilities.buildComponentPanelCenter(busyLabel);
p.setBackground(UIUtilities.BACKGROUND_COLOR);
bar.add(p);
return bar;
}
/**
* Builds and lays out the UI.
*
* @param showControl Pass <code>true</code> to display the buttons,
* <code>false</code> otherwise.
*/
private void buildGUI(boolean showControl)
{
setBackground(UIUtilities.BACKGROUND_COLOR);
double[][] size = {{TableLayout.PREFERRED},
{TableLayout.PREFERRED, TableLayout.PREFERRED,
TableLayout.PREFERRED, TableLayout.PREFERRED,
TableLayout.PREFERRED}};
setLayout(new TableLayout(size));
int i = 0;
String key = "0, ";
add(uiDelegate, key+i);
i++;
if (showControl) {
add(buildToolBar(), key+i);
}
i++;
add(buildStatusBar(), key+i);
resultPane = new JPanel();
resultPane.setBackground(UIUtilities.BACKGROUND_COLOR);
resultPane.setLayout(new BoxLayout(resultPane, BoxLayout.Y_AXIS));
JPanel sep = new SeparatorPane();
sep.setBackground(UIUtilities.BACKGROUND_COLOR);
i++;
add(sep, key+i);
i++;
add(resultPane, key+i);
}
/** Closes and disposes of the window. */
private void cancel()
{
firePropertyChange(CANCEL_SEARCH_PROPERTY,
Boolean.valueOf(false), Boolean.valueOf(true));
setSearchEnabled(-1);
}
/** Sets the default contexts. */
private void setDefaultContext()
{
nodes = new ArrayList<SearchObject>();
SearchObject node = new SearchObject(SearchContext.NAME,
null, NAME_TEXT);
nodes.add(node);
node = new SearchObject(SearchContext.DESCRIPTION,
null, NAME_DESCRIPTION);
nodes.add(node);
node = new SearchObject(SearchContext.ANNOTATION, null,
NAME_ANNOTATION);
nodes.add(node);
types = new ArrayList<SearchObject>();
node = new SearchObject(SearchContext.IMAGES, null, "Images");
types.add(node);
node = new SearchObject(SearchContext.DATASETS, null, "Datasets");
types.add(node);
node = new SearchObject(SearchContext.PROJECTS, null, "Projects");
types.add(node);
node = new SearchObject(SearchContext.PLATES, null, "Plates");
types.add(node);
node = new SearchObject(SearchContext.SCREENS, null, "Screens");
types.add(node);
}
/**
* Creates a new instance.
*
* @param context The context of the search.
*/
public SearchComponent(SearchContext context)
{
searchContext = context;
}
/** Creates a new instance. */
public SearchComponent()
{
this(null);
}
/**
* Initializes the component. Displays the controls buttons.
*
* @param groups The collection of groups.
*/
public void initialize(Collection<GroupData> groups)
{
initialize(true, groups);
}
/**
* Initializes the component.
*
* @param showControl Pass <code>true</code> to display the buttons,
* <code>false</code> otherwise.
* @param groups The collection of groups.
*/
public void initialize(boolean showControl, Collection<GroupData> groups)
{
setDefaultContext();
if (groups == null)
throw new IllegalArgumentException("No groups specified.");
this.groups = groups;
initComponents();
buildGUI(showControl);
}
/**
* Returns the terms that may be in the document.
*
* @return See above.
*/
protected List<String> getTerms()
{
String[] terms = uiDelegate.getQueryTerms();
List<String> l = new ArrayList<String>();
if (terms != null) {
for (int i = 0; i < terms.length; i++) {
l.add(terms[i]);
}
}
return l;
}
/**
* Sets the values to add.
*
* @param terms The values to add.
*/
protected void setTerms(List<String> terms)
{
uiDelegate.setTerms(terms);
}
/**
* Returns the initial search context.
*
* @return See above.
*/
SearchContext getSearchContext() { return searchContext; }
/**
* Returns the collection of possible context.
*
* @return See above.
*/
List<SearchObject> getNodes() { return nodes; }
/**
* Returns the collection of possible types.
*
* @return See above.
*/
List<SearchObject> getTypes() { return types; }
/** Fires a property when the nodes are expanded. */
void notifyNodeExpanded()
{
firePropertyChange(NODES_EXPANDED_PROPERTY,Boolean.FALSE, Boolean.TRUE);
}
/** Fires a property change to search. */
void search() {
List<Integer> scope = uiDelegate.getScope();
SearchContext ctx;
String query = uiDelegate.getQuery();
if(query.trim().equals("*")) {
String msg = "Wildcard searches (*) must contain more than a single wildcard.";
DataBrowserAgent.getRegistry().getUserNotifier().notifyWarning("Cannot perform search", msg);
return;
}
ctx = new SearchContext(query, scope);
Timestamp start = uiDelegate.getFromDate();
Timestamp end = uiDelegate.getToDate();
if (start != null && end != null) {
if(start.after(end))
ctx.setTime(end, start);
else
ctx.setTime(start, end);
}
else if(start!=null) {
ctx.setTime(start, null);
}
else if(end!=null) {
ctx.setTime(null, end);
}
ctx.setTimeType(uiDelegate.getDateType().equals(SearchPanel.ITEM_ACQUISITIONDATE) ? SearchParameters.DATE_ACQUISITION : SearchParameters.DATE_IMPORT);
ctx.setSelectedOwner(uiDelegate.getUserId());
ctx.setSelectedGroup(uiDelegate.getGroupId());
ctx.setType(uiDelegate.getType());
firePropertyChange(SEARCH_PROPERTY, null, ctx);
}
/**
* Returns the list of possible groups.
*
* @return See above.
*/
List<GroupContext> getGroups()
{
if (groupsContext != null && groupsContext.size() > 0)
return groupsContext;
Iterator<GroupData> i = groups.iterator();
GroupData g;
GroupContext gc;
groupsContext = new ArrayList<GroupContext>();
while (i.hasNext()) {
g = i.next();
gc = new GroupContext(g);
groupsContext.add(gc);
}
return groupsContext;
}
/**
* Sets the buttons enabled when performing search.
*
* @param resultSize
* Number of results found, pass <code>-1</code> if search still
* in progress
*/
public void setSearchEnabled(int resultSize) {
if (resultSize == -1)
setSearchEnabled("Searching...", false);
else
setSearchEnabled(resultSize + " results found", false);
}
/**
* Sets the buttons enabled when performing search.
*
* @param text The text to display.
* @param b Pass <code>true</code> to enable the {@link #searchButton},
* <code>false</code>otherwise, and modifies the cursor.
*/
public void setSearchEnabled(String text, boolean b)
{
searchButton.setEnabled(!b);
busyLabel.setEnabled(b);
busyLabel.setBusy(b);
progressLabel.setText(text);
}
/**
* Adds the component displaying the result.
*
* @param result The value to set.
*/
public void displayResult(JComponent result)
{
remove(resultPane);
if (result != null) {
resultPane = result;
resultPane.setBackground(UIUtilities.BACKGROUND_COLOR);
add(resultPane, "0, 4");
}
repaint();
}
/**
* Adds the specified component to the result display.
*
* @param result The component to add.
* @param clear Pass <code>true</code> to remove the previous components,
* <code>false</code> otherwise.
*/
public void addResult(JComponent result, boolean clear)
{
if (clear) resultPane.removeAll();
if (result == null) return;
resultPane.add(result);
resultPane.add(new JSeparator());
result.setBackground(UIUtilities.BACKGROUND_COLOR);
revalidate();
repaint();
}
/**
* Cancels or searches.
* @see ActionListener#actionPerformed(ActionEvent)
*/
public void actionPerformed(ActionEvent e)
{
int index = Integer.parseInt(e.getActionCommand());
switch (index) {
case CANCEL:
cancel();
break;
case SEARCH:
search();
break;
case HELP:
String url = (String) DataBrowserAgent.getRegistry().lookup(
LookupNames.HELP_ON_LINE_SEARCH);
help(url);
break;
case RESET_DATE:
uiDelegate.resetDate();
break;
}
}
/** Subclasses should override this method. */
protected void help(String url) {}
/**
* Handles a SearchEvent (sent by the search field
* in the toolbar); just takes the search query from
* the event and initiates a search with it
*/
public void handleSearchEvent(SearchEvent evt) {
uiDelegate.reset();
uiDelegate.setTerms(Collections.singletonList(evt.getQuery()));
search();
}
}