package org.greenstone.gatherer.gui; /** *######################################################################### * * 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. *######################################################################## */ import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.collection.BuildOptions; import org.greenstone.gatherer.collection.Collection; import org.greenstone.gatherer.gui.OptionsPane; import org.greenstone.gatherer.shell.GBasicProgressMonitor; import org.greenstone.gatherer.shell.GBuildProgressMonitor; import org.greenstone.gatherer.shell.GImportProgressMonitor; import org.greenstone.gatherer.shell.GShell; import org.greenstone.gatherer.shell.GShellEvent; import org.greenstone.gatherer.shell.GShellListener; import org.greenstone.gatherer.shell.GShellProgressMonitor; /** This class provides a GUI view showing some statistics on your current collection, and options for building it. As the collection is built this initial view is replaced with one showing progress bars and a message log of the creation process. This log can be later accessed via the options tree located in the center of the initial pane. This class is also responsible for creating the GShellProgressMonitors that are then attatched to external shell processes, and calling the methods in the CollectionManager for actually importing and building the collection.

*
* * * * * * * * * * * * * * * * * * * * *
* The (i)mport and/or (b)uild options supported:
ScriptAssociated ArgumentControlComments
b-allclassificationsJCheckBox 
i/b-archivedirJTextField 
b-builddirJTextField 
i/b-debugJCheckBox 
i/b-collectdirJTextField 
b-create_imagesJCheckBox 
i-groupsizeJSpinner  
i-gzipJCheckBox 
i-importdirJTextField 
b-indexGCheckList 
i/b-keepoldJCheckBoxsanity check removeold
i/b-maxdocsJSpinner 
b-modeJComboBox"all", "compress_text", "infodb", "build_index"
b-no_textJCheckBox 
i-OIDtypeJComboBox"hash","incremental"
i/b-outJTextField 
i-removeoldJCheckBoxsanity check keepold
i-sortmetaJComboBox 
i/b-verbosityJSpinner1, 3
* @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ public class CreatePane extends JPanel implements GShellListener { /** Determines the current view that should be shown for this pane. */ public boolean processing = false; /** The options pane generates all the various option sheet configuations. */ public OptionsPane options_pane = null; /** A card layout is used to store the separate configuration and progress panes. */ private CardLayout card_layout = null; /** Stores a reference to the current collection. */ private Collection previous_collection = null; /** This monitor tracks the build processes progress. */ private GShellProgressMonitor build_monitor = null; /** This monitor tracks the copy processes progress. */ private GShellProgressMonitor copy_monitor = null; /** This monitor tracks the import processes progress. */ private GShellProgressMonitor import_monitor = null; /** The button for begining the building processes. */ private JButton build_button = null; /** The button for stopping the building processes. */ private JButton cancel_button = null; /** The label displaying the number of documents in this collection. */ private JLabel document_count = null; /** The label alongside the build progress bar gets some special treatment so... */ private JLabel progress_build_label = null; /** The label alongside the copy progress bar gets some special treatment so... */ private JLabel progress_copy_label = null; /** The label alongside the import progress bar gets some special treatment so... */ private JLabel progress_import_label = null; /** The pane which contains the controls for configuration. */ private JPanel control_pane = null; /** The pane which has the card layout as its manager. */ private JPanel main_pane = null; /** The pane which contains the progress information. */ private JPanel progress_pane = null; /** the pane on the right-hand side - shows the requested options view */ private JPanel right = null; /** This pane is the one that is updated to show the requested options view. */ //private JPanel target_pane = null; /** The scrolling pane the log is inside. */ private JScrollPane scroll_log = null; /** the scrolling pane the righthand side is inside - only used for import and build options. the log and log list are not inside a scrollpane */ private JScrollPane scroll_pane; /** The log for the current process. */ private JTextArea process_log = null; /** The message log for the entire session. */ private JTextArea message_log = new JTextArea(); /** A tree used to display the currently available option views. */ private OptionTree tree = null; /** An array used to pass arguments with dictionary calls. */ private String args[] = null; /** The size of the buttons at the bottom of the screen. */ static private Dimension BUTTON_SIZE = new Dimension(386, 50); /** The size of the labels on the progress pane. */ static private Dimension LABEL_SIZE = new Dimension(140, 25); /** An identifier for the control panel within the card layout. */ static private String CONTROL = "Control"; /** An identifier for the progress panel within the card layout. */ static private String PROGRESS = "Progress"; /** The constructor creates important helper classes, and initializes all the components. * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.gui.BuildOptions * @see org.greenstone.gatherer.gui.CreatePane.BuildButtonListener * @see org.greenstone.gatherer.gui.CreatePane.CancelButtonListener * @see org.greenstone.gatherer.gui.CreatePane.OptionTree * @see org.greenstone.gatherer.gui.OptionsPane * @see org.greenstone.gatherer.shell.GBasicProgressMonitor * @see org.greenstone.gatherer.shell.GBuildProgressMonitor * @see org.greenstone.gatherer.shell.GImportProgressMonitor * @see org.greenstone.gatherer.shell.GShellProgressMonitor */ public CreatePane() { //this.target_pane = new JPanel(new BorderLayout()); Collection collection = Gatherer.c_man.getCollection(); //if(collection != null) { // this.options_pane = new OptionsPane(message_log, collection.build_options); // } // Create components card_layout = new CardLayout(); // Control Pane control_pane = new JPanel(); args = new String[1]; args[0] = "0"; document_count = new JLabel(get("Document_Count", args)); tree = new OptionTree(); // Progress Pane progress_pane = new JPanel(); progress_copy_label = new JLabel(get("Copy_Progress")); progress_copy_label.setForeground(Color.black); progress_copy_label.setHorizontalAlignment(JLabel.LEFT); progress_copy_label.setPreferredSize(LABEL_SIZE); progress_copy_label.setMinimumSize(LABEL_SIZE); progress_copy_label.setSize(LABEL_SIZE); copy_monitor = new GBasicProgressMonitor(); Gatherer.c_man.registerCopyMonitor(copy_monitor); progress_import_label = new JLabel(get("Import_Progress")); progress_import_label.setForeground(Color.black); progress_import_label.setHorizontalAlignment(JLabel.LEFT); progress_import_label.setPreferredSize(LABEL_SIZE); progress_import_label.setMinimumSize(LABEL_SIZE); progress_import_label.setSize(LABEL_SIZE); import_monitor = new GImportProgressMonitor(); //GBasicProgressMonitor(); Gatherer.c_man.registerImportMonitor(import_monitor); progress_build_label = new JLabel(get("Build_Progress")); progress_build_label.setForeground(Color.black); progress_build_label.setHorizontalAlignment(JLabel.LEFT); progress_build_label.setPreferredSize(LABEL_SIZE); progress_build_label.setMinimumSize(LABEL_SIZE); progress_build_label.setSize(LABEL_SIZE); build_monitor = new GBuildProgressMonitor((GImportProgressMonitor)import_monitor); //GBasicProgressMonitor(); Gatherer.c_man.registerBuildMonitor(build_monitor); // Buttons. build_button = new JButton(get("Build_Collection")); build_button.addActionListener(new BuildButtonListener()); build_button.setPreferredSize(BUTTON_SIZE); cancel_button = new JButton(get("Cancel_Build")); cancel_button.addActionListener(new CancelButtonListener()); cancel_button.setEnabled(false); cancel_button.setPreferredSize(BUTTON_SIZE); } /** This method is invoked at any time there has been a significant change in the collection, such as saving, loading or creating. * @param ready A boolean indicating if a collection is currently available. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.gui.BuildOptions * @see org.greenstone.gatherer.gui.OptionsPane */ public void collectionChanged(boolean ready) { ///ystem.err.println("Collection changed... "); if(Gatherer.c_man != null && Gatherer.c_man.ready()) { if (!processing &&this.options_pane!=null) { // we only do this if we are not processing - if we are processing, then we haven't added the new entry yet, and it will be added by another method this.options_pane.resetLogList(); // saves any residual message } Collection current_collection = Gatherer.c_man.getCollection(); if (current_collection != previous_collection) { this.options_pane = new OptionsPane(message_log, current_collection.build_options); previous_collection = current_collection; } tree.valueChanged(null); } } /** This method is called to actually layout the components. * @see org.greenstone.gatherer.Configuration * @see org.greenstone.gatherer.Gatherer */ public void display() { // Build control_pane JPanel stats_area = new JPanel(); stats_area.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createTitledBorder(get("Statistics_Title")))); stats_area.setLayout(new GridLayout(1,1)); stats_area.add(document_count); JPanel left = new JPanel(); left.setBorder(BorderFactory.createEmptyBorder(0,5,5,5)); left.setLayout(new BorderLayout()); left.add(tree, BorderLayout.CENTER); right = new JPanel(); right.setBorder(BorderFactory.createEmptyBorder(0,0,5,5)); right.setLayout(new BorderLayout()); //scroll_pane = new JScrollPane(target_pane); //right.add(scroll_pane, BorderLayout.CENTER); JPanel options_area = new JPanel(); options_area.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createTitledBorder(get("Options_Title")))); options_area.setLayout(new BorderLayout()); options_area.add(left, BorderLayout.WEST); options_area.add(right, BorderLayout.CENTER); control_pane.setLayout(new BorderLayout()); control_pane.add(options_area, BorderLayout.CENTER); //control_pane.add(stats_area, BorderLayout.SOUTH); // Build progress_pane JPanel copy_pane = new JPanel(); copy_pane.setLayout(new BorderLayout()); copy_pane.add(progress_copy_label, BorderLayout.WEST); copy_pane.add(copy_monitor.getProgress(), BorderLayout.CENTER); JPanel import_pane = new JPanel(); import_pane.setLayout(new BorderLayout()); import_pane.add(progress_import_label, BorderLayout.WEST); import_pane.add(import_monitor.getProgress(), BorderLayout.CENTER); JPanel build_pane = new JPanel(); build_pane.setLayout(new BorderLayout()); build_pane.add(progress_build_label, BorderLayout.WEST); build_pane.add(build_monitor.getProgress(), BorderLayout.CENTER); JPanel bar_area = new JPanel(); bar_area.setBorder(BorderFactory.createEmptyBorder(10,10,5,10)); bar_area.setLayout(new GridLayout(2,2,10,5)); bar_area.add(import_pane); bar_area.add(build_pane); progress_pane.setBorder(BorderFactory.createEmptyBorder(20,20,20,20)); progress_pane.setLayout(new BorderLayout()); progress_pane.add(bar_area, BorderLayout.NORTH); // Main pane main_pane = new JPanel(card_layout); main_pane.add(control_pane, CONTROL); main_pane.add(progress_pane, PROGRESS); // Buttons JPanel button_pane = new JPanel(); button_pane.setBorder(BorderFactory.createEmptyBorder(5,10,10,10)); button_pane.setLayout(new GridLayout(1,2)); button_pane.add(build_button); button_pane.add(cancel_button); this.setLayout(new BorderLayout()); this.add(main_pane, BorderLayout.CENTER); this.add(button_pane, BorderLayout.SOUTH); } /** This method is called whenever the 'Create' tab is selected from the view bar. It allows this view to perform any preactions required prior to display. In this case this entails gathering up to date information about the status of the collection including number of documents etc. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.gui.CreatePane.OptionTree */ public void gainFocus() { if(!processing) { card_layout.show(main_pane, CONTROL); //args = new String[1]; //if(Gatherer.c_man.ready()) { // args[0] = String.valueOf(Gatherer.c_man.getCollection().getDocumentCount()); //} //else { //args[0] = "0"; //} //document_count.setText(get("Document_Count", args)); } // Refresh the set of controls. TreePath path = tree.getSelectionPath(); tree.clearSelection(); tree.setSelectionPath(path); } /** Method to acquire the progress monitor associated with builds. * @return The build GShellProgressMonitor. */ public GShellProgressMonitor getBuildProgress() { return build_monitor; } /** Method to acquire the progress monitor associated with copying. * @return The copying GShellProgressMonitor. */ public GShellProgressMonitor getCopyProgress() { return copy_monitor; } /** Method to acquire the progress monitor associated with import. * @return The import GShellProgressMonitor. */ public GShellProgressMonitor getImportProgress() { return import_monitor; } /** We are informed when this view pane loses focus so we can update build options. */ public void loseFocus() { tree.valueChanged(null); } /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell. * @param event A GShellEvent that contains, amoung other things, the message. */ public synchronized void message(GShellEvent event) { ///ystem.err.println("Recieved message: " + event.getMessage()); String text = event.getMessage(); message_log.append(text + "\n"); if(process_log != null) { process_log.append(text + "\n"); process_log.setCaretPosition(process_log.getCaretPosition() + text.length() + 1); //scroll_log.getVerticalScrollBar().setValue(process_log.getHeight() - process_log.getVisibleRect().height); } } /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used. * @param event A GShellEvent that contains details of the initial state of the GShell before task comencement. */ public synchronized void processBegun(GShellEvent event) { // We don't care. We'll get a more acurate start from the progress monitors. } /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task. * @param event A GShellEvent that contains details of the final state of the GShell after task completion. */ public synchronized void processComplete(GShellEvent event) { if(event.getType() == GShell.BUILD) { processing = false; cancel_button.setEnabled(false); build_button.setEnabled(true); int status = event.getStatus(); if (status == GShell.OK) { options_pane.addNewLog(OptionsPane.SUCCESSFUL); } else { options_pane.addNewLog(OptionsPane.UNSUCCESSFUL); } card_layout.show(main_pane, CONTROL); } } /** This method retrieves a phrase from the dictionary based apon the key. * @param key A String used as a reference to the correct phrase. * @return A phrase, matching the key, as a String. */ private String get(String key) { return get(key, null); } /** This method retrieves a phrase from the dictionary based apon the key and arguments. * @param key A String used as a reference to the correct phrase. * @param args A String[] whose contents are often substituted into the phrase before it is returned. * @return A phrase, matching the key and constructed using the arguments, as a String. * @see org.greenstone.gatherer.Dictionary * @see org.greenstone.gatherer.Gatherer */ private String get(String key, String args[]) { if(key.indexOf(".") == -1) { key = "CreatePane." + key; } return Gatherer.dictionary.get(key, args); } /** This class serves as the listener for actions on the build button. */ private class BuildButtonListener implements ActionListener { /** This method causes a call to be made to CollectionManager.importCollection(), which then imports and builds the collection as necessary. * @param event An ActionEvent who, thanks to the power of object oriented programming, we don't give two hoots about. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.gui.BuildOptions * @see org.greenstone.gatherer.shell.GShellProgressMonitor */ public void actionPerformed(ActionEvent event) { // First we force the build options to be updated if we haven't already. tree.valueChanged(null); // Now go about building. build_button.setEnabled(false); cancel_button.setEnabled(true); // Create a new process log, and place it. if(scroll_log != null) { progress_pane.remove(scroll_log); } process_log = new JTextArea(); scroll_log = new JScrollPane(process_log); progress_pane.add(scroll_log, BorderLayout.CENTER); // clear the log list and message_log options_pane.resetLogList(); // Change the view. processing = true; card_layout.show(main_pane, PROGRESS); // Reset the stop flag in all the process monitors, just incase. ((GBuildProgressMonitor)build_monitor).reset(); copy_monitor.setStop(false); import_monitor.setStop(false); // Set removeold automatically. if(Gatherer.c_man.ready() && !Gatherer.c_man.built()) { Gatherer.c_man.getCollection().build_options.setImportValue("removeold", true, null); } // Call CollectionManagers method to build collection. Gatherer.c_man.importCollection(); } } /** This class serves as the listener for actions on the cancel button. */ private class CancelButtonListener implements ActionListener { /** This method attempts to cancel the current GShell task. It does this by first telling CollectionManager not to carry out any further action. This it turn tells the GShell to break from the current job immediately, without waiting for the processEnded message, and then kills the thread in an attempt to stop the process. The results of such an action are debatable. * @param event An ActionEvent who, thanks to the power of object oriented programming, we don't give two hoots about. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.gui.BuildOptions * @see org.greenstone.gatherer.shell.GShellProgressMonitor */ public void actionPerformed(ActionEvent event) { build_button.setEnabled(true); cancel_button.setEnabled(false); processing = false; options_pane.addNewLog(OptionsPane.CANCELLED); card_layout.show(main_pane, CONTROL); // Set the stop flag in all the process monitor. build_monitor.setStop(true); copy_monitor.setStop(true); import_monitor.setStop(true); // Set removeold automatically. if(Gatherer.c_man.ready()) { // and it should be. Gatherer.c_man.getCollection().build_options.setImportValue("removeold", true, null); } // Remove the collection lock. Gatherer.g_man.lockCollection(false, false); } } /** The OptionTree is simply a tree structure that has a root node labelled "Options" and then has a child node for each available options screen. These screens are either combinations of the available import and build arguments, or a message log detailing the shell processes progress. */ private class OptionTree extends JTree implements TreeSelectionListener { /** The model behind the tree. */ private DefaultTreeModel model = null; /** The previous options view displayed, which is sometimes needed to refresh properly. */ private JPanel previous_pane = null; /** The node corresponding to the building options view. */ private OptionTreeNode building = null; /** The node corresponding to the importing options view. */ private OptionTreeNode importing = null; /** The node corresponding to the log view. */ private OptionTreeNode log = null; /** The root node of the options tree, which has no associated options view. */ private OptionTreeNode options = null; /** Constructor. * @see org.greenstone.gatherer.gui.CreatePane.OptionTreeNode */ public OptionTree() { super(); addTreeSelectionListener(this); getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); setRootVisible(false); setToggleClickCount(1); // Create tree. building = new OptionTreeNode(get("Build")); importing = new OptionTreeNode(get("Import")); log = new OptionTreeNode(get("Log")); options = new OptionTreeNode(get("Options")); model = new DefaultTreeModel(options); setModel(model); model.insertNodeInto(importing, options, 0); model.insertNodeInto(building, options, 1); model.insertNodeInto(log, options, 2); // Expand the root node expandPath(new TreePath(options)); } /** Any implementation of TreeSelectionListener must include this method to allow this listener to know when the selection has changed. Here we swap the options view depending on the selected OptionTreeNode. * @param event A TreeSelectionEvent which contains all the information garnered when the event occured. * @see org.greenstone.gatherer.gui.CreatePane.OptionTreeNode */ public void valueChanged(TreeSelectionEvent event) { TreePath path = null; OptionTreeNode node = null; //if(event != null) { //path = event.getPath(); path = getSelectionPath(); if(path != null) { node = (OptionTreeNode)path.getLastPathComponent(); } //} if(previous_pane != null) { //target_pane.remove(previous_pane); options_pane.update(previous_pane); if(scroll_pane != null) { right.remove(scroll_pane); } else { right.remove(previous_pane); } previous_pane = null; scroll_pane = null; } if(node != null && node.equals(building)) { previous_pane = options_pane.buildBuild(); scroll_pane = new JScrollPane(previous_pane); right.add(scroll_pane, BorderLayout.CENTER); //target_pane.add(previous_pane, BorderLayout.CENTER); } else if(node != null && node.equals(importing)) { previous_pane = options_pane.buildImport(); scroll_pane = new JScrollPane(previous_pane); right.add(scroll_pane, BorderLayout.CENTER); //target_pane.add(previous_pane, BorderLayout.CENTER); } else { previous_pane = options_pane.buildLog(); right.add(previous_pane, BorderLayout.CENTER); right.updateUI(); // we need to repaint the log pane, cos it hasn't changed since last time ///ystem.err.println("I've added the log back to the right pane again."); //target_pane.add(previous_pane, BorderLayout.CENTER); } //scroll_pane.setViewportView(previous_pane); previous_pane.validate(); } } /** The OptionTree is built from these nodes, each of which has several methods used in creating the option panes. */ private class OptionTreeNode extends DefaultMutableTreeNode implements Comparable { /** The text label given to this node in the tree. */ private String title = null; /** Constructor. * @param title The String which serves as this nodes title. */ public OptionTreeNode(String title) { super(); this.title = title; } /** This method compares two nodes for ordering. * @param obj The Object to compare to. * @return An int of one of the possible values -1, 0 or 1 indicating if this node is less than, equal to or greater than the target node respectively. */ public int compareTo(Object obj) { return title.compareTo(obj.toString()); } /** This method checks if two nodes are equivelent. * @param obj The Object to be tested against. * @return A boolean which is true if the objects are equal, false otherwise. */ public boolean equals(Object obj) { if(compareTo(obj) == 0) { return true; } return false; } /** Method to translate this node into a textual representation. * @return A String which in this case is the title. */ public String toString() { return title; } } }