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

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

Changed text handling to use Dictionary.get rather than Dictionary.setText or Dictionary.registerBoth etc. also removed mnemonics cos they suck for other languages

  • Property svn:keywords set to Author Date Id Revision
File size: 20.9 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(Dictionary.get("OptionsPane.LogHistory"));
268 JPanel log_history_pane = new JPanel();
269 log_history_pane.setPreferredSize(new Dimension(600, 100));
270 log_history_pane.setLayout(new BorderLayout());
271 log_history_pane.add(log_history_label, BorderLayout.NORTH);
272 log_history_pane.add(new JScrollPane(log_list), BorderLayout.CENTER);
273
274 log_pane.add(new JScrollPane(log_textarea), BorderLayout.CENTER);
275 log_pane.add(log_history_pane, BorderLayout.SOUTH);
276 }
277 return log_pane;
278 }
279
280 public AppendLineOnlyFileDocument createNewLogDocument() {
281 long time = System.currentTimeMillis();
282 StringBuffer name = new StringBuffer();
283 name.append("build_log.");
284 name.append(time);
285 name.append(".txt");
286 // just in case there is no log directory
287 File file = new File(Gatherer.c_man.getCollectionLogDirectoryPath() + name.toString());
288 File parent_file = file.getParentFile();
289 parent_file.mkdirs();
290 parent_file = null;
291 // create the file entry and add it to the list at pos 0 - it will always be the newest one created
292 file_entry = new FileEntry(name.toString(), file.getAbsolutePath());
293 ((DefaultListModel)log_list.getModel()).add(0, file_entry);
294 log_list.setSelectedIndex(0);
295 // Finally retrieve and return the document associated with this file entry
296 return file_entry.getDocument();
297 }
298
299
300 /** Attempts to discover the latest document count.
301 * @return An <strong>int</strong> detailing the number of documents in this collection.
302 */
303 public int getDocumentCount() {
304 if(Gatherer.c_man.ready()) {
305 int count = Gatherer.c_man.getCollection().getDocumentCount();
306 if(count != 0) {
307 return count;
308 }
309 }
310 return 1;
311 }
312
313 /** 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. */
314 public void remove(AppendLineOnlyFileDocument document) {
315 writing_documents.remove(document);
316 }
317
318 public void resetFileEntry() {
319 if(file_entry != null) {
320 file_entry.reset();
321 }
322 }
323
324 /** Given a panel containing ArgumentControls, update the values associated with them. */
325 public void update(JPanel panel) {
326 if(panel == log_pane) {
327 return;
328 }
329
330 for(int i = 0; i < panel.getComponentCount(); i++) {
331 Component component = panel.getComponent(i);
332 if(component instanceof MyArgumentControl) {
333 ((MyArgumentControl)component).update();
334 }
335 }
336 }
337
338 /** Implementation side-effect
339 * @param e a MouseEvent
340 */
341 public void mouseClicked(MouseEvent e) {}
342
343 /** Implementation side-effect
344 * @param e a MouseEvent
345 */
346 public void mouseEntered(MouseEvent e) {}
347
348 /** 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
349 * @param e a MouseEvent
350 */
351 public void mouseExited(MouseEvent e) {
352 // Loop through the controls, and if the current control is a JSpinner, commit its current editing
353 for(int i = 0; i < current_controls.size(); i++) {
354 MyArgumentControl control = (MyArgumentControl) current_controls.get(i);
355 JComponent value_control = control.getValueControl();
356 if(value_control instanceof JSpinner) {
357 try {
358 ((JSpinner)value_control).commitEdit();
359 }
360 catch(Exception exception) {
361 DebugStream.println("Exception in OptionsPane.mouseExited() - unexpected");
362 DebugStream.printStackTrace(exception);
363 }
364 }
365 value_control = null;
366 control = null;
367 }
368 }
369
370 /** Implementation side-effect
371 * @param e a MouseEvent
372 */
373 public void mousePressed(MouseEvent e) {}
374
375 /** Implementation side-effect
376 * @param e a MouseEvent
377 */
378 public void mouseReleased(MouseEvent e) {}
379
380 private class MyArgumentControl
381 extends ArgumentControl {
382 private int type;
383
384 public MyArgumentControl(int type, Argument argument, boolean enable, String value) {
385 super(argument, enable, value);
386 this.type = type;
387 }
388
389 /** Update the values stored in the collection so as to remember the current state of this argument. */
390 public void update() {
391 String name = getArgumentName();
392 boolean enable = isEnabled();
393 String value = getValue();
394 // If this argument was a flag, but is now disabled, remove from the build options altogether
395 if(!enable && value == null) {
396 if(type == BUILD) {
397 build_options.removeValue(name);
398 }
399 else {
400 import_options.removeValue(name);
401 }
402 }
403 // Otherwise update the argument value
404 else {
405 if(type == BUILD) {
406 build_options.setValue(name, enable, value);
407 }
408 else {
409 import_options.setValue(name, enable, value);
410 }
411 }
412 }
413 }
414
415 /** Holds a File which has a particular naming convention build_log.date.txt also keeps a Date corresponding to the date in its name*/
416 private class FileEntry {
417
418 private AppendLineOnlyFileDocument current_document;
419 private Date date;
420 private long last_modified;
421 private String display;
422 private String filename;
423 private String filepath;
424
425 public FileEntry(String filename, String filepath) {
426 this.date = null;
427 this.display = null;
428 this.filename = filename;
429 this.filepath = filepath;
430 this.last_modified = 0L;
431 }
432
433 /** returns 0 if the dates are the same, -ve number if the current FileEntry is earlier than the fe FileEntry ...*/
434 public int compareTo(FileEntry file_entry) {
435 Date our_date = getDate();
436 Date other_date = file_entry.getDate();
437 return our_date.compareTo(other_date);
438 }
439
440 public Date getDate() {
441 if(date == null) {
442 // Need to exclude first '.'
443 int first_index = filename.indexOf(".") + 1;
444 // Need to exclude the last '.'
445 int last_index = filename.lastIndexOf(".");
446 if(first_index > 0 && last_index > 0 && first_index < last_index) {
447 String date_string = filename.substring(first_index, last_index);
448 date = new Date(Long.parseLong(date_string));
449 }
450 else {
451 date = new Date(); // Current date
452 }
453 }
454 return date;
455 }
456
457 public AppendLineOnlyFileDocument getDocument() {
458 if(current_document == null) {
459 current_document = new AppendLineOnlyFileDocument(filepath);
460 }
461 return current_document;
462 }
463
464 public void reset() {
465 display = null;
466 }
467
468 /** we only want the date out of the file name, not the whole path */
469 public String toString() {
470 File file = new File(filename);
471 if(display == null) {
472 last_modified = file.lastModified();
473 StringBuffer d = new StringBuffer();
474 Date date = getDate();
475 d.append(date.toString());
476 char success = UNKNOWN;
477 File the_file = new File(filepath);
478 if(the_file.exists()) {
479 try {
480 FileInputStream in = new FileInputStream(the_file);
481 success = (char) in.read();
482 in.close();
483 in = null;
484 }
485 catch(Exception error) {
486 ///ystem.err.println("Log '" + filepath + "' not found!");
487 ///atherer.printStackTrace(error);
488 }
489 }
490 the_file = null;
491 switch (success) {
492 case SUCCESSFUL:
493 d.append(Dictionary.get("OptionsPane.Successful"));
494 break;
495 case UNSUCCESSFUL:
496 d.append(Dictionary.get("OptionsPane.Unsuccessful"));
497 break;
498 case CANCELLED:
499 d.append(Dictionary.get("OptionsPane.Cancelled"));
500 break;
501 default:
502 d.append(Dictionary.get("OptionsPane.Unknown"));
503 }
504 display = d.toString();
505 }
506 return display;
507 }
508 }
509
510 /** a ListSelectionListener that triggers the load of a newly selected log */
511 private class LogListListener implements ListSelectionListener {
512
513 public void valueChanged(ListSelectionEvent e) {
514 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
515 ///ystem.err.println("Log change detected.");
516 JList source = (JList)e.getSource();
517 file_entry = (FileEntry) source.getSelectedValue();
518 // First we determine if the old log has been completely written to file
519 Document document = log_textarea.getDocument();
520 ///ystem.err.println(" * current document: " + document);
521 ///ystem.err.println(" * new document: " + file_entry.getDocument());
522 // If we are dealing with the same document don't do anything.
523 if(document != file_entry.getDocument()) {
524 if(document instanceof AppendLineOnlyFileDocument) {
525 AppendLineOnlyFileDocument append_line_only_file_document = (AppendLineOnlyFileDocument) document;
526 if(append_line_only_file_document.isStillWriting()) {
527 ///ystem.err.println("Current log is still active... finishing.");
528 writing_documents.add(append_line_only_file_document); // We have to maintain a reference until they are all done.
529 append_line_only_file_document.setOwner(OptionsPane.this);
530 append_line_only_file_document.setExit();
531 }
532 else {
533 ///ystem.err.println("Current log is complete. Nothing to do.");
534 }
535 }
536 // Load the new log
537 log_textarea.setDocument(file_entry.getDocument());
538 }
539 }
540 }
541 }
542
543}
Note: See TracBrowser for help on using the repository browser.