package org.greenstone.gatherer.cdm; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Iterator; import org.greenstone.gatherer.Configuration; import org.greenstone.gatherer.DebugStream; import org.greenstone.gatherer.Dictionary; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.gui.GComboBox; import org.greenstone.gatherer.util.StaticStrings; import org.greenstone.gatherer.util.Utility; import org.greenstone.gatherer.metadata.MetadataElement; import org.greenstone.gatherer.metadata.MetadataSet; import org.greenstone.gatherer.metadata.MetadataSetManager; import org.greenstone.gatherer.metadata.MetadataTools; /** This class encapsulates all the technical difficulty of creating a specific control based on an Argument. */ public class ArgumentControl extends JPanel { static protected Dimension LABEL_SIZE = new Dimension(175, 25); static protected Dimension ROW_SIZE = new Dimension(400, 30); /** The Argument this control will be based on. */ private Argument argument = null; /** A checkbox to allow enabling or diabling of this Argument. */ private JCheckBox enabled = null; /** Some form of editor component, such as a JComboBox or JTextField, used to set parameters to an Argument. */ private JComponent value_control = null; /** Constructor. */ public ArgumentControl(Argument argument, boolean is_enabled, String preset_value) { this.setComponentOrientation(Dictionary.getOrientation()); this.argument = argument; String tip = "" + argument.getName()+": "+argument.getDescription() + ""; tip = Utility.formatHTMLWidth(tip, 80); setBackground(Configuration.getColor("coloring.collection_tree_background", false)); setBorder(BorderFactory.createEmptyBorder(2,0,2,0)); setLayout(new BorderLayout()); setPreferredSize(ROW_SIZE); // display the name set in the disp option if there is one // otherwise display name option value instead String dispName = argument.getDisplayName(); if(dispName.equals("")){ dispName = argument.getName(); } if (argument.isRequired()) { //JLabel label = new JLabel(argument.getName()); JLabel label = new JLabel(dispName); label.setComponentOrientation(Dictionary.getOrientation()); label.setOpaque(false); label.setPreferredSize(LABEL_SIZE); label.setToolTipText(tip); add(label, BorderLayout.LINE_START); } else { //enabled = new JCheckBox(argument.getName()); enabled = new JCheckBox(dispName); enabled.setComponentOrientation(Dictionary.getOrientation()); enabled.setOpaque(false); enabled.setPreferredSize(LABEL_SIZE); enabled.setToolTipText(tip); add(enabled, BorderLayout.LINE_START); } String initial_value; if (preset_value != null && !preset_value.equals("")) { initial_value = preset_value; } else { initial_value = argument.getValue(); } if (initial_value == null || initial_value.equals("")) { initial_value = argument.getDefaultValue(); } if (initial_value == null) { initial_value = ""; } switch(argument.getType()) { case Argument.ENUM: ArrayList option_list = argument.getOptions(); value_control = new GComboBox(option_list.toArray(), false, false); value_control.setComponentOrientation(Dictionary.getOrientation()); selectValue((JComboBox)value_control, initial_value); // also sets the tooltip ((JComboBox)value_control).addActionListener(new ToolTipUpdater()); break; case Argument.FLAG: // Only need the check box. break; case Argument.INTEGER: // Build a spinner int initial_int=0; // If there was an original value, set it. try { initial_int = Integer.parseInt(initial_value); } catch (Exception error) { DebugStream.println("ArgumentControl Error: "+error); } if (initial_int < argument.getMinimum()) { initial_int = argument.getMinimum(); } else if (initial_int > argument.getMaximum()) { initial_int = argument.getMaximum(); } JSpinner spinner = new JSpinner(new SpinnerNumberModel(initial_int, argument.getMinimum(), argument.getMaximum(), 1)); spinner.setComponentOrientation(Dictionary.getOrientation()); // And remember it value_control = spinner; break; case Argument.REGEXP: case Argument.STRING: value_control = new JTextField(initial_value); value_control.setComponentOrientation(Dictionary.getOrientation()); break; case Argument.LANGUAGE: value_control = new GComboBox(CollectionDesignManager.language_manager.getLanguageCodes().toArray(), false); value_control.setComponentOrientation(Dictionary.getOrientation()); // we want to display the language name not the code ((JComboBox)value_control).setRenderer(new LanguageListCellRenderer()); // Now ensure we have the existing value or default value selected if either exist in our known languages String lang_name = CollectionDesignManager.language_manager.getLanguageName(initial_value); if (lang_name != null) { ((JComboBox)value_control).setSelectedItem(initial_value); } break; case Argument.METADATUM: case Argument.METADATA: value_control = new GComboBox(MetadataSetManager.getEveryMetadataSetElement(), false); value_control.setComponentOrientation(Dictionary.getOrientation()); // Editable for advanced modes (allows things like dc.Title,ex.Title) if (Configuration.getMode() > Configuration.ASSISTANT_MODE) { ((JComboBox) value_control).setEditable(true); } // Now ensure we have the existing value or default value selected if either exist. String existing_value = preset_value; if (existing_value == null || existing_value.length() == 0) { existing_value = argument.getValue(); } if (existing_value != null && existing_value.length() > 0) { boolean found = selectValue((JComboBox) value_control, existing_value); // It's possible that this is a custom value and so doesn't exist in the combobox if (!found) { // If so, add it then select it ((JComboBox) value_control).addItem(existing_value); ((JComboBox) value_control).setSelectedItem(existing_value); } } else { String default_value = argument.getDefaultValue(); if (default_value != null) { // if no namespace for default value, add ex. // won't work if we want to set a non-metadata value if (MetadataTools.getMetadataSetNamespace(default_value).equals("")) { default_value = StaticStrings.EXTRACTED_NAMESPACE+default_value; } selectValue((JComboBox) value_control, default_value); } } break; case Argument.METADATA_SET_NAMESPACE: value_control = new JComboBox(); value_control.setComponentOrientation(Dictionary.getOrientation()); // !! Hack for exploding metadata databases: add the (empty) exploded metadata set File exploded_metadata_set_file = new File(Gatherer.getGLIMetadataDirectoryPath() + "exp" + StaticStrings.METADATA_SET_EXTENSION); MetadataSet exploded_metadata_set = new MetadataSet(exploded_metadata_set_file); Gatherer.c_man.importMetadataSet(exploded_metadata_set); // All the loaded metadata sets except the extracted metadata set are applicable ArrayList metadata_sets = MetadataSetManager.getMetadataSets(); for (int i = metadata_sets.size() - 1; i >= 0; i--) { MetadataSet metadata_set = (MetadataSet) metadata_sets.get(i); if (!(metadata_set.getNamespace().equals(MetadataSetManager.EXTRACTED_METADATA_NAMESPACE))) { ((JComboBox)value_control).addItem(metadata_set); } } selectValue((JComboBox) value_control, initial_value); } // end of switch // Enable or disable as necessary. if(argument.isRequired() || argument.isAssigned() || is_enabled) { if (enabled != null) { enabled.setSelected(true); } if(value_control != null) { value_control.setOpaque(true); value_control.setBackground(Color.white); value_control.setEnabled(true); if(value_control instanceof JSpinner) { // Set enabled JComponent c = ((JSpinner)value_control).getEditor(); if ( c instanceof JSpinner.DefaultEditor ) { JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) c; JFormattedTextField field = editor.getTextField(); field.setEditable(true); field.setBackground(Color.white); } } } } else { if (enabled != null) { enabled.setSelected(false); } if(value_control != null) { value_control.setOpaque(true); value_control.setBackground(Color.lightGray); value_control.setEnabled(false); if(value_control instanceof JSpinner) { // Set enabled JComponent c = ((JSpinner)value_control).getEditor(); if ( c instanceof JSpinner.DefaultEditor ) { JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) c; JFormattedTextField field = editor.getTextField(); field.setEditable(false); field.setBackground(Color.lightGray); } } } } // Listener if(value_control != null) { if (argument.getType() != Argument.ENUM) { // enums have already set tooltips based on option value value_control.setToolTipText(tip); } add(value_control, BorderLayout.CENTER); if (!argument.isRequired()) { enabled.addActionListener(new EnabledListener(value_control)); } } } public Argument getArgument() { return argument; } public String getArgumentName() { return argument.getName(); } public String getValue() { if(value_control == null) { return null; } if (value_control instanceof JSpinner) { return ((JSpinner)value_control).getValue().toString(); } if(value_control instanceof JComboBox) { Object selected_item = ((JComboBox)value_control).getSelectedItem(); if (selected_item != null) { if (argument.getType() == Argument.METADATA_SET_NAMESPACE) { return ((MetadataSet) selected_item).getNamespace(); } if (selected_item instanceof Argument.ArgumentOption) { return ((Argument.ArgumentOption)selected_item).name; } if (selected_item instanceof MetadataElement) { return ((MetadataElement) selected_item).getFullName(); } return selected_item.toString(); } return null; } if(value_control instanceof JTextField) { return ((JTextField)value_control).getText(); } return null; } /** Retrieve the control used for storing values. * @return JComponent */ public JComponent getValueControl() { return value_control; } public boolean isEnabled() { if (enabled == null) { return true; // always enabled } return enabled.isSelected(); } /** Identifies this control by returning the name of the Argument it is based on. * @return The name of the Argument as a String. * @see org.greenstone.gatherer.cdm.Argument */ public String toString() { return argument.getName(); } /** Updates the enwrapped Argument using the values provided by the controls. * @return true if the update was successful, false otherwise. * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.Argument.ArgumentOption * @see org.greenstone.gatherer.cdm.Language */ public boolean updateArgument() { if(argument.isRequired() || enabled.isSelected() ) { argument.setAssigned(false); String result = null; switch(argument.getType()) { case Argument.ENUM: Argument.ArgumentOption option = (Argument.ArgumentOption)((JComboBox)value_control).getSelectedItem(); if(option != null && option.name.length() > 0) { argument.setValue(option.name); } else { String args[] = new String[1]; args[0] = argument.getName(); if(argument.isRequired()) { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.Required_Argument", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); } // They've left the field blank else { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.No_Value", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); argument.setValue(null); } args = null; return false; } argument.setAssigned(true); return true; case Argument.FLAG: // Should have already been handled above. argument.setAssigned(true); return true; case Argument.INTEGER: result = ((JSpinner)value_control).getValue().toString(); if(result.length() > 0) { // Test if the value entered is a valid int. try { int x = Integer.parseInt(result); } catch(NumberFormatException nfe) { String args[] = new String[2]; args[0] = argument.getName(); args[1] = result; JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.Bad_Integer", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); args = null; return false; } argument.setValue(result); } else { String args[] = new String[1]; args[0] = argument.getName(); if(argument.isRequired()) { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.Required_Argument", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); } // They've left the field blank else { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.No_Value", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); argument.setValue(null); } args = null; return false; } argument.setAssigned(true); return true; case Argument.LANGUAGE: String language = (((JComboBox)value_control).getSelectedItem()).toString(); argument.setValue(language); // Kinda lucked out here. Its impossible not to choose an entry from these comboboxes as they are restricted. argument.setAssigned(true); return true; case Argument.METADATUM: case Argument.METADATA: Object new_value_raw = ((JComboBox) value_control).getSelectedItem(); if (new_value_raw instanceof MetadataElement) { argument.setValue(((MetadataElement) new_value_raw).getFullName()); } else { // But we have to be careful as an arbitary string object could be zero length String new_value = new_value_raw.toString(); ///ystem.err.println("The current value is: " + new_value); if(new_value.length() > 0) { argument.setValue(new_value); } else { String args[] = new String[1]; args[0] = argument.getName(); if(argument.isRequired()) { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.Required_Argument", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); } // They've left the field blank else { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.No_Value", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); argument.setValue(null); } args = null; return false; } } argument.setAssigned(true); return true; case Argument.REGEXP: case Argument.STRING: result = ((JTextField)value_control).getText(); if(result.length() > 0) { argument.setValue(result); } else { String args[] = new String[1]; args[0] = argument.getName(); if(argument.isRequired()) { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.Required_Argument", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); } // They've left the field blank else { JOptionPane.showMessageDialog(this, Dictionary.get("CDM.ArgumentConfiguration.No_Value", args), Dictionary.get("CDM.ArgumentConfiguration.Error_Title"), JOptionPane.ERROR_MESSAGE); argument.setValue(null); } args = null; return false; } argument.setAssigned(true); return true; } return false; } else { argument.setAssigned(false); return true; } } public boolean updateArgument(boolean checkRequired) { if (checkRequired){ return updateArgument(); } else{ if (argument.getType() == Argument.STRING){ String result = ((JTextField)value_control).getText(); if(result.length() > 0) { argument.setValue(result); argument.setAssigned(true); } } } return true; } /** Method to ensure that a certain value is selected, if it exists within that combobox to begin with. * @param combobox The JComboBox whose selection we are trying to preset. * @param target The desired value of the selection as a String. * @return true if the item was found and selected, false otherwise * @see org.greenstone.gatherer.cdm.ArgumentConfiguration.Argument.ArgumentOption */ public static boolean selectValue(JComboBox combobox, String target) { for (int i = 0; i < combobox.getItemCount(); i++) { Object object = combobox.getItemAt(i); if (object instanceof Argument.ArgumentOption) { Argument.ArgumentOption opt = (Argument.ArgumentOption) object; if (opt.name.startsWith(target)) { combobox.setSelectedIndex(i); combobox.setToolTipText(opt.getToolTip()); return true; } } else if (object instanceof MetadataElement) { if(((MetadataElement)object).getFullName().equals(target)) { combobox.setSelectedIndex(i); return true; } } else if (object.toString().equals(target)) { combobox.setSelectedIndex(i); return true; } } return false; } /** Forces the control into an 'enabled' mode. */ public void setEnabled() { enabled.setSelected(true); } /** Explicitly sets the value of a JTextField type control to the given String. * @param value_str The new value of the control as a String. */ public void setValue(String value_str) { ((JTextField)value_control).setText(value_str); } /** Listens for actions apon the enable checkbox, and if detected enables or diables control appropriately. */ private class EnabledListener implements ActionListener { /** An editor component, such as a JComboBox or JTextField, that might have its enabled state changed by this listener. */ private JComponent target = null; /** Constructor. */ public EnabledListener(JComponent target) { this.target = target; } /** Any implementation of ActionListener must include this method so that we can be informed when an action has been performed on or registered check box, prompting us to change the state of the other controls as per the users request. * @param event An ActionEvent containing information about the click. */ public void actionPerformed(ActionEvent event) { if (this.target == null) { return; } JCheckBox source = (JCheckBox)event.getSource(); if(source.isSelected()) { target.setBackground(Color.white); target.setEnabled(true); } else { target.setBackground(Color.lightGray); target.setEnabled(false); } // Special case of stupid JSpinners who don't let their backgrounds change properly. if(target instanceof JSpinner) { JSpinner spinner = (JSpinner) target; JComponent c = spinner.getEditor(); if ( c instanceof JSpinner.DefaultEditor ) { JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) c; JFormattedTextField field = editor.getTextField(); field.setEditable(source.isSelected()); if(source.isSelected()) { field.setBackground(Color.white); } else { field.setBackground(Color.lightGray); } } } } } /** Listener that sets the tooltip associated to a combobox to the tooltip relevant to the selected item. */ private class ToolTipUpdater implements ActionListener { /** Any implementation of an ActionListener must include this method so that we can be informed when the selection in a combobox has changed and update the tooltip accordingly. * @param event An ActionEvent containing information about the action that fired this call. */ public void actionPerformed(ActionEvent event) { JComboBox source = (JComboBox)event.getSource(); Object object = source.getSelectedItem(); if(object instanceof Argument.ArgumentOption) { Argument.ArgumentOption opt = (Argument.ArgumentOption)object; if(opt != null) { source.setToolTipText(opt.getToolTip()); } else { source.setToolTipText(StaticStrings.EMPTY_STR); } } } } }