source: trunk/gli/src/org/greenstone/gatherer/gui/OptionsPane.java@ 6087

Last change on this file since 6087 was 6087, checked in by jmt12, 20 years ago

Extended arguments so they could be given a minimum and maximum range. Then if they are INTEGER type controls, the spinner control generated will honour any range information. Range information is supplied via the perl -xml type mechanism, using the tag form <Range>0,3</Range>

  • Property svn:keywords set to Author Date Id Revision
File size: 22.4 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.gui;
38
39import java.awt.*;
40import java.awt.event.*;
41import java.io.*;
42import java.util.*;
43import javax.swing.*;
44import javax.swing.event.*;
45import javax.swing.text.*;
46import org.greenstone.gatherer.Dictionary;
47import org.greenstone.gatherer.Gatherer;
48import org.greenstone.gatherer.cdm.Argument;
49import org.greenstone.gatherer.checklist.CheckList;
50import org.greenstone.gatherer.collection.BuildOptions;
51import org.greenstone.gatherer.collection.Collection;
52import org.greenstone.gatherer.collection.CollectionManager;
53import org.greenstone.gatherer.msm.ElementWrapper;
54import org.greenstone.gatherer.util.AppendLineOnlyFileDocument;
55import org.greenstone.gatherer.util.AppendLineOnlyFileDocumentOwner;
56import org.greenstone.gatherer.util.Utility;
57
58/** This class serves as the data holder for all subclasses of option panes, such as Import options or All options. It also contains methods for creating each of the option lines as they would appear in the subpane. Futhermore it has a method for considering all the arguments and generating a <strong>String[]</strong> to allow you to pass them to the <strong>GShell</strong>.
59 * @author John Thompson, Greenstone Digital Library, University of Waikato
60 * @version 2.2
61 */
62public class OptionsPane
63 extends JPanel
64 implements AppendLineOnlyFileDocumentOwner {
65
66 static final public char SUCCESSFUL = 's';
67 static final public char UNSUCCESSFUL = 'u';
68 static final public char CANCELLED = 'c';
69 static final public char UNKNOWN = 'x';
70
71 /** All process messages are written to this log text area. */
72 public JTextArea log_textarea = null;
73
74 /** The <strong>BuildOptions</strong> data object contains all the option settings we wish to persist between Gatherer sessions (and thus is stored in <strong>Collection</strong>). */
75 private BuildOptions build_options = null;
76
77 private FileEntry file_entry = null;
78 /** the log pane - we only create it once now, not each time */
79 private JPanel log_pane = null;
80 /** the list of previous log messages */
81 private JList log_list = null;
82 private Vector writing_documents;
83
84 static private int BUILD = 0;
85 static private int IMPORT = 1;
86 static private Dimension LABEL_SIZE = new Dimension(180, 25);
87 static private Dimension ROW_SIZE = new Dimension(610, 25);
88 static private Dimension SPINNER_SIZE = new Dimension(100, 25);
89 static private String DESCRIPTION_SEP = " + ";
90
91
92 /** The default constructor creates the few session length options, but either retrieves the rest from the current collection, or creates a default set of options. */
93 public OptionsPane(BuildOptions build_options) {
94 this.build_options = build_options;
95 this.writing_documents = new Vector();
96
97 // Have to do this here, not in display, as the message log view may not have been displayed yet.
98 log_textarea = new JTextArea();
99 log_textarea.setEditable(false);
100 }
101
102 /** This method creates the panel with all the build only options on it.
103 * @return A <strong>JPanel</strong> which can be used to display all the build only options.
104 */
105 public JPanel buildBuild() {
106 JPanel pane = new JPanel();
107 pane.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
108 pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
109 int build_argument_count = build_options.getBuildArgumentCount();
110 pane.setLayout(new GridLayout(build_argument_count, 1, 5, 5));
111 for(int i = 0; i < build_argument_count; i++) {
112 // Retrieve the argument so we know how to format the control.
113 Argument argument = build_options.getBuildArgument(i);
114 // Now attempt to retrieve any existing value for this argument.
115 boolean enabled = build_options.getBuildValueEnabled(argument.getName());
116 String value = build_options.getBuildValue(argument.getName());
117 ArgumentControl argument_control = new ArgumentControl(BUILD, argument, enabled, value);
118 pane.add(argument_control);
119 }
120 return pane;
121 }
122 /** This method creates the panel with all the import only options on it.
123 * @return A <strong>JPanel</strong> which can be used to display all the import only options.
124 */
125 public JPanel buildImport() {
126 JPanel pane = new JPanel();
127 pane.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
128 pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
129 int import_argument_count = build_options.getImportArgumentCount();
130 pane.setLayout(new GridLayout(import_argument_count, 1, 5, 5));
131 for(int i = 0; i < import_argument_count; i++) {
132 // Retrieve the argument so we know how to format the control.
133 Argument argument = build_options.getImportArgument(i);
134 // Now attempt to retrieve any existing value for this argument.
135 boolean enabled = build_options.getImportValueEnabled(argument.getName());
136 String value = build_options.getImportValue(argument.getName());
137 ArgumentControl argument_control = new ArgumentControl(IMPORT, argument, enabled, value);
138 pane.add(argument_control);
139 }
140 return pane;
141 }
142 /** This method is used to build a panel based on the message log, which is nothing like any of the other panels.
143 * @return A <strong>JPanel</strong> containing a scrollable text area which represents the shell process message log.
144 */
145 public JPanel buildLog() {
146 // we now save the log pane
147 if (log_pane == null) {
148 log_pane = new JPanel(new BorderLayout());
149
150 // Build a list of the log files available, ordering by last modified. Log files are like build_log.date.txt
151 DefaultListModel contents = new DefaultListModel();
152 File log_directory = new File(Gatherer.c_man.getCollectionLog());
153 File children[] = log_directory.listFiles();
154 for(int i = 0; children != null && i < children.length; i++) {
155 if(children[i].getName().startsWith("build_log") && children[i].getName().endsWith(".txt") ) {
156 FileEntry entry = new FileEntry(children[i].getName(), children[i].getAbsolutePath());
157 // We are about to insert it. But where.
158 boolean found = false;
159 for(int j = 0; !found && j < contents.size(); j++) {
160 FileEntry sibling = (FileEntry) contents.getElementAt(j);
161 int order = entry.compareTo(sibling);
162 if(order > 0) {
163 contents.insertElementAt(entry, j);
164 found = true;
165 }
166 }
167 if(!found) {
168 contents.addElement(entry);
169 }
170 }
171 }
172
173 log_list = new JList(contents);
174 log_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
175 log_list.setLayoutOrientation(JList.VERTICAL);
176 log_list.setPreferredSize(new Dimension(600, 100));
177 log_list.setVisibleRowCount(3);
178 log_list.addListSelectionListener(new LogListListener());
179
180 JLabel log_history_label = new JLabel();
181 Dictionary.registerText(log_history_label, "OptionsPane.LogHistory");
182 JPanel log_history_pane = new JPanel();
183 log_history_pane.setPreferredSize(new Dimension(600, 100));
184 log_history_pane.setLayout(new BorderLayout());
185 log_history_pane.add(log_history_label, BorderLayout.NORTH);
186 log_history_pane.add(new JScrollPane(log_list), BorderLayout.CENTER);
187
188 log_pane.add(new JScrollPane(log_textarea), BorderLayout.CENTER);
189 log_pane.add(log_history_pane, BorderLayout.SOUTH);
190 }
191 return log_pane;
192 }
193
194 public AppendLineOnlyFileDocument createNewLogDocument() {
195 long time = System.currentTimeMillis();
196 StringBuffer name = new StringBuffer();
197 name.append("build_log.");
198 name.append(time);
199 name.append(".txt");
200 // just in case there is no log directory
201 File file = new File(Gatherer.c_man.getCollectionLog() + name.toString());
202 File parent_file = file.getParentFile();
203 parent_file.mkdirs();
204 parent_file = null;
205 // create the file entry and add it to the list at pos 0 - it will always be the newest one created
206 file_entry = new FileEntry(name.toString(), file.getAbsolutePath());
207 ((DefaultListModel)log_list.getModel()).add(0, file_entry);
208 log_list.setSelectedIndex(0);
209 // Finally retrieve and return the document associated with this file entry
210 return file_entry.getDocument();
211 }
212
213
214 /** Attempts to discover the latest document count.
215 * @return An <strong>int</strong> detailing the number of documents in this collection.
216 */
217 public int getDocumentCount() {
218 if(Gatherer.c_man.ready()) {
219 int count = Gatherer.c_man.getCollection().getDocumentCount();
220 if(count != 0) {
221 return count;
222 }
223 }
224 return 1;
225 }
226
227 /** Called by our magic log documents after they have finished writing themselves to file, whereapon it is no longer necessary to hold a reference to them. */
228 public void remove(AppendLineOnlyFileDocument document) {
229 writing_documents.remove(document);
230 }
231
232 public void resetFileEntry() {
233 if(file_entry != null) {
234 file_entry.reset();
235 }
236 }
237
238 /** Given a panel containing ArgumentControls, update the values associated with them. */
239 public void update(JPanel panel) {
240 if(panel == log_pane) {
241 return;
242 }
243
244 for(int i = 0; i < panel.getComponentCount(); i++) {
245 Component component = panel.getComponent(i);
246 if(component instanceof ArgumentControl) {
247 ((ArgumentControl)component).update();
248 }
249 }
250 }
251
252 private class ArgumentControl
253 extends JPanel {
254 private Argument argument;
255 private int type;
256 private JComponent value_control;
257 private JCheckBox enabled;
258 public ArgumentControl(int type, Argument argument, boolean enable, String value) {
259 super();
260 this.argument = argument;
261 this.type = type;
262 String tooltip = Utility.formatHTMLWidth("<html>" + argument.getDescription() + "</html>", 60);
263 // Because of the dynamic order of component creation/connection/layout, we can't really follow that pattern here.
264 setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
265 setBorder(BorderFactory.createEmptyBorder(2,0,2,0));
266 setLayout(new BorderLayout());
267 setPreferredSize(ROW_SIZE);
268
269 // Try to determine what the value_controls value should be.
270 if(value == null) {
271 value = argument.getDefaultValue();
272 }
273 // Create a correct value control based on the argument provided.
274 switch(argument.getType()) {
275 case Argument.ENUM:
276 JComboBox combobox = new JComboBox();
277 combobox.setEnabled(enable);
278 combobox.setToolTipText(tooltip);
279 // Set enabled
280 if(enable) {
281 combobox.setBackground(Color.white);
282 }
283 else {
284 combobox.setBackground(Color.lightGray);
285 }
286 // Build an option model, wrapping each entry of the list table.
287 HashMap arg_list = argument.getOptions();
288 Iterator it = arg_list.keySet().iterator();
289 while(it.hasNext()) {
290 combobox.addItem((String) it.next());
291 }
292 // Connect this up first, so that if a value is selected the tooltip updates accordingly.
293 combobox.addActionListener(new ToolTipUpdater(arg_list));
294 if(value != null) {
295 // Set the selected string. However since they are all strings we had best iterate ourselves.
296 for(int i = 0; i < combobox.getItemCount(); i++) {
297 if(combobox.getItemAt(i).toString().equals(value)) {
298 combobox.setSelectedIndex(i);
299 }
300 }
301 }
302 // Layout
303 add(combobox, BorderLayout.CENTER);
304 // And remember
305 value_control = combobox;
306 break;
307 case Argument.FLAG:
308 // Only need the check box.
309 value_control = null;
310 break;
311 case Argument.INTEGER:
312 // Build a spinner
313 JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, argument.getMinimum(), argument.getMaximum(), 1));
314 try {
315 int default_value = Integer.parseInt(argument.getDefaultValue());
316 spinner.setValue(new Integer(default_value));
317 }
318 catch (Exception exception) {
319 }
320 spinner.setEnabled(enable);
321 spinner.setPreferredSize(SPINNER_SIZE);
322 spinner.setToolTipText(tooltip);
323 // Set enabled
324 JComponent c = spinner.getEditor();
325 if ( c instanceof JSpinner.DefaultEditor ) {
326 JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) c;
327 JFormattedTextField field = editor.getTextField();
328 field.setEditable(enable);
329 if(enable) {
330 field.setBackground(Color.white);
331 }
332 else {
333 field.setBackground(Color.lightGray);
334 }
335 }
336 // If there was an original value, set it.
337 if(value != null) {
338 try {
339 spinner.setValue(new Integer(value));
340 }
341 catch (Exception error) {
342 }
343 }
344 // Layout
345 add(new JLabel(), BorderLayout.CENTER);
346 add(spinner, BorderLayout.EAST);
347 // And remember it
348 value_control = spinner;
349 break;
350 case Argument.STRING:
351 // Use a standard text field
352 JTextField textfield = new JTextField();
353 textfield.setEnabled(enable);
354 textfield.setToolTipText(tooltip);
355 // Set enabled
356 if(enable) {
357 textfield.setBackground(Color.white);
358 }
359 else {
360 textfield.setBackground(Color.lightGray);
361 }
362 // If there was an original value, set it.
363 if(value != null) {
364 textfield.setText(value);
365 }
366 // Layout
367 add(textfield, BorderLayout.CENTER);
368 // And remember it
369 value_control = textfield;
370 break;
371 }
372
373 // If the argument is required, then you don't get a choice of whether it is enabled.
374 if(argument.isRequired()) {
375 JLabel label = new JLabel(argument.getName());
376 label.setOpaque(false);
377 label.setPreferredSize(LABEL_SIZE);
378 label.setToolTipText(tooltip);
379 add(label, BorderLayout.WEST);
380 }
381 else {
382 enabled = new JCheckBox(argument.getName(), enable);
383 enabled.setOpaque(false);
384 enabled.setPreferredSize(LABEL_SIZE);
385 enabled.setToolTipText(tooltip);
386 // Connect
387 enabled.addActionListener(new EnabledListener(value_control));
388 // Layout
389 add(enabled, BorderLayout.WEST);
390 }
391 }
392
393 /** Update the values stored in the collection so as to rememebr the current state of this argument. */
394 public void update() {
395 String name = argument.getName();
396 boolean enable = true;
397 if(enabled != null) {
398 enable = enabled.isSelected();
399 }
400 String value = null;
401 if(value_control == null) {
402 // Flag value, nothing to do.
403 }
404 else if(value_control instanceof JTextField) {
405 value = ((JTextField)value_control).getText();
406 }
407 else if(value_control instanceof JSpinner) {
408 value = ((JSpinner)value_control).getValue().toString();
409 }
410 else if(value_control instanceof JComboBox) {
411 value = (String) ((JComboBox)value_control).getSelectedItem();
412 }
413 // If this argument was a flag, but is now disabled, remove from the build options altogether
414 if(!enable && value == null) {
415 if(type == BUILD) {
416 build_options.removeBuildValue(name);
417 }
418 else {
419 build_options.removeImportValue(name);
420 }
421 }
422 // Otherwise update the argument value
423 else {
424 if(type == BUILD) {
425 build_options.setBuildValue(name, enable, value);
426 }
427 else {
428 build_options.setImportValue(name, enable, value);
429 }
430 }
431 }
432 }
433
434 /** Listens for actions apon the enable checkbox, and if detected enables or diables control appropriately. */
435 private class EnabledListener
436 implements ActionListener {
437 /** An editor component, such as a JComboBox or JTextField, that might have its enabled state changed by this listener. */
438 private JComponent target = null;
439 /** Constructor. */
440 public EnabledListener(JComponent target) {
441 this.target = target;
442 }
443 /** 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.
444 * @param event An <strong>ActionEvent</strong> containing information about the click.
445 */
446 public void actionPerformed(ActionEvent event) {
447 JCheckBox source = (JCheckBox)event.getSource();
448 if(target != null) {
449 if(source.isSelected()) {
450 target.setBackground(Color.white);
451 target.setEnabled(true);
452 }
453 else {
454 target.setBackground(Color.lightGray);
455 target.setEnabled(false);
456 }
457 // Special case of stupid JSpinners who don't let their backgrounds change properly.
458 if(target instanceof JSpinner) {
459 JSpinner spinner = (JSpinner) target;
460 JComponent c = spinner.getEditor();
461 if ( c instanceof JSpinner.DefaultEditor ) {
462 JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) c;
463 JFormattedTextField field = editor.getTextField();
464 field.setEditable(source.isSelected());
465 if(source.isSelected()) {
466 field.setBackground(Color.white);
467 }
468 else {
469 field.setBackground(Color.lightGray);
470 }
471 }
472 }
473 }
474 }
475 }
476
477 /** Holds a File which has a particular naming convention build_log.date.txt also keeps a Date corresponding to the date in its name*/
478 private class FileEntry {
479
480 private AppendLineOnlyFileDocument current_document;
481 private Date date;
482 private long last_modified;
483 private String display;
484 private String filename;
485 private String filepath;
486
487 public FileEntry(String filename, String filepath) {
488 this.date = null;
489 this.display = null;
490 this.filename = filename;
491 this.filepath = filepath;
492 this.last_modified = 0L;
493 }
494
495 /** returns 0 if the dates are the same, -ve number if the current FileEntry is earlier than the fe FileEntry ...*/
496 public int compareTo(FileEntry file_entry) {
497 Date our_date = getDate();
498 Date other_date = file_entry.getDate();
499 return our_date.compareTo(other_date);
500 }
501
502 public Date getDate() {
503 if(date == null) {
504 // Need to exclude first '.'
505 int first_index = filename.indexOf(".") + 1;
506 // Need to exclude the last '.'
507 int last_index = filename.lastIndexOf(".");
508 if(first_index > 0 && last_index > 0 && first_index < last_index) {
509 String date_string = filename.substring(first_index, last_index);
510 date = new Date(Long.parseLong(date_string));
511 }
512 else {
513 date = new Date(); // Current date
514 }
515 }
516 return date;
517 }
518
519 public AppendLineOnlyFileDocument getDocument() {
520 if(current_document == null) {
521 current_document = new AppendLineOnlyFileDocument(filepath);
522 }
523 return current_document;
524 }
525
526 public void reset() {
527 display = null;
528 }
529
530 /** we only want the date out of the file name, not the whole path */
531 public String toString() {
532 File file = new File(filename);
533 if(display == null) {
534 last_modified = file.lastModified();
535 StringBuffer d = new StringBuffer();
536 Date date = getDate();
537 d.append(date.toString());
538 char success = UNKNOWN;
539 File the_file = new File(filepath);
540 if(the_file.exists()) {
541 try {
542 FileInputStream in = new FileInputStream(the_file);
543 success = (char) in.read();
544 in.close();
545 in = null;
546 }
547 catch(Exception error) {
548 ///ystem.err.println("Log '" + filepath + "' not found!");
549 ///atherer.printStackTrace(error);
550 }
551 }
552 the_file = null;
553 switch (success) {
554 case SUCCESSFUL:
555 d.append(Dictionary.get("OptionsPane.Successful"));
556 break;
557 case UNSUCCESSFUL:
558 d.append(Dictionary.get("OptionsPane.Unsuccessful"));
559 break;
560 case CANCELLED:
561 d.append(Dictionary.get("OptionsPane.Cancelled"));
562 break;
563 default:
564 d.append(Dictionary.get("OptionsPane.Unknown"));
565 }
566 display = d.toString();
567 }
568 return display;
569 }
570 }
571
572 /** a ListSelectionListener that triggers the load of a newly selected log */
573 private class LogListListener implements ListSelectionListener {
574
575 public void valueChanged(ListSelectionEvent e) {
576 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
577 JList source = (JList)e.getSource();
578 file_entry = (FileEntry) source.getSelectedValue();
579 // First we determine if the old log has been completely written to file
580 Document document = log_textarea.getDocument();
581 if(document instanceof AppendLineOnlyFileDocument) {
582 AppendLineOnlyFileDocument append_line_only_file_document = (AppendLineOnlyFileDocument) document;
583 if(append_line_only_file_document.isStillWriting()) {
584 writing_documents.add(append_line_only_file_document); // We have to maintain a reference until they are all done.
585 append_line_only_file_document.setOwner(OptionsPane.this);
586 append_line_only_file_document.setExit();
587 }
588 }
589 // Load the new log
590 log_textarea.setDocument(file_entry.getDocument());
591 }
592 }
593 }
594
595 /** Listener that sets the tooltip associated to a combobox to the tooltip relevant to the selected item. */
596 private class ToolTipUpdater
597 implements ActionListener {
598 private HashMap arg_list;
599 public ToolTipUpdater(HashMap arg_list) {
600 this.arg_list = arg_list;
601 }
602 /** 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.
603 * @param event An <strong>ActionEvent</strong> containing information about the action that fired this call.
604 */
605 public void actionPerformed(ActionEvent event) {
606 JComboBox source = (JComboBox)event.getSource();
607 String key = (String) source.getSelectedItem();
608 if(arg_list != null) {
609 String description = (String) arg_list.get(key);
610 if(description != null) {
611 description = Utility.formatHTMLWidth(DESCRIPTION_SEP + description, 60);
612 String original = source.getToolTipText();
613 if(original == null) {
614 original = "";
615 }
616 // Remove any existing extra description.
617 if(original.indexOf(DESCRIPTION_SEP) != -1) {
618 original = original.substring(0, original.indexOf(DESCRIPTION_SEP));
619 }
620 source.setToolTipText(original + description);
621 }
622 }
623 }
624 }
625}
Note: See TracBrowser for help on using the repository browser.