/* *------------------------------------------------------------------------------ * 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.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import org.apache.commons.collections.CollectionUtils; import org.openmicroscopy.shoola.util.CommonsLangUtils; import org.jdesktop.swingx.JXDatePicker; import org.openmicroscopy.shoola.agents.dataBrowser.DataBrowserAgent; import org.openmicroscopy.shoola.agents.util.EditorUtil; import org.openmicroscopy.shoola.agents.util.finder.FinderFactory; import org.openmicroscopy.shoola.env.LookupNames; import org.openmicroscopy.shoola.env.ui.UserNotifier; import org.openmicroscopy.shoola.util.ui.IconManager; import org.openmicroscopy.shoola.util.ui.SeparatorPane; import org.openmicroscopy.shoola.util.ui.UIUtilities; import org.openmicroscopy.shoola.util.ui.search.ExperimenterContext; 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 org.openmicroscopy.shoola.util.ui.search.SearchUtil; import omero.gateway.model.ExperimenterData; /** * The Component hosting the various fields used to collect the context of the * search. * * @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 SearchPanel extends JPanel { /** Indicates that the date is to be interpreted as acquisition date */ public static final String ITEM_ACQUISITIONDATE = "Acquisition date"; /** Indicates that the date is to be interpreted as import date */ public static final String ITEM_IMPORTDATE = "Import date"; /** The title of the type UI component. */ private static final String TYPE_TITLE = "Type"; /** The number of columns of the search areas. */ private static final int AREA_COLUMNS = 12; private static final String DATE_TOOLTIP = "<html>Please select a date from the drop-down menu or enter<br> a date in the format YYYY-MM-DD (e. g. 2014-07-10)</html>"; public static final String DATE_TYPE_TOOLTIP = "Select the type of date (Acquisition date applies to images only)"; /** The terms to search for. */ private JTextField fullTextArea; /** Date used to specify the beginning of the time interval. */ private JXDatePicker fromDate; /** Date used to specify the ending of the time interval. */ private JXDatePicker toDate; /** Button to clear both date fields */ private JButton clearDate; /** Reference to the model . */ private SearchComponent model; /** Items used to defined the scope of the search (Name, Description, ...). */ private Map<Integer, JCheckBox> scopes; /** * Items used to defined the scope of the search. (Images, Projects, * Datasets, ...). */ private Map<Integer, JCheckBox> types; /** Button to bring up the tooltips for help. */ private JButton helpBasicButton; /** The component used to perform a basic search. */ private JPanel basicSearchComp; /** The component hosting either the advanced or basic search component. */ private JPanel searchFor; /** The box displaying the groups. */ private JComboBox groupsBox; /** The box displaying the users. */ private JComboBox usersBox; /** Box for selecting the type of the dates */ private JComboBox dateBox; /** * Creates a <code>JComboBox</code> with the available groups. * * @return See above. */ private JComboBox createGroupBox() { List<GroupContext> groups = model.getGroups(); Object[] values = new Object[groups.size() + 1]; values[0] = new GroupContext("All groups", GroupContext.ALL_GROUPS_ID); int j = 1; Iterator<GroupContext> i = groups.iterator(); while (i.hasNext()) { values[j] = i.next(); j++; } return new JComboBox(values); } /** Initializes the components composing the display. */ private void initComponents() { usersBox = new JComboBox(); groupsBox = createGroupBox(); scopes = new HashMap<Integer, JCheckBox>(model.getNodes().size()); types = new HashMap<Integer, JCheckBox>(model.getTypes().size()); IconManager icons = IconManager.getInstance(); fromDate = UIUtilities.createDatePicker(true, EditorUtil.DATE_PICKER_FORMAT); fromDate.setBackground(UIUtilities.BACKGROUND_COLOR); fromDate.addPropertyChangeListener("date", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Date d = (Date) evt.getNewValue(); if (d != null) { if (d.after(new Date())) { UserNotifier un = DataBrowserAgent .getRegistry().getUserNotifier(); un.notifyWarning("Invalid Date", "Selecting a future 'From' date doesn't make any sense."); fromDate.setDate(null); } if (toDate.getDate() != null && d.after(toDate.getDate())) { UserNotifier un = DataBrowserAgent .getRegistry().getUserNotifier(); un.notifyWarning("Invalid Date", "Cannot set a 'From' date which is more recent than the 'To' date."); fromDate.setDate(null); } } } }); fromDate.setToolTipText(DATE_TOOLTIP); toDate = UIUtilities.createDatePicker(true, EditorUtil.DATE_PICKER_FORMAT); toDate.setBackground(UIUtilities.BACKGROUND_COLOR); toDate.setToolTipText(DATE_TOOLTIP); toDate.addPropertyChangeListener("date", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Date d = (Date) evt.getNewValue(); if (d != null) { if (fromDate.getDate() != null && d.before(fromDate.getDate())) { UserNotifier un = DataBrowserAgent.getRegistry() .getUserNotifier(); un.notifyWarning("Invalid Date", "Cannot set a 'To' date which is prior to the 'From' date."); toDate.setDate(null); } } } }); clearDate = new JButton(icons.getIcon(IconManager.CLOSE)); clearDate.setToolTipText("Reset the dates"); UIUtilities.unifiedButtonLookAndFeel(clearDate); clearDate.setBackground(UIUtilities.BACKGROUND_COLOR); clearDate.setActionCommand("" + SearchComponent.RESET_DATE); clearDate.addActionListener(model); fullTextArea = new JTextField(AREA_COLUMNS); fullTextArea.addKeyListener(new KeyAdapter() { /** Finds the phrase. */ public void keyPressed(KeyEvent e) { Object source = e.getSource(); if (source != fullTextArea) return; switch (e.getKeyCode()) { case KeyEvent.VK_ENTER: model.search(); } } }); helpBasicButton = new JButton(icons.getIcon(IconManager.HELP)); helpBasicButton.setToolTipText("Search Tips."); helpBasicButton.setBackground(UIUtilities.BACKGROUND_COLOR); UIUtilities.unifiedButtonLookAndFeel(helpBasicButton); helpBasicButton.addActionListener(model); helpBasicButton.setActionCommand("" + SearchComponent.HELP); SearchContext ctx = model.getSearchContext(); if (ctx == null) return; } /** * Resets the date fields */ public void resetDate() { toDate.setDate(null); fromDate.setDate(null); } /** * Builds the panel hosting the time fields. * * @return See above; */ private JPanel buildTimeRange() { JPanel p = new JPanel(); p.setBackground(UIUtilities.BACKGROUND_COLOR); p.add(UIUtilities.setTextFont("From: ")); p.add(fromDate); p.add(UIUtilities.setTextFont("To: ")); p.add(toDate); p.add(clearDate); return p; } /** * Builds and lays out the component displaying the various options. * * @return See above. */ private JPanel buildFields() { List<SearchObject> nodes = model.getNodes(); SearchObject n; int m = nodes.size(); JCheckBox box; JPanel p = new JPanel(); p.setBackground(UIUtilities.BACKGROUND_COLOR); GridBagConstraints c = new GridBagConstraints(); c.weightx = 1.0; c.gridy = 1; List<Integer> ctxNodes = null; SearchContext ctx = model.getSearchContext(); if (ctx != null) ctxNodes = ctx.getContext(); if (ctxNodes == null) { for (int i = 0; i < m; i++) { n = nodes.get(i); box = new JCheckBox(n.getDescription()); box.setBackground(UIUtilities.BACKGROUND_COLOR); if (i % 2 == 0) { c.gridy++; } p.add(box, c); scopes.put(n.getIndex(), box); } } else { for (int i = 0; i < m; i++) { n = nodes.get(i); box = new JCheckBox(n.getDescription()); box.setBackground(UIUtilities.BACKGROUND_COLOR); box.setSelected(ctxNodes.contains(n.getIndex())); if (i % 2 == 0) c.gridy++; p.add(box, c); scopes.put(n.getIndex(), box); } } c.gridy++; UIUtilities.setBoldTitledBorder("Restrict by Field", p); return p; } /** * Builds and lays out the component displaying the various options. * * @return See above. */ private JPanel buildScope() { JPanel p = new JPanel(); p.setBackground(UIUtilities.BACKGROUND_COLOR); p.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; c.gridx = 0; c.gridy = 0; p.add(new JLabel("Groups:"), c); c.gridx = 1; p.add(groupsBox, c); groupsBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { updateUsersBox(); } }); c.gridx = 0; c.gridy++; p.add(new JLabel("Data owned by:"), c); c.gridx = 1; p.add(usersBox, c); UIUtilities.setBoldTitledBorder("Scope", p); return p; } /** * Updates the content of the usersBox depending on the current groupsBox * selection */ private void updateUsersBox() { updateUsersBox(false); } /** * Updates the content of the usersBox depending on the current groupsBox * selection * * @param reset * If <code>true</code> reset the selection to 'all' */ private void updateUsersBox(boolean reset) { ExperimenterContext me = new ExperimenterContext(getUserDetails()); ExperimenterContext all = new ExperimenterContext("All", ExperimenterContext.ALL_EXPERIMENTERS_ID); ExperimenterContext selected = (usersBox.getSelectedIndex() != -1 && !reset) ? (ExperimenterContext) usersBox .getSelectedItem() : null; usersBox.removeAllItems(); // the users to present in the combobox List<ExperimenterContext> items = new ArrayList<ExperimenterContext>(); // always add 'me' and 'all' items.add(all); items.add(me); // gather the users from the GroupContexts if (groupsBox.getSelectedIndex() > -1) { GroupContext groupContext = (GroupContext) groupsBox .getSelectedItem(); List<GroupContext> groupContexts = new ArrayList<GroupContext>(); if (groupContext.getId() == GroupContext.ALL_GROUPS_ID) { // users from all GroupContexts must be added for (int i = 0; i < groupsBox.getItemCount(); i++) { groupContext = (GroupContext) groupsBox.getItemAt(i); if (groupContext.getId() == GroupContext.ALL_GROUPS_ID) continue; groupContexts.add(groupContext); } } else { // just users from the selected GroupContext must be added groupContexts.add(groupContext); } for (GroupContext gc : groupContexts) { for (ExperimenterContext exp : gc.getExperimenters()) { if (!items.contains(exp)) { items.add(exp); } } } } for (ExperimenterContext item : items) { usersBox.addItem(item); } // restore the previous selection if there was any if (selected != null && items.contains(selected)) { usersBox.setSelectedItem(selected); } else { usersBox.setSelectedItem(me); } } /** * Builds and lays out the component displaying the various types. * * @return See above. */ private JPanel buildType() { JPanel p = new JPanel(); p.setBackground(UIUtilities.BACKGROUND_COLOR); p.setLayout(new GridBagLayout()); // p.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10)); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; c.insets = new Insets(3, 3, 3, 3); List<SearchObject> nodes = model.getTypes(); List<Integer> ctxNodes = null; SearchContext ctx = model.getSearchContext(); if (ctx != null) ctxNodes = ctx.getType(); SearchObject n; int m = nodes.size(); JCheckBox box; c.weightx = 1.0; if (ctxNodes == null) { for (int i = 0; i < m; i++) { n = nodes.get(i); box = new JCheckBox(n.getDescription()); box.setBackground(UIUtilities.BACKGROUND_COLOR); box.setSelected(true); p.add(box, c); if (i % 2 == 0) c.gridy++; types.put(n.getIndex(), box); } } else { for (int i = 0; i < m; i++) { n = nodes.get(i); box = new JCheckBox(n.getDescription()); box.setBackground(UIUtilities.BACKGROUND_COLOR); box.setSelected(ctxNodes.contains(n.getIndex())); p.add(box, c); if (i % 2 == 0) c.gridy++; types.put(n.getIndex(), box); } } UIUtilities.setBoldTitledBorder(TYPE_TITLE, p); return p; } /** * Builds and lays out the Basic search component. * * @return See above. */ private JPanel buildBasicSearchComp() { basicSearchComp = new JPanel(); basicSearchComp.setBackground(UIUtilities.BACKGROUND_COLOR); UIUtilities.setBoldTitledBorder("Search", basicSearchComp); basicSearchComp.setLayout(new BoxLayout(basicSearchComp, BoxLayout.X_AXIS)); basicSearchComp.add(fullTextArea); basicSearchComp.add(helpBasicButton); return basicSearchComp; } /** * Builds the UI component hosting the terms to search for. * * @return See above. */ private JPanel buildSearchFor() { if (searchFor == null) { searchFor = new JPanel(); searchFor.setBackground(UIUtilities.BACKGROUND_COLOR); searchFor.setLayout(new BoxLayout(searchFor, BoxLayout.Y_AXIS)); } searchFor.removeAll(); searchFor.add(buildBasicSearchComp()); return searchFor; } /** * Builds the UI component hosting the time interval. * * @return See above. */ private JPanel buildDate() { JPanel p = new JPanel(); p.setBackground(UIUtilities.BACKGROUND_COLOR); p.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.anchor = GridBagConstraints.WEST; c.fill = GridBagConstraints.HORIZONTAL; c.gridx = 0; c.gridy = 0; dateBox = new JComboBox(); dateBox.setToolTipText(DATE_TYPE_TOOLTIP); dateBox.addItem(ITEM_IMPORTDATE); dateBox.addItem(ITEM_ACQUISITIONDATE); p.add(dateBox); c.gridy++; p.add(buildTimeRange(), c); UIUtilities.setBoldTitledBorder("Date", p); return p; } /** Builds and lays out the UI. */ private void buildGUI() { setBorder(null); setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.WEST; c.insets = new Insets(0, 2, 2, 0); c.gridy = 0; c.gridx = 0; c.gridwidth = GridBagConstraints.REMAINDER; c.weightx = 1.0; setBackground(UIUtilities.BACKGROUND_COLOR); add(buildSearchFor(), c);// , "0, 0"); SeparatorPane sep = new SeparatorPane(); sep.setBackground(UIUtilities.BACKGROUND_COLOR); c.gridy++; add(sep, c);// , "0, 1"); JPanel typePanel = buildType(); c.gridy++; add(typePanel, c);// , "0, 2") JPanel fieldsPanel = buildFields(); c.gridy++; add(fieldsPanel, c);// , "0, 2"); JPanel scopePanel = buildScope(); c.gridy++; add(scopePanel, c);// , "0, 2"); JPanel datePanel = buildDate(); c.gridy++; add(datePanel, c);// , "0, 4"); updateUsersBox(); } /** * Creates a new instance. * * @param model * Reference to the model. Mustn't be <code>null</code>. */ SearchPanel(SearchComponent model) { if (model == null) throw new IllegalArgumentException("No model."); this.model = model; initComponents(); buildGUI(); } /** Resets. */ public void reset() { updateUsersBox(true); resetDate(); setTerms(Collections.<String> emptyList()); for (Entry<Integer, JCheckBox> scope : scopes.entrySet()) { scope.getValue().setSelected(false); } for (Entry<Integer, JCheckBox> type : types.entrySet()) { type.getValue().setSelected(true); } } /** * Returns the 'from' time. * * @return See above. */ Timestamp getFromDate() { Date d = fromDate.getDate(); if (d == null) return null; return new Timestamp(d.getTime()); } /** * Returns the 'to' time. * * @return See above. */ Timestamp getToDate() { Date d = toDate.getDate(); if (d == null) return null; return new Timestamp(d.getTime()); } /** * Returns the type of the date (acquisition/import) */ String getDateType() { return (String) dateBox.getSelectedItem(); } /** * Returns the scope of the search. * * @return See above. */ List<Integer> getScope() { List<Integer> list = new ArrayList<Integer>(); Iterator i = scopes.keySet().iterator(); JCheckBox box; Integer key; while (i.hasNext()) { key = (Integer) i.next(); box = scopes.get(key); if (box.isSelected()) list.add(key); } return list; } /** * Returns the scope of the search. * * @return See above. */ List<Integer> getType() { List<Integer> list = new ArrayList<Integer>(); Iterator i = types.keySet().iterator(); JCheckBox box; Integer key; while (i.hasNext()) { key = (Integer) i.next(); box = types.get(key); if (box.isSelected()) list.add(key); } return list; } /** * Sets the values to add. * * @param values * The values to add. */ void setTerms(List<String> terms) { if (CollectionUtils.isEmpty(terms)) return; StringBuffer text = new StringBuffer(); Iterator<String> i = terms.iterator(); while (i.hasNext()) { text.append(i.next()); if(i.hasNext()) text.append(SearchUtil.SPACE_SEPARATOR); } fullTextArea.setText(text.toString()); } String getQuery() { return fullTextArea.getText(); } /** * Returns the terms that may be in the document. * * @return See above. */ String[] getQueryTerms() { String text; text = fullTextArea.getText(); if (CommonsLangUtils.isNotBlank(text)) { List<String> l = SearchUtil.splitTerms(text); if (l.size() > 0) return l.toArray(new String[0]); } List<String> l = SearchUtil.splitTerms(text); if (l.size() > 0) return l.toArray(new String[] {}); return null; } /** * Returns the current user's details. * * @return See above. */ private ExperimenterData getUserDetails() { return (ExperimenterData) FinderFactory.getRegistry().lookup( LookupNames.CURRENT_USER_DETAILS); } /** * Get the selected groupId */ long getGroupId() { long result = GroupContext.ALL_GROUPS_ID; if (groupsBox.getSelectedIndex() >= 0) { GroupContext g = (GroupContext) groupsBox.getSelectedItem(); result = g.getId(); } return result; } /** * Get the selected userId */ long getUserId() { long result = -1; if (usersBox.getSelectedIndex() >= 0) { ExperimenterContext u = (ExperimenterContext) usersBox .getSelectedItem(); result = u.getId() == ExperimenterContext.ALL_EXPERIMENTERS_ID ? -1 : u.getId(); } return result; } }