/* Copyright (C) 2003 EBI, GRL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.ensembl.mart.explorer; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractButton; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JRadioButton; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.LineBorder; import javax.swing.text.JTextComponent; import org.ensembl.mart.guiutils.QuickFrame; import org.ensembl.mart.lib.Filter; import org.ensembl.mart.lib.IDListFilter; import org.ensembl.mart.lib.Query; import org.ensembl.mart.lib.config.FilterDescription; import org.ensembl.mart.lib.config.FilterGroup; import org.ensembl.mart.lib.config.Option; import org.ensembl.mart.util.LoggingUtil; /** * An ID list filter offers the user with a mechanism for filtering by IDs. The user can specify * a list oif IDs using verious sources and specify the type of the IDs. * @author <a href="mailto:craig@ebi.ac.uk">Craig Melsopp</a> * */ public class IDListFilterWidget extends FilterWidget implements ActionListener { /** * "Listens" to changes in component and performs button.doClick() in response. * coClick() is perfomred if "enter" is pressed in the component or the text changed between * the component gaining and losing focus. * @author <a href="mailto:craig@ebi.ac.uk">Craig Melsopp</a> */ private class ModificationListener extends KeyAdapter implements FocusListener { private AbstractButton button; private JTextComponent component; private int textHashCode = -1; /** * Adds self to component as both key and focus listener. * @param component * @param button */ public ModificationListener(JTextComponent component, AbstractButton button) { this.component = component; this.button = button; component.addKeyListener(this); component.addFocusListener(this); } /** * doClick() if "enter" pressed. */ public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER){ textHashCode = component.getText().hashCode(); button.doClick(); } } public void focusGained(FocusEvent e) { textHashCode = component.getText().hashCode(); } /** * doClick() if text changed between gaining and losing focus. */ public void focusLost(FocusEvent e) { int tmp = component.getText().hashCode(); if (tmp != textHashCode) { textHashCode = tmp; button.doClick(); } } } protected int lastIDStringHashCode; private JComboBox list = new JComboBox(); private JTextArea idString = new JTextArea(10, 10); private JTextField file = new JTextField(20); private JTextField url = new JTextField(20); private JButton chooseFileButton = new JButton("Choose"); private JFileChooser fileChooser = new JFileChooser(); private JRadioButton idStringRadioButton = new JRadioButton("IDs (type or paste)"); private JRadioButton fileRadioButton = new JRadioButton("File containing IDs"); private JRadioButton urlRadioButton = new JRadioButton("URL containing IDs"); private JRadioButton noneButton = new JRadioButton("None"); private Feedback feedback = new Feedback(this); /** * @param filterGroupWidget * @param query * @param filterDescription * @param tree */ public IDListFilterWidget( FilterGroupWidget filterGroupWidget, Query query, FilterDescription filterDescription, QueryTreeView tree) { super(filterGroupWidget, query, filterDescription, tree); file.setEditable(false); ButtonGroup bg = new ButtonGroup(); bg.add(idStringRadioButton); bg.add(fileRadioButton); bg.add(urlRadioButton); bg.add(noneButton); noneButton.setSelected(true); idStringRadioButton.addActionListener(this); fileRadioButton.addActionListener(this); urlRadioButton.addActionListener(this); noneButton.addActionListener(this); new ModificationListener(idString, idStringRadioButton); new ModificationListener(url, urlRadioButton); Box b = Box.createVerticalBox(); b.setBorder(new LineBorder(Color.BLACK)); b.add( createRow(createLabel(), (JComponent) Box.createHorizontalGlue(), null)); b.add(list); b.add(Box.createVerticalStrut(Constants.GAP_BETWEEN_COMPONENTS_IN_WIDGET)); b.add(createRow(idStringRadioButton, idString, null)); b.add(Box.createVerticalStrut(Constants.GAP_BETWEEN_COMPONENTS_IN_WIDGET)); b.add(createRow(fileRadioButton, chooseFileButton, file)); b.add(Box.createVerticalStrut(Constants.GAP_BETWEEN_COMPONENTS_IN_WIDGET)); b.add(createRow(urlRadioButton, url, null)); b.add(Box.createVerticalStrut(Constants.GAP_BETWEEN_COMPONENTS_IN_WIDGET)); b.add(createRow(noneButton, (JComponent) Box.createHorizontalGlue(), null)); setOptions(filterDescription.getOptions()); add(b); final IDListFilterWidget parent = this; chooseFileButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { file.setText(fileChooser.getSelectedFile().getAbsolutePath()); fileRadioButton.doClick(); } } }); } private JComponent createRow(JComponent a, JComponent b, JComponent c) { Box p = Box.createHorizontalBox(); if (a != null) p.add(a); if (b != null) p.add(b); if (c != null) p.add(c); return p; } public void setOptions(Option[] options) { if (list == null) return; list.removeActionListener(this); list.removeAllItems(); // add items for (int i = 0; i < options.length; i++) { Option o = options[i]; if (o.getHidden() != null && o.getHidden().equals("true")) continue; if (o.getAttribute("hideDisplay") != null && o.getAttribute("hideDisplay").equals("true")) continue; if (o.isSelectable()) list.addItem(new OptionToStringWrapper(this, o)); } list.addActionListener(this); list.validate(); } /** * Selects the button and item in list (if necessary) when filter changed. * This is a callback method called when a filter with the same fieldName * as this widget is added or * removed to/from the query. */ protected void setFilter(Filter filter) { if (filter == null) { noneButton.setSelected(true); } else { IDListFilter f = (IDListFilter) filter; String[] ids = null; URL u = null; File fl = null; if ((ids = f.getIdentifiers()) != null && ids.length != 0) { idStringRadioButton.setSelected(true); StringBuffer buf = new StringBuffer(); for (int i = 0; i < ids.length; i++) buf.append(ids[i]).append('\n'); idString.setText(buf.toString()); } else if ((u = f.getUrl()) != null) { urlRadioButton.setSelected(true); url.setText(u.toExternalForm()); } else if ((fl = f.getFile()) != null) { fileRadioButton.setSelected(true); file.setText(fl.getName()); fileChooser.setSelectedFile(fl); } } this.filter = filter; } /** * Updates query in response to a user action. Removes old filter if necessary, adds new one if necessary * , or replaces old with new if necessary. */ public void actionPerformed(ActionEvent e) { Filter newFilter = createFilter(); query.removeQueryChangeListener(this); if (newFilter == null) { if (filter != null) query.removeFilter(filter); } else { if (filter != null) query.replaceFilter(filter, newFilter); else query.addFilter(newFilter); } filter = newFilter; query.addQueryChangeListener(this); } /** * Creates a filter based on the current state of the widget. If "none" is selected or * the field associated with the radio button is empty/invalid then no filter is returned. * @return filter if current state relates to one, otherwise null. */ private Filter createFilter() { Option o = ((OptionToStringWrapper) list.getSelectedItem()).option; String f = o.getFieldFromContext(); String tc = o.getTableConstraintFromContext(); String k = o.getKeyFromContext(); if (idStringRadioButton.isSelected() && idString.getText().length() != 0) return new IDListFilter( f, tc, k, idString.getText().split("(\\s+|\\s*,\\s*)")); else if (urlRadioButton.isSelected() && url.getText().length() != 0) try { return new IDListFilter(f, tc, k, new URL(url.getText())); } catch (MalformedURLException e) { feedback.warning("There is a problem with the URL: " + url.getText()); noneButton.doClick(); } else if (fileRadioButton.isSelected() && file.getText().length() != 0) return new IDListFilter(f, tc, k, new File(file.getText())); return null; } /** * Unit test for this class. * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // enable logging messages LoggingUtil.setAllRootHandlerLevelsToFinest(); Logger.getLogger(Query.class.getName()).setLevel(Level.FINEST); /* Query q = new Query(); FilterGroup fg = new FilterGroup(); FilterGroupWidget fgw = new FilterGroupWidget(q, "fgw", fg, null); FilterDescription fd = new FilterDescription( "someInternalName", "someField", "boolean", "someQualifier", "someLegalQualifiers", "id_list test", "someTableConstraint", "someKey", "someDescription", "", "","","","","","",""); Option o = new Option("fred_id", "true"); o.setParent(fd); o.setDisplayName("Fred"); o.setField("fred_field"); fd.addOption(o); Option o2 = new Option("barney_id", "true"); o2.setParent(fd); o2.setDisplayName("Barney"); o2.setField("barney"); fd.addOption(o2); new QuickFrame( IDListFilterWidget.class.getName(), new IDListFilterWidget(null, q, fd, null)); */ } protected boolean equivalentFilter(Object otherFilter) { if (super.equivalentFilter(otherFilter)) return true; if (otherFilter == null || !(otherFilter instanceof Filter)) return false; if (indexOfListItemMatchingFilter((Filter) otherFilter) > -1) return true; return false; } /** * * @param otherFilter * @return -1 if filter not relat */ private int indexOfListItemMatchingFilter(Filter filter) { int index = -1; final int n = list.getItemCount(); for (int i = 0; index == -1 && i < n; i++) { OptionToStringWrapper op = (OptionToStringWrapper) list.getItemAt(i); Option o = op.option; String f = filter.getField(); String tc = filter.getTableConstraint(); String k = filter.getKey(); if (f != null && tc != null && k != null && !"".equals(f) && !"".equals(tc) && !"".equals(k) && f.equals(o.getFieldFromContext()) && tc.equals(o.getTableConstraintFromContext()) && k.equals(o.getKeyFromContext())) index = i; } return index; } }