/* 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.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JTextField; import org.ensembl.mart.lib.BasicFilter; import org.ensembl.mart.lib.Filter; import org.ensembl.mart.lib.Query; import org.ensembl.mart.lib.config.ConfigurationException; 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; /** * Represents a set of user options as a tree. * Component consists of a label, text area and button. * */ public class TreeFilterWidget extends FilterWidget { /** * Test program; a simple GUI using test data. * @param args * @throws org.ensembl.mart.lib.config.ConfigurationException */ public static void main(String[] args) throws org.ensembl.mart.lib.config.ConfigurationException { // switch on logging for test purposes. LoggingUtil.setAllRootHandlerLevelsToFinest(); Logger.getLogger(Query.class.getName()).setLevel(Level.FINE); /* Query q = new Query(); FilterGroup fg = new FilterGroup(); FilterGroupWidget fgw = new FilterGroupWidget(q, "fgw", fg, null); FilterDescription fd = new FilterDescription( "someInternalName", "someField", "text", "someQualifier", "someLegalQualifiers", "someDisplayName", "someTableConstraint", "someKey", "someDescription", "", "","","","","","",""); TreeFilterWidget tfw = new TreeFilterWidget(fgw, q, fd, null); JFrame f = new JFrame("Tree Filter - test"); f.getContentPane().add(tfw); f.pack(); f.setVisible(true); */ } private Feedback feedback = new Feedback(this); private HashSet allOptions; private JMenuItem nullItem; private Option nullOption; private Option lastSelectedOption; private Logger logger = Logger.getLogger(TreeFilterWidget.class.getName()); /** represent a "tree" of options. */ private JMenuBar treeMenu = new JMenuBar(); private JMenu treeTopOptions = null; private JLabel label = null; private JTextField currentSelectedText = new JTextField(30); private JButton button = new JButton("change"); private String propertyName; private Map valueToOption = new HashMap(); private Option option = null; /** * * @param query * @param filterDescription */ public TreeFilterWidget( FilterGroupWidget filterGroupWidget, Query query, FilterDescription filterDescription, QueryTreeView tree) { super(filterGroupWidget, query, filterDescription, tree); try { nullOption = new Option("No Filter", "true"); } catch (ConfigurationException e) { // shouldn't happen e.printStackTrace(); } nullItem = new JMenuItem(nullOption.getInternalName()); nullItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { setOption(nullOption); } }); // default property name. this.propertyName = filterDescription.getInternalName(); label = new JLabel(filterDescription.getDisplayName()); currentSelectedText.setEditable(false); currentSelectedText.setMaximumSize(new Dimension(400, 27)); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { showTree(); } }); // make the menu appear beneath the row of components // containing the label, textField and button when displayed. treeMenu.setMaximumSize(new Dimension(0, 100)); setOptions(filterDescription.getOptions()); Box box = Box.createHorizontalBox(); box.add(treeMenu); box.add(label); box.add(Box.createHorizontalStrut(5)); box.add(button); box.add(Box.createHorizontalStrut(5)); box.add(currentSelectedText); setLayout(new BorderLayout()); add(box, BorderLayout.NORTH); option = nullOption; lastSelectedOption = option; } /** * Adds menu items and submenus to menu based on contents of _options_. A submenu is added * when an option contains * sub options. If an option has no sub options it is added as a leaf node. This method calls * itself recursively to build up the menu tree. * @param menu menu to add options to * @param options options to be added, method does nothing if null. * @param prefix prepended to option.getDisplayName() to create internal name for menu item * created for each option. */ private void addOptions(JMenu menu, Option[] options, String prefix) { for (int i = 0; options != null && i < options.length; i++) { final Option option = options[i]; if (option.getHidden() != null && option.getHidden().equals("true")) continue; if (option.getAttribute("hideDisplay") != null && option.getAttribute("hideDisplay").equals("true")) continue; String displayName = option.getDisplayName(); String qualifiedName = prefix + " " + displayName; if (option.getOptions().length == 0) { // add menu item JMenuItem item = new JMenuItem(displayName); item.setName(qualifiedName); menu.add(item); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { setOption(option); } }); valueToOption.put(option.getValue(), option); } else { // Add sub menu JMenu subMenu = new JMenu(displayName); menu.add(subMenu); addOptions(subMenu, option.getOptions(), qualifiedName); } } } /** * Sets the currentlySelectedText and node label based on * the option. If option is null these values are cleared. * @param option */ private void updateDisplay(Option option) { String name = ""; if (option != null && option != nullOption) name = option.getDisplayName(); currentSelectedText.setText(name); setNodeLabel(fieldName, name); } /** * * @return selected option if one is selected, otherwise null. */ public Option getOption() { return option; } /** * Default value is filterDescription.getInternalName(). * @return propertyName included in PropertyChangeEvents. */ public String getPropertyName() { return propertyName; } /** * Set the propertyName to some specific value. * @param string */ public void setPropertyName(String string) { propertyName = string; } /* (non-Javadoc) * @see org.ensembl.mart.explorer.FilterWidget#setOptions(org.ensembl.mart.lib.config.Option[]) */ public void setOptions(Option[] options) { if ( filter!=null ) query.removeFilter( filter ); // reset the maps so we can can find things later valueToOption.clear(); treeMenu.removeAll(); treeTopOptions = new JMenu(); treeMenu.add(treeTopOptions); // add the nullItem to the top of the list, user selects this to clear // choice. treeTopOptions.add(nullItem); valueToOption.put(nullOption.getValue(), nullOption); addOptions(treeTopOptions, options, ""); allOptions = new HashSet(valueToOption.values()); } public void showTree() { treeTopOptions.doClick(); } /** * Sets filter and also causes the appropriate item in the tree to be selected and any relevant * PushOption to be assigned. If filter is null then "No Filter" is selected * and and PushOption are unassigned. * @see org.ensembl.mart.explorer.FilterWidget#setFilter(org.ensembl.mart.lib.Filter) */ protected void setFilter(Filter filter) { this.filter = filter; if (filter == null) { updateDisplay(null); unassignPushOptions(); } else { Option option = (Option) valueToOption.get(filter.getValue()); updateDisplay(option); assignPushOptions(option.getPushActions()); } } /** * Callback method called in response to user selecting an item. * Updates query by removing any relevant query and adding a new one, also * updates other widgets with push options. * @param option should be one of the options currently available to this filter. * @throws IllegalArgumentException if option unavailable in filter. */ public void setOption(Option option) { if (!allOptions.contains(option)) throw new IllegalArgumentException( "Option is unailable in filter: " + option); if (option == lastSelectedOption) return; updateDisplay(option); this.option = option; unassignPushOptions(); if (filter != null) query.removeFilter(filter); filter = null; if (option != nullOption) { assignPushOptions(option.getPushActions()); String tmp = option.getValueFromContext(); String value = (tmp != null && !"".equals(tmp)) ? tmp : null; if (value != null) { // need to reset FilterWidget.fieldName because it is used by // FilterWidget.equivalentFilter(...) during callbacks fieldName = option.getFieldFromContext(); if ( fieldName == null) { String s = "Can't add filter because of configuration problem in DatsetConfig."; String s2 = s + "option= " + option + " filterDescription=" + filterDescription; logger.warning(s2); feedback.warning(s); // tidy up after disovering problem. Force the selected item // to be removed. lastSelectedOption = option; setOption(nullOption); } else { //String handler = option.getHandlerFromContext(); filter = new BasicFilter( fieldName, option.getTableConstraintFromContext(), option.getKeyFromContext(), "=", value); query.addFilter(filter); } } } lastSelectedOption = option; } }