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

Last change on this file since 11060 was 11060, checked in by mdewsnip, 18 years ago

Fixed a problem with the CreatePane being blank with Java 1.5.0 and the GTK look and feel.

  • Property svn:keywords set to Author Date Id Revision
File size: 21.0 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 remember the current state of this argument. */
391 public void update() {
392 String name = getArgumentName();
393 boolean enable = isEnabled();
394 String value = getValue();
395 // If this argument was a flag, but is now disabled, remove from the build options altogether
396 if(!enable && value == null) {
397 if(type == BUILD) {
398 build_options.removeValue(name);
399 }
400 else {
401 import_options.removeValue(name);
402 }
403 }
404 // Otherwise update the argument value
405 else {
406 if(type == BUILD) {
407 build_options.setValue(name, enable, value);
408 }
409 else {
410 import_options.setValue(name, enable, value);
411 }
412 }
413 }
414 }
415
416 /** Holds a File which has a particular naming convention build_log.date.txt also keeps a Date corresponding to the date in its name*/
417 private class FileEntry {
418
419 private AppendLineOnlyFileDocument current_document;
420 private Date date;
421 private long last_modified;
422 private String display;
423 private String filename;
424 private String filepath;
425
426 public FileEntry(String filename, String filepath) {
427 this.date = null;
428 this.display = null;
429 this.filename = filename;
430 this.filepath = filepath;
431 this.last_modified = 0L;
432 }
433
434 /** returns 0 if the dates are the same, -ve number if the current FileEntry is earlier than the fe FileEntry ...*/
435 public int compareTo(FileEntry file_entry) {
436 Date our_date = getDate();
437 Date other_date = file_entry.getDate();
438 return our_date.compareTo(other_date);
439 }
440
441 public Date getDate() {
442 if(date == null) {
443 // Need to exclude first '.'
444 int first_index = filename.indexOf(".") + 1;
445 // Need to exclude the last '.'
446 int last_index = filename.lastIndexOf(".");
447 if(first_index > 0 && last_index > 0 && first_index < last_index) {
448 String date_string = filename.substring(first_index, last_index);
449 date = new Date(Long.parseLong(date_string));
450 }
451 else {
452 date = new Date(); // Current date
453 }
454 }
455 return date;
456 }
457
458 public AppendLineOnlyFileDocument getDocument() {
459 if(current_document == null) {
460 current_document = new AppendLineOnlyFileDocument(filepath);
461 }
462 return current_document;
463 }
464
465 public void reset() {
466 display = null;
467 }
468
469 /** we only want the date out of the file name, not the whole path */
470 public String toString() {
471 File file = new File(filename);
472 if(display == null) {
473 last_modified = file.lastModified();
474 StringBuffer d = new StringBuffer();
475 Date date = getDate();
476 d.append(date.toString());
477 char success = UNKNOWN;
478 File the_file = new File(filepath);
479 if(the_file.exists()) {
480 try {
481 FileInputStream in = new FileInputStream(the_file);
482 success = (char) in.read();
483 in.close();
484 in = null;
485 }
486 catch(Exception error) {
487 ///ystem.err.println("Log '" + filepath + "' not found!");
488 ///atherer.printStackTrace(error);
489 }
490 }
491 the_file = null;
492 switch (success) {
493 case SUCCESSFUL:
494 d.append(Dictionary.get("OptionsPane.Successful"));
495 break;
496 case UNSUCCESSFUL:
497 d.append(Dictionary.get("OptionsPane.Unsuccessful"));
498 break;
499 case CANCELLED:
500 d.append(Dictionary.get("OptionsPane.Cancelled"));
501 break;
502 default:
503 d.append(Dictionary.get("OptionsPane.Unknown"));
504 }
505 display = d.toString();
506 }
507 return display;
508 }
509 }
510
511 /** a ListSelectionListener that triggers the load of a newly selected log */
512 private class LogListListener implements ListSelectionListener {
513
514 public void valueChanged(ListSelectionEvent e) {
515 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
516 ///ystem.err.println("Log change detected.");
517 JList source = (JList)e.getSource();
518 file_entry = (FileEntry) source.getSelectedValue();
519 // First we determine if the old log has been completely written to file
520 Document document = log_textarea.getDocument();
521 ///ystem.err.println(" * current document: " + document);
522 ///ystem.err.println(" * new document: " + file_entry.getDocument());
523 // If we are dealing with the same document don't do anything.
524 if(document != file_entry.getDocument()) {
525 if(document instanceof AppendLineOnlyFileDocument) {
526 AppendLineOnlyFileDocument append_line_only_file_document = (AppendLineOnlyFileDocument) document;
527 if(append_line_only_file_document.isStillWriting()) {
528 ///ystem.err.println("Current log is still active... finishing.");
529 writing_documents.add(append_line_only_file_document); // We have to maintain a reference until they are all done.
530 append_line_only_file_document.setOwner(OptionsPane.this);
531 append_line_only_file_document.setExit();
532 }
533 else {
534 ///ystem.err.println("Current log is complete. Nothing to do.");
535 }
536 }
537 // Load the new log
538 log_textarea.setDocument(file_entry.getDocument());
539 }
540 }
541 }
542 }
543
544}
Note: See TracBrowser for help on using the repository browser.