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

Last change on this file since 12822 was 12822, checked in by kjdon, 18 years ago

changed from a BoxLayout to a GridLayout for build and import options - looks nicer

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