source: gli/branches/rtl-gli/src/org/greenstone/gatherer/gui/OptionsPane.java@ 18297

Last change on this file since 18297 was 18297, checked in by kjdon, 15 years ago

interface updated to display right to left for rtl languages. This code is thanks to Amin Hejazi. It seems to be only partially complete. Amin was working with Greenstone 3 so might have missed some panels

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