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

Last change on this file since 9139 was 9139, checked in by kjdon, 19 years ago

moved the ArgumentControl class out of OptionsPane. OptionsPane now has MyArgumentControl classs which extends ArgumentCOntrol, adding in the small amount of opitons pane specific functionality

  • Property svn:keywords set to Author Date Id Revision
File size: 21.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.Configuration;
47import org.greenstone.gatherer.DebugStream;
48import org.greenstone.gatherer.Dictionary;
49import org.greenstone.gatherer.Gatherer;
50import org.greenstone.gatherer.cdm.Argument;
51import org.greenstone.gatherer.collection.ScriptOptions;
52import org.greenstone.gatherer.collection.Collection;
53import org.greenstone.gatherer.collection.CollectionManager;
54import org.greenstone.gatherer.util.AppendLineOnlyFileDocument;
55import org.greenstone.gatherer.util.AppendLineOnlyFileDocumentOwner;
56
57/** 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>.
58 * @author John Thompson, Greenstone Digital Library, University of Waikato
59 * @version 2.2
60 */
61public class OptionsPane
62 extends JPanel
63 implements AppendLineOnlyFileDocumentOwner, MouseListener {
64
65 static final public char SUCCESSFUL = 's';
66 static final public char UNSUCCESSFUL = 'u';
67 static final public char CANCELLED = 'c';
68 static final public char UNKNOWN = 'x';
69
70 static private int BUILD = 0;
71 static private int IMPORT = 1;
72 static private int MINIMUM_ROWS = 11;
73
74 /** All process messages are written to this log text area. */
75 public JTextArea log_textarea = null;
76
77 private ArrayList current_controls;
78
79 /** The <strong>ScriptOptions</strong> data object contains all the option settings we wish to persist between Gatherer sessions (and thus is stored in <strong>Collection</strong>). */
80 private ScriptOptions build_options = null;
81 private ScriptOptions import_options = null;
82
83 private FileEntry file_entry = null;
84
85 /** the log pane - we only create it once now, not each time */
86 private JPanel log_pane = null;
87 /** the list of previous log messages */
88 private JList log_list = null;
89 private Vector writing_documents;
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(ScriptOptions import_options, ScriptOptions build_options) {
94 this.build_options = build_options;
95 this.import_options = import_options;
96 this.current_controls = new ArrayList();
97 this.writing_documents = new Vector();
98
99 // Have to do this here, not in display, as the message log view may not have been displayed yet.
100 log_textarea = new JTextArea();
101 log_textarea.setEditable(false);
102 }
103
104 /** This method creates the panel with all the build only options on it.
105 * @param pane a JPanel which already has previous arguments available on it, to allow for lower moes to concatenate all of the arguments together
106 * @return a JPanel which can be used to display all the build only options
107 * @see org.greenstone.gatherer.Configuration#EXPERT_MODE
108 * @see org.greenstone.gatherer.Configuration#getColor
109 * @see org.greenstone.gatherer.Configuration#getMode
110 * @see org.greenstone.gatherer.Gatherer#config
111 * @see org.greenstone.gatherer.cdm.Argument
112 * @see org.greenstone.gatherer.collection.BuildOptions#getBuildArgument
113 * @see org.greenstone.gatherer.collection.BuildOptions#getBuildArgumentCount
114 * @see org.greenstone.gatherer.collection.BuildOptions#getBuildValue
115 * @see org.greenstone.gatherer.collection.BuildOptions#getBuildValueEnabled
116 * @see org.greenstone.gatherer.gui.OptionsPane.MyArgumentControl
117 */
118 public JPanel buildBuild(JPanel pane) {
119 // Reset the arguments
120 if(pane == null) {
121 current_controls.clear();
122 }
123 ArrayList build_arguments = new ArrayList();
124 int current_mode = Configuration.getMode();
125 int total_build_argument_count = build_options.getArgumentCount();
126
127 for(int i = 0; i < total_build_argument_count; i++) {
128 // Retrieve the argument so we know how to format the control.
129 Argument argument = build_options.getArgument(i);
130 if(!argument.isHiddenGLI() && argument.getModeLevel() <= current_mode) {
131 // Now attempt to retrieve any existing value for this argument.
132 boolean enabled = build_options.getValueEnabled(argument.getName());
133 String value = build_options.getValue(argument.getName());
134 MyArgumentControl argument_control = new MyArgumentControl(BUILD, argument, enabled, value);
135 build_arguments.add(argument_control);
136 }
137 }
138 current_controls.addAll(build_arguments);
139 // Now that we know how many arguments there are we can build the pane to view them on. Modes lower than EXPERT can provide a previous pane on which to add the arguments.
140 if(pane == null || current_mode >= Configuration.EXPERT_MODE) {
141 pane = new JPanel();
142 pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
143 pane.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
144 int argument_count = build_arguments.size();
145 // If in any of the higher detail modes, and assuming we don't want super phat argument controls, we better ensure there is a minimum number or lines in the grid layout
146 if(current_mode >= Configuration.EXPERT_MODE) {
147 if(argument_count < MINIMUM_ROWS) {
148 argument_count = MINIMUM_ROWS;
149 }
150 pane.setLayout(new GridLayout(argument_count, 1, 5, 5));
151 }
152 // Otherwise we're just going to throw them on one after another and chuck it in a scroll pane anyway
153 else {
154 pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
155 }
156 }
157 for(int j = 0; j < build_arguments.size(); j++) {
158 pane.add((JComponent)build_arguments.get(j));
159 }
160 pane.addMouseListener(this);
161 build_arguments = null;
162 return pane;
163 }
164
165 /** This method creates the panel with all the import only options on it.
166 * @param pane a JPanel which already has previous arguments available on it, to allow for lower moes to concatenate all of the arguments together
167 * @return a JPanel which can be used to display all the build only options
168 * @see org.greenstone.gatherer.Configuration#EXPERT_MODE
169 * @see org.greenstone.gatherer.Configuration#getColor
170 * @see org.greenstone.gatherer.Configuration#getMode
171 * @see org.greenstone.gatherer.Gatherer#config
172 * @see org.greenstone.gatherer.cdm.Argument
173 * @see org.greenstone.gatherer.collection.BuildOptions#getImportArgument
174 * @see org.greenstone.gatherer.collection.BuildOptions#getImportArgumentCount
175 * @see org.greenstone.gatherer.collection.BuildOptions#getImportValue
176 * @see org.greenstone.gatherer.collection.BuildOptions#getImportValueEnabled
177 * @see org.greenstone.gatherer.gui.OptionsPane.ArgumentControl
178 */
179 public JPanel buildImport(JPanel pane) {
180 // Reset the arguments
181 if(pane == null) {
182 current_controls.clear();
183 }
184 ArrayList import_arguments = new ArrayList();
185 int current_mode = Configuration.getMode();
186 int total_import_argument_count = import_options.getArgumentCount();
187 for(int i = 0; i < total_import_argument_count; i++) {
188 // Retrieve the argument so we know how to format the control.
189 Argument argument = import_options.getArgument(i);
190 if(!argument.isHiddenGLI() && argument.getModeLevel() <= current_mode) {
191 // Now attempt to retrieve any existing value for this argument.
192 boolean enabled = import_options.getValueEnabled(argument.getName());
193 String value = import_options.getValue(argument.getName());
194 MyArgumentControl argument_control = new MyArgumentControl(IMPORT, argument, enabled, value);
195 import_arguments.add(argument_control);
196 }
197 }
198 current_controls.addAll(import_arguments);
199 // Now that we know how many arguments there are we can build the pane to view them on. Modes lower than EXPERT can provide a previous pane on which to add the arguments.
200 if(pane == null || current_mode >= Configuration.EXPERT_MODE) {
201 pane = new JPanel();
202 pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
203 pane.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
204 int argument_count = import_arguments.size();
205 // If in any of the higher detail modes, and assuming we don't want super phat argument controls, we better ensure there is a minimum number or lines in the grid layout
206 if(current_mode >= Configuration.EXPERT_MODE) {
207 if(argument_count < MINIMUM_ROWS) {
208 argument_count = MINIMUM_ROWS;
209 }
210 pane.setLayout(new GridLayout(argument_count, 1, 5, 5));
211 }
212 // Otherwise we're just going to throw them on one after another and chuck it in a scroll pane anyway
213 else {
214 pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
215 }
216 }
217 for(int j = 0; j < import_arguments.size(); j++) {
218 pane.add((JComponent)import_arguments.get(j));
219 }
220 pane.addMouseListener(this);
221 import_arguments = null;
222 return pane;
223 }
224
225 /** This method is used to build a panel based on the message log, which is nothing like any of the other panels.
226 * @return A <strong>JPanel</strong> containing a scrollable text area which represents the shell process message log.
227 */
228 public JPanel buildLog() {
229 // we now save the log pane
230 if (log_pane == null) {
231 log_pane = new JPanel(new BorderLayout());
232
233 // Build a list of the log files available, ordering by last modified. Log files are like build_log.date.txt
234 DefaultListModel contents = new DefaultListModel();
235 File log_directory = new File(Gatherer.c_man.getCollectionLogDirectoryPath());
236 File children[] = log_directory.listFiles();
237 for(int i = 0; children != null && i < children.length; i++) {
238 String filename = children[i].getName();
239 if(filename.startsWith("build_log.") && filename.endsWith(".txt") ) {
240 String datestamp = filename.substring(filename.indexOf(".") + 1, filename.lastIndexOf(".")).toLowerCase();
241 if(datestamp.indexOf("s") == -1 && datestamp.indexOf("u") == -1 && datestamp.indexOf("c") == -1 && datestamp.indexOf("x") == -1) {
242 FileEntry entry = new FileEntry(children[i].getName(), children[i].getAbsolutePath());
243 // We are about to insert it. But where.
244 boolean found = false;
245 for(int j = 0; !found && j < contents.size(); j++) {
246 FileEntry sibling = (FileEntry) contents.getElementAt(j);
247 int order = entry.compareTo(sibling);
248 if(order > 0) {
249 contents.insertElementAt(entry, j);
250 found = true;
251 }
252 }
253 if(!found) {
254 contents.addElement(entry);
255 }
256 }
257 }
258 }
259
260 log_list = new JList(contents);
261 log_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
262 log_list.setLayoutOrientation(JList.VERTICAL);
263 log_list.setPreferredSize(new Dimension(600, 100));
264 log_list.setVisibleRowCount(3);
265 log_list.addListSelectionListener(new LogListListener());
266
267 JLabel log_history_label = new JLabel();
268 Dictionary.registerText(log_history_label, "OptionsPane.LogHistory");
269 JPanel log_history_pane = new JPanel();
270 log_history_pane.setPreferredSize(new Dimension(600, 100));
271 log_history_pane.setLayout(new BorderLayout());
272 log_history_pane.add(log_history_label, BorderLayout.NORTH);
273 log_history_pane.add(new JScrollPane(log_list), BorderLayout.CENTER);
274
275 log_pane.add(new JScrollPane(log_textarea), BorderLayout.CENTER);
276 log_pane.add(log_history_pane, BorderLayout.SOUTH);
277 }
278 return log_pane;
279 }
280
281 public AppendLineOnlyFileDocument createNewLogDocument() {
282 long time = System.currentTimeMillis();
283 StringBuffer name = new StringBuffer();
284 name.append("build_log.");
285 name.append(time);
286 name.append(".txt");
287 // just in case there is no log directory
288 File file = new File(Gatherer.c_man.getCollectionLogDirectoryPath() + name.toString());
289 File parent_file = file.getParentFile();
290 parent_file.mkdirs();
291 parent_file = null;
292 // create the file entry and add it to the list at pos 0 - it will always be the newest one created
293 file_entry = new FileEntry(name.toString(), file.getAbsolutePath());
294 ((DefaultListModel)log_list.getModel()).add(0, file_entry);
295 log_list.setSelectedIndex(0);
296 // Finally retrieve and return the document associated with this file entry
297 return file_entry.getDocument();
298 }
299
300
301 /** Attempts to discover the latest document count.
302 * @return An <strong>int</strong> detailing the number of documents in this collection.
303 */
304 public int getDocumentCount() {
305 if(Gatherer.c_man.ready()) {
306 int count = Gatherer.c_man.getCollection().getDocumentCount();
307 if(count != 0) {
308 return count;
309 }
310 }
311 return 1;
312 }
313
314 /** 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. */
315 public void remove(AppendLineOnlyFileDocument document) {
316 writing_documents.remove(document);
317 }
318
319 public void resetFileEntry() {
320 if(file_entry != null) {
321 file_entry.reset();
322 }
323 }
324
325 /** Given a panel containing ArgumentControls, update the values associated with them. */
326 public void update(JPanel panel) {
327 if(panel == log_pane) {
328 return;
329 }
330
331 for(int i = 0; i < panel.getComponentCount(); i++) {
332 Component component = panel.getComponent(i);
333 if(component instanceof MyArgumentControl) {
334 ((MyArgumentControl)component).update();
335 }
336 }
337 }
338
339 /** Implementation side-effect
340 * @param e a MouseEvent
341 */
342 public void mouseClicked(MouseEvent e) {}
343
344 /** Implementation side-effect
345 * @param e a MouseEvent
346 */
347 public void mouseEntered(MouseEvent e) {}
348
349 /** Implemented to ensure that, by the time the mouse pointer leaves the current build options screen, ang changes to the value the JSpinners have been commited
350 * @param e a MouseEvent
351 */
352 public void mouseExited(MouseEvent e) {
353 // Loop through the controls, and if the current control is a JSpinner, commit its current editing
354 for(int i = 0; i < current_controls.size(); i++) {
355 MyArgumentControl control = (MyArgumentControl) current_controls.get(i);
356 JComponent value_control = control.getValueControl();
357 if(value_control instanceof JSpinner) {
358 try {
359 ((JSpinner)value_control).commitEdit();
360 }
361 catch(Exception exception) {
362 DebugStream.println("Exception in OptionsPane.mouseExited() - unexpected");
363 DebugStream.printStackTrace(exception);
364 }
365 }
366 value_control = null;
367 control = null;
368 }
369 }
370
371 /** Implementation side-effect
372 * @param e a MouseEvent
373 */
374 public void mousePressed(MouseEvent e) {}
375
376 /** Implementation side-effect
377 * @param e a MouseEvent
378 */
379 public void mouseReleased(MouseEvent e) {}
380
381 private class MyArgumentControl
382 extends ArgumentControl {
383 private int type;
384
385 public MyArgumentControl(int type, Argument argument, boolean enable, String value) {
386 super(argument, enable, value);
387 this.type = type;
388 }
389
390 /** Update the values stored in the collection so as to rememebr the current state of this argument. */
391 public void update() {
392 String name = argument.getName();
393 boolean enable = true;
394 if(enabled != null) {
395 enable = enabled.isSelected();
396 }
397 String value = null;
398 if(value_control == null) {
399 // Flag value, nothing to do.
400 }
401 else if(value_control instanceof JTextField) {
402 value = ((JTextField)value_control).getText();
403 }
404 else if(value_control instanceof JSpinner) {
405 value = ((JSpinner)value_control).getValue().toString();
406 }
407 else if(value_control instanceof JComboBox) {
408 value = (((JComboBox)value_control).getSelectedItem()).toString();
409 }
410 // If this argument was a flag, but is now disabled, remove from the build options altogether
411 if(!enable && value == null) {
412 if(type == BUILD) {
413 build_options.removeValue(name);
414 }
415 else {
416 import_options.removeValue(name);
417 }
418 }
419 // Otherwise update the argument value
420 else {
421 if(type == BUILD) {
422 build_options.setValue(name, enable, value);
423 }
424 else {
425 import_options.setValue(name, enable, value);
426 }
427 }
428 }
429 }
430
431 /** Holds a File which has a particular naming convention build_log.date.txt also keeps a Date corresponding to the date in its name*/
432 private class FileEntry {
433
434 private AppendLineOnlyFileDocument current_document;
435 private Date date;
436 private long last_modified;
437 private String display;
438 private String filename;
439 private String filepath;
440
441 public FileEntry(String filename, String filepath) {
442 this.date = null;
443 this.display = null;
444 this.filename = filename;
445 this.filepath = filepath;
446 this.last_modified = 0L;
447 }
448
449 /** returns 0 if the dates are the same, -ve number if the current FileEntry is earlier than the fe FileEntry ...*/
450 public int compareTo(FileEntry file_entry) {
451 Date our_date = getDate();
452 Date other_date = file_entry.getDate();
453 return our_date.compareTo(other_date);
454 }
455
456 public Date getDate() {
457 if(date == null) {
458 // Need to exclude first '.'
459 int first_index = filename.indexOf(".") + 1;
460 // Need to exclude the last '.'
461 int last_index = filename.lastIndexOf(".");
462 if(first_index > 0 && last_index > 0 && first_index < last_index) {
463 String date_string = filename.substring(first_index, last_index);
464 date = new Date(Long.parseLong(date_string));
465 }
466 else {
467 date = new Date(); // Current date
468 }
469 }
470 return date;
471 }
472
473 public AppendLineOnlyFileDocument getDocument() {
474 if(current_document == null) {
475 current_document = new AppendLineOnlyFileDocument(filepath);
476 }
477 return current_document;
478 }
479
480 public void reset() {
481 display = null;
482 }
483
484 /** we only want the date out of the file name, not the whole path */
485 public String toString() {
486 File file = new File(filename);
487 if(display == null) {
488 last_modified = file.lastModified();
489 StringBuffer d = new StringBuffer();
490 Date date = getDate();
491 d.append(date.toString());
492 char success = UNKNOWN;
493 File the_file = new File(filepath);
494 if(the_file.exists()) {
495 try {
496 FileInputStream in = new FileInputStream(the_file);
497 success = (char) in.read();
498 in.close();
499 in = null;
500 }
501 catch(Exception error) {
502 ///ystem.err.println("Log '" + filepath + "' not found!");
503 ///atherer.printStackTrace(error);
504 }
505 }
506 the_file = null;
507 switch (success) {
508 case SUCCESSFUL:
509 d.append(Dictionary.get("OptionsPane.Successful"));
510 break;
511 case UNSUCCESSFUL:
512 d.append(Dictionary.get("OptionsPane.Unsuccessful"));
513 break;
514 case CANCELLED:
515 d.append(Dictionary.get("OptionsPane.Cancelled"));
516 break;
517 default:
518 d.append(Dictionary.get("OptionsPane.Unknown"));
519 }
520 display = d.toString();
521 }
522 return display;
523 }
524 }
525
526 /** a ListSelectionListener that triggers the load of a newly selected log */
527 private class LogListListener implements ListSelectionListener {
528
529 public void valueChanged(ListSelectionEvent e) {
530 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
531 ///ystem.err.println("Log change detected.");
532 JList source = (JList)e.getSource();
533 file_entry = (FileEntry) source.getSelectedValue();
534 // First we determine if the old log has been completely written to file
535 Document document = log_textarea.getDocument();
536 ///ystem.err.println(" * current document: " + document);
537 ///ystem.err.println(" * new document: " + file_entry.getDocument());
538 // If we are dealing with the same document don't do anything.
539 if(document != file_entry.getDocument()) {
540 if(document instanceof AppendLineOnlyFileDocument) {
541 AppendLineOnlyFileDocument append_line_only_file_document = (AppendLineOnlyFileDocument) document;
542 if(append_line_only_file_document.isStillWriting()) {
543 ///ystem.err.println("Current log is still active... finishing.");
544 writing_documents.add(append_line_only_file_document); // We have to maintain a reference until they are all done.
545 append_line_only_file_document.setOwner(OptionsPane.this);
546 append_line_only_file_document.setExit();
547 }
548 else {
549 ///ystem.err.println("Current log is complete. Nothing to do.");
550 }
551 }
552 // Load the new log
553 log_textarea.setDocument(file_entry.getDocument());
554 }
555 }
556 }
557 }
558
559}
Note: See TracBrowser for help on using the repository browser.