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

Last change on this file since 5350 was 5350, checked in by mdewsnip, 21 years ago

Changed dictionary get()s to have the whole key.

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