/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * *

* * Author: John Thompson, Greenstone Digital Library, University of Waikato * *

* * Copyright (C) 1999 New Zealand Digital Library Project * *

* * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.cdm; /************************************************************************************** * Title: Gatherer * Description: The Gatherer: a tool for gathering and enriching a digital collection. * Copyright: Copyright (c) 2001 * Company: The University of Waikato * Written: 03/05/02 * Revised: 28/05/02 **************************************************************************************/ import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.text.JTextComponent; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.cdm.CollectionDesignManager; import org.greenstone.gatherer.cdm.CollectionMeta; import org.greenstone.gatherer.util.EmailAddress; import org.greenstone.gatherer.util.Utility; /** This class is responsible for generating the necessary GUI components. It does this by calling the getEditControls() method of the appropriate class (i.e. IndexManager for index related edits). It also is in charge of correctly adding (and removing) listeners, using phrases from the Dictionary rather than the text keys inserted in the component classes, and several other aspects of the design page, including the config file section tree. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ public class GUI extends JPanel { /** A reference to the collection manager, as it is here that all of the other data members and managers reside. */ private CollectionDesignManager manager = null; /** The controls used to modify the general options. */ private Control controls = null; /** A tree to serve as a 'table of contents' for this design tool. We decided on a tree rather than a list, as it allows us to break sections into subsections if they become to complicated. */ private DesignTree tree = null; /** A reference to the Gatherer. */ private Gatherer gatherer = null; /** The title located just above the content tree. */ private JLabel title = null; /** The panel containing both the title and the tree. */ private JPanel tree_pane = null; /** The panel apon which is rendered the currently selected section screen. */ private JPanel view = null; /** The available subscreens. */ static final public String CONTENTS[] = {"General", "Indexes", "Subcollections", "Languages", "Plugins", "Classifiers", "Formats", "MetadataSets"}; /** The preferred size of the collection design module screen real-estate. */ static final private Dimension SIZE = new Dimension(760, 500); /** Constructor. * @see DesignTree * @see org.greenstone.gatherer.cdm.CollectionDesignManager */ public GUI(Gatherer gatherer, CollectionDesignManager manager) { super(); // Assignments this.gatherer = gatherer; this.manager = manager; // Creation this.title = new JLabel(get("Design_Topics")); this.tree = new DesignTree(); this.tree_pane = new JPanel(); this.view = getControls(); // Connect tree.addTreeSelectionListener(new TreeListener()); tree_pane.setLayout(new BorderLayout()); tree_pane.add(title, BorderLayout.NORTH); tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER); // Layout setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); setLayout(new BorderLayout()); add(tree_pane, BorderLayout.WEST); add(view, BorderLayout.CENTER); } /** Mark the current set of controls as invalid. If they are needed again in the future new controls will be generate. * @see org.greenstone.gatherer.cdm.GUI.Control */ public void invalidateControls() { if(controls != null) { controls.destroy(); } controls = null; } /** Force the display to show a certain pane of controls. * @param type A String giving the name of the submanager view we wish to display. */ public void setSelectedView(String type) { tree.setSelectedView(type); } /** Overrides the normal updateUI to ensure that the current view also recieves an update message. */ public void updateUI() { if(view != null) { view.updateUI(); } super.updateUI(); } /**Overridden so we can exit when window is closed * @param event A WindowsEvent that encapsulates all the information gathered about the event that called this method. * @see org.greenstone.gatherer.cdm.CollectionDesignManager */ protected void processWindowEvent(WindowEvent event) { if(event.getID() == WindowEvent.WINDOW_CLOSING) { manager.save(); System.exit(0); } } /** Overloaded to call get with both a key and an empty argument array. * @param key A String which is mapped to a initial String within the ResourceBundle. * @return A String which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call. */ private String get(String key) { return get(key, null); } /** Used to retrieve a property value from the Locale specific ResourceBundle, based upon the key and arguments supplied. If the key cannot be found or if some other part of the call fails a default (English) error message is returned.
* Here the get recieves a second argument which is an array of Strings used to populate argument fields, denoted {n}, within the value String returned. Note that argument numbers greater than or equal to 32 are automatically mapped to the formatting String named Fargn. * @param key A String which is mapped to a initial String within the ResourceBundle. * @param args A String[] used to populate argument fields within the complete String. * @return A String which has been referenced by the key String and that either contains no argument fields, or has had the argument fields automatiically populated with formatting Strings of with argument String provided in the get call. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.Dictionary */ private String get(String key, String args[]) { if(key.indexOf('.') == -1) { key = "CDM.GUI." + key; } return gatherer.dictionary.get(key, args); } /** Because the CollectionDesignManager has enough to do, this class is resposible for generating the controls for the general options, all of which are stored in the aforementioned manager. * @return A JPanel containing controls for editing the general options. */ private JPanel getControls() { if(controls == null) { controls = new Control(); } return controls; } /** This class represents the visual component of the general options stored in the CollectionDesignManager. */ private class Control extends JPanel { /** The collection metadata representing the extra or description of the collection. */ private CollectionMeta collection_extra_data = null; /** The collection metadata representing the name of the collection. */ private CollectionMeta collection_name_data = null; /** The collection metadata representing the icon file location of the collection. */ private CollectionMeta icon_collection_data = null; /** The default size of label on this control. */ private Dimension LABEL_SIZE = new Dimension(200,25); /** The checkbox controlling public access to the collection. */ private JCheckBox access = null; /** The checkbox controlling the state of the collection. */ private JCheckBox beta = null; /** The label denoting the collection extra area. */ private JLabel collection_extra_label = null; /** The label denoting the collection extra language (Default). */ private JLabel collection_extra_language = null; /** The label denoting the collection name area. */ private JLabel collection_name_label = null; /** The label denoting the name language (Default). */ private JLabel collection_name_language = null; /** The label denoting the collection icon area. */ private JLabel icon_collection_label = null; /** The label denoting the icon language (Default). */ private JLabel icon_collection_language = null; /** The label which serves as the title of this view. */ private JLabel title = null; /** The panel containing the access controls. */ private JPanel access_pane = null; /** The panel containing the state controls. */ private JPanel beta_pane = null; /** The central pane that will contain the view. */ private JPanel central_pane = null; /** A panel used to affect internal layout of the collection extra area. */ private JPanel collection_extra_inner_pane = null; /** The panel containing the collection extra area. */ private JPanel collection_extra_pane = null; /** A panel used to affect internal layout of the collection name area. */ private JPanel collection_name_inner_pane = null; /** The panel containing the collection name area. */ private JPanel collection_name_pane = null; /** A panel used to affect internal layout of the collection icon area. */ private JPanel icon_collection_inner_pane = null; /** The panel containing the icon area. */ private JPanel icon_collection_pane = null; /** A text area used to display the inline help for this manager. */ private JTextArea instructions = null; /** The text field used to edit the collections title. */ private JTextField collection_name = null; /** The text field used to edit the file name of the collections icon. */ private JTextField icon_collection = null; /** A text area used to modify the collection description. */ private JTextArea collection_extra = null; /** Constructor. * @see org.greenstone.gatherer.cdm.CollectionDesignManager * @see org.greenstone.gatherer.cdm.CollectionMeta * @see org.greenstone.gatherer.cdm.CollectionMetaManager * @see org.greenstone.gatherer.cdm.Language * @see org.greenstone.gatherer.cdm.LanguageManager */ public Control() { super(); collection_extra_data = manager.collectionmetadatum.getCollectionExtra(); collection_name_data = manager.collectionmetadatum.getCollectionName(); icon_collection_data = manager.collectionmetadatum.getIconCollection(); // Creation. access = new JCheckBox(get("CDM.General.Access")); access.setSelected(manager.public_col); access_pane = new JPanel(); beta = new JCheckBox(get("CDM.General.Beta")); beta.setSelected(manager.beta); beta_pane = new JPanel(); central_pane = new JPanel(); collection_extra = new JTextArea(); collection_extra_inner_pane = new JPanel(); collection_extra_label = new JLabel(get("CDM.General.Collection_Extra")); collection_extra_label.setHorizontalAlignment(JLabel.CENTER); collection_extra_language = new JLabel(); collection_extra_pane = new JPanel(); if(collection_extra_data != null) { collection_extra.setText(collection_extra_data.getValue()); Language language = null; if((language = collection_extra_data.getLanguage()) != null) { collection_extra_language.setText(language.toString()); } else { collection_extra_language.setText(manager.languages.getDefaultLanguage().toString()); } } collection_name = new JTextField(); collection_name_inner_pane = new JPanel(); collection_name_label = new JLabel(get("CDM.General.Collection_Name")); collection_name_label.setPreferredSize(LABEL_SIZE); collection_name_language = new JLabel(); collection_name_pane = new JPanel(); if(collection_name_data != null) { collection_name.setText(collection_name_data.getValue()); Language language = null; if((language = collection_name_data.getLanguage()) != null) { collection_name_language.setText(language.toString()); } else { collection_name_language.setText(manager.languages.getDefaultLanguage().toString()); } } icon_collection = new JTextField(); icon_collection_inner_pane = new JPanel(); icon_collection_label = new JLabel(get("CDM.General.Icon_Collection")); icon_collection_label.setPreferredSize(LABEL_SIZE); icon_collection_language = new JLabel(); icon_collection_pane = new JPanel(); if(icon_collection_data != null) { icon_collection.setText(icon_collection_data.getValue()); Language language = null; if((language = icon_collection_data.getLanguage()) != null) { icon_collection_language.setText(language.toString()); } else { icon_collection_language.setText(manager.languages.getDefaultLanguage().toString()); } } instructions = new JTextArea(get("CDM.General.Instructions")); instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false)); instructions.setEditable(false); instructions.setLineWrap(true); instructions.setRows(4); instructions.setWrapStyleWord(true); JPanel lower = new JPanel(); title = new JLabel(get("CDM.General.Title")); title.setHorizontalAlignment(JLabel.CENTER); title.setOpaque(true); JPanel upper = new JPanel(); // Add listeners. access.addActionListener(new AccessListener()); beta.addActionListener(new BetaListener()); collection_extra.addKeyListener(new ChangeListener(collection_extra_data, collection_extra, "collectionextra")); collection_name.addKeyListener(new ChangeListener(collection_name_data, collection_name, "collectionname")); collection_name.addKeyListener(new CollectionTitleListener()); icon_collection.addKeyListener(new ChangeListener(icon_collection_data, icon_collection, "iconcollection")); // Layout instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); upper.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); upper.setLayout(new BorderLayout()); upper.add(title, BorderLayout.NORTH); upper.add(new JScrollPane(instructions), BorderLayout.CENTER); collection_name_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); collection_name_language.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); collection_name_inner_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); collection_name_inner_pane.setLayout(new BorderLayout()); collection_name_inner_pane.add(collection_name); collection_name_pane.setLayout(new BorderLayout()); collection_name_pane.add(collection_name_label, BorderLayout.WEST); collection_name_pane.add(collection_name_inner_pane, BorderLayout.CENTER); collection_name_pane.add(collection_name_language, BorderLayout.EAST); icon_collection_label.setBorder(BorderFactory.createEmptyBorder(0,0,0,5)); icon_collection_language.setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); icon_collection_inner_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0)); icon_collection_inner_pane.setLayout(new BorderLayout()); icon_collection_inner_pane.add(icon_collection, BorderLayout.CENTER); icon_collection_pane.setLayout(new BorderLayout()); icon_collection_pane.add(icon_collection_label, BorderLayout.WEST); icon_collection_pane.add(icon_collection_inner_pane, BorderLayout.CENTER); icon_collection_pane.add(icon_collection_language, BorderLayout.EAST); access_pane.add(access); beta_pane.add(beta); lower.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); lower.setLayout(new GridLayout(6,1)); lower.add(manager.creator.getControls()); lower.add(manager.maintainer.getControls()); lower.add(access_pane); lower.add(beta_pane); lower.add(collection_name_pane); lower.add(icon_collection_pane); collection_extra_inner_pane.setLayout(new BorderLayout()); collection_extra_inner_pane.add(collection_extra_label, BorderLayout.WEST); collection_extra_inner_pane.add(collection_extra_language, BorderLayout.EAST); collection_extra.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); collection_extra_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); collection_extra_pane.setLayout(new BorderLayout()); collection_extra_pane.add(collection_extra_inner_pane, BorderLayout.NORTH); collection_extra_pane.add(new JScrollPane(collection_extra), BorderLayout.CENTER); central_pane.setLayout(new BorderLayout()); central_pane.add(lower, BorderLayout.NORTH); central_pane.add(collection_extra_pane, BorderLayout.CENTER); setLayout(new BorderLayout()); add(upper, BorderLayout.NORTH); add(central_pane, BorderLayout.CENTER); } /** Destructor. */ public void destroy() { } /** We override the updateUI method so that we can ensure we are scrolled to the top of the instructions box first. */ public void updateUI() { if(instructions != null) { instructions.setCaretPosition(0); } if(collection_extra != null) { collection_extra.setCaretPosition(0); } super.updateUI(); } /** Detect when the collection access changes, and update the configuration as necessary. * @see org.greenstone.gatherer.cdm.CollectionDesignManager */ private class AccessListener implements ActionListener { public void actionPerformed(ActionEvent event) { manager.public_col = access.isSelected(); } } /** Detect when the collection state changes and update the configuration as necessary. * @see org.greenstone.gatherer.cdm.CollectionDesignManager */ private class BetaListener implements ActionListener { public void actionPerformed(ActionEvent event) { manager.beta = beta.isSelected(); } } /** This class listens for any changes its registered controls, including enter actions and keys typed, and updates the fields stored in CollectionDesignManager as necessary. */ private class ChangeListener extends KeyAdapter { /** The collection metadata to alter. */ private CollectionMeta metadata = null; /** The control we are watching for changes. */ private JTextComponent component = null; /** The name of the metadata to monitor. */ private String name = null; /** Construstor. * @param metadata The CollectionMeta to alter. * @param component The JTextComponent we are monitoring for changes. * @param name The name of the metadata as a String. */ public ChangeListener(CollectionMeta metadata, JTextComponent component, String name) { this.component = component; this.metadata = metadata; if(metadata != null) { this.name = metadata.getName().toString(); } else { this.name = name; } } /** Any extension of a KeyAdapter may override this method so we can be informed when a key has been released (ie after the character etc has been added). In this case we want to update the appropriate metadata, however there are three distinct cases to consider:
 1. A simple update of the value field is all that is necessary,
 2. The metadata exists but it currently has no language, so we must set the language to default and update the value, or
 3. The metadata doesn't exist so we'll create a new one with the specified name, default language and new value.
This final case is highly unlikely but still possible. * @param event An Event containing information about the key release. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.CollectionDesignManager * @see org.greenstone.gatherer.cdm.CollectionMeta * @see org.greenstone.gatherer.cdm.CollectionMetaManager * @see org.greenstone.gatherer.cdm.Language * @see org.greenstone.gatherer.cdm.LanguageManager * @see org.greenstone.gatherer.collection.CollectionManager */ public void keyReleased(KeyEvent event) { if(metadata != null) { Language language = metadata.getLanguage(); if(language == null) { metadata.update(metadata.getName(), manager.languages.getDefaultLanguage(), component.getText()); } else { metadata.update(metadata.getName(), metadata.getLanguage(), component.getText()); } } else { metadata = new CollectionMeta(manager, name, manager.languages.getDefaultLanguage(), component.getText()); manager.collectionmetadatum.addMetadata(metadata); } gatherer.c_man.configurationChanged(); } } /** Listens for changes to the collection name and updates the windows title bar appropriately and collection file. */ private class CollectionTitleListener extends KeyAdapter { /** Any extension of a KeyAdapter may override this method so we can be informed when a key has been released (ie after the character etc has been added). When such a change occurs update the main guis title bar to reflect the change and fix the collection name. * @param event An Event containing information about the key release. */ public void keyReleased(KeyEvent event) { String name = collection_name.getText(); if(name != null && name.length() > 0) { gatherer.g_man.setTitle(Utility.PROGRAM_NAME + ":\"" + name + "\""); gatherer.c_man.getCollection().setTitle(name); } } } } /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */ private class DesignTree extends JTree { private DesignNode root = null; /** Constructor. Automatically generates all of the nodes, in the order of CONTENTS. */ public DesignTree() { super(); root = new DesignNode("Root"); this.setModel(new DefaultTreeModel(root)); // Now add the design categories. for(int i = 0; i < CONTENTS.length; i++) { root.add(new DesignNode(CONTENTS[i])); } expandRow(0); setRootVisible(false); setSelectionRow(0); } /** Set the current view to the one specified. * @param type The name of the desired view as a String. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.GUI.DesignNode */ public void setSelectedView(String type) { type = gatherer.get(type); for(int i = 0; i < root.getChildCount(); i++) { DesignNode child = (DesignNode) root.getChildAt(i); if(child.toString().equals(type)) { TreePath path = new TreePath(child.getPath()); setSelectionPath(path); } } } } /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */ private class DesignNode extends DefaultMutableTreeNode { /** Constructor. * @param object The Object assigned to this node. */ public DesignNode(String object) { super(object); } /** Retrieve a textual representation of the object. * @return A String. */ public String toString() { return get((String)getUserObject()); } } /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */ private class TreeListener implements TreeSelectionListener { /** Called whenever the selection changes, we must update the view so it matches the node selected. * @param event A TreeSelectionEvent containing more information about the tree selection. * @see org.greenstone.gatherer.cdm.ClassifierManager * @see org.greenstone.gatherer.cdm.CollectionDesignManager * @see org.greenstone.gatherer.cdm.CollectionMetaManager * @see org.greenstone.gatherer.cdm.FormatManager * @see org.greenstone.gatherer.cdm.LanguageManager * @see org.greenstone.gatherer.cdm.MetadataSetManager * @see org.greenstone.gatherer.cdm.SubcollectionManager * @see org.greenstone.gatherer.cdm.PlugInManager */ public void valueChanged(TreeSelectionEvent event) { if(!tree.isSelectionEmpty()) { TreePath path = tree.getSelectionPath(); DesignNode node = (DesignNode)path.getLastPathComponent(); String type = (String)node.getUserObject(); // Assure user // Build and change panes. remove(view); view.hasFocus(); // Trigger pending information update events. if(type.equals("General")) { view = getControls(); } else if(type.equals("Indexes")) { view = manager.indexes.getControls(); } else if(type.equals("Subcollections")) { view = manager.subcollections.getControls(); } else if(type.equals("Languages")) { view = manager.languages.getControls(); } else if(type.equals("Plugins")) { view = manager.plugins.getControls(); } else if(type.equals("Classifiers")) { view = manager.classifiers.getControls(); } else if(type.equals("Formats")) { view = manager.formats.getControls(); } else if(type.equals("MetadataSets")) { view = manager.metadatasets.getControls(); } add(view, BorderLayout.CENTER); view.updateUI(); // Ready } } } }