source: main/trunk/gli/src/org/greenstone/gatherer/gui/CreatePane.java@ 31638

Last change on this file since 31638 was 31638, checked in by ak19, 7 years ago

Rewriting GShell.runLocal() to use SafeProcess instead of Process. This required overhaul of SafeProcess to correctly deal with interrupts as the build/import process launched in GShell can be cancelled from the CreatePane. SafeProcess now also keeps track of its internal process object and has static logging methods to simplify the changes required when swapping between the GS3 version and GS2 version of SafeProcess. May eventually call DebugStream methods from the log methods for GLI. Will add a DEBUG flag to allow verbose logging.

  • Property svn:keywords set to Author Date Id Revision
File size: 48.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 javax.swing.*;
43import javax.swing.event.*;
44import javax.swing.text.*;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.DebugStream;
47import org.greenstone.gatherer.Configuration;
48import org.greenstone.gatherer.Dictionary;
49import org.greenstone.gatherer.Gatherer;
50import org.greenstone.gatherer.cdm.BuildTypeManager;
51import org.greenstone.gatherer.cdm.CollectionDesignManager;
52import org.greenstone.gatherer.collection.Collection;
53import org.greenstone.gatherer.shell.GBuildProgressMonitor;
54import org.greenstone.gatherer.shell.GImportProgressMonitor;
55import org.greenstone.gatherer.shell.GScheduleProgressMonitor;
56import org.greenstone.gatherer.shell.GShell;
57import org.greenstone.gatherer.shell.GShellEvent;
58import org.greenstone.gatherer.shell.GShellListener;
59import org.greenstone.gatherer.shell.GShellProgressMonitor;
60import org.greenstone.gatherer.util.AppendLineOnlyFileDocument;
61import org.greenstone.gatherer.util.StaticStrings;
62import org.greenstone.gatherer.util.Utility;
63
64import org.greenstone.gatherer.collection.CollectionManager;
65import org.greenstone.gatherer.util.GS3ServerThread;
66
67/** This class provides a GUI view showing some statistics on your current collection, and options for building it. As the collection is built this initial view is replaced with one showing progress bars and a message log of the creation process. This log can be later accessed via the options tree located in the center of the initial pane. This class is also responsible for creating the GShellProgressMonitors that are then attatched to external shell processes, and calling the methods in the CollectionManager for actually importing and building the collection. <br><BR>
68 * @author John Thompson, Greenstone Digital Library, University of Waikato
69 * @version 2.3
70 */
71public class CreatePane
72 extends JPanel
73 implements GShellListener {
74
75 static private Dimension ARGUMENT_SIZE = new Dimension(800,90);
76 /** The threshold for when the simple view is replaced by the complex one. */
77 static private final int THRESHOLD = Configuration.EXPERT_MODE;
78 /** An identifier for the control panel within the card layout. */
79 static private String CONTROL = "Control";
80 /** An identifier for the progress panel within the card layout. */
81 static private String PROGRESS = "Progress";
82 /** An identifier for the simple panel within the card layout. */
83 static private String SIMPLE = "Simple";
84
85 /** Determines the current view that should be shown for this pane. */
86 public boolean processing = false;
87 /** The options pane generates all the various option sheet configuations. */
88 public OptionsPane options_pane = null;
89 private AppendLineOnlyFileDocument document;
90 /** A card layout is used to store the separate configuration and progress panes. */
91 private CardLayout card_layout = null;
92 /** Stores a reference to the current collection. */
93 private Collection previous_collection = null;
94 /** This monitor tracks the build processes progress. */
95 private GShellProgressMonitor build_monitor = null;
96 /** This monitor tracks the import processes progress. */
97 private GShellProgressMonitor import_monitor = null;
98 /** This monitor tracks the schedule processes progress. */
99 private GShellProgressMonitor schedule_monitor = null;
100 /** The button for begining the building processes. */
101 private JButton build_button = null;
102 /** The button for stopping the building processes. */
103 private JButton cancel_button = null;
104 /** The button for setting up scheduling */
105 private JButton schedule_button = null;
106 /** The button for viewing the collection. */
107 private JButton preview_button = null;
108 private JButton simple_build_button;
109 private JButton simple_cancel_button;
110 private JButton simple_preview_button;
111 /** The radio buttons for chnaging the build type (incremental/full) */
112 private JRadioButton full_build_radio_button;
113 private JRadioButton incremental_build_radio_button;
114 private JRadioButton sfull_build_radio_button;
115 private JRadioButton sincremental_build_radio_button;
116 /** The label displaying the number of documents in this collection. */
117 private JLabel document_count = null;
118 /** The label alongside the build progress bar gets some special treatment so... */
119 private JLabel progress_build_label = null;
120 /** The label alongside the import progress bar gets some special treatment so... */
121 private JLabel progress_import_label = null;
122 private JPanel bar_area;
123 /** The panel on which buttons are rendered on higher detail modes. */
124 private JPanel button_pane;
125 /** The pane which contains the controls for configuration. */
126 private JPanel control_pane = null;
127 /** The pane which has the card layout as its manager. */
128 private JPanel main_pane = null;
129 /** The pane which contains the progress information. */
130 private JPanel progress_pane = null;
131 /** The pane on the right-hand side - shows the requested options view */
132 private JPanel right = null;
133 /** The simple panel on which all of the available arguments are rendered. */
134 private JPanel sargument_configuration_panel;
135 /** The panel on which buttons are rendered on lower details modes. */
136 private JPanel sbutton_panel;
137 /** A simplified version for lower detail modes containing both the control and progress panes smooshed together. */
138 private JSplitPane simple_panel;
139 /** The lower part of the panel which is global so the log pane can be added and removed from it */
140 private JPanel slower_panel;
141 /** The inner panel of the simple pane which is global so that the bar_area can be added and removed from it. */
142 private JPanel sinner_panel;
143 /** The scrolling pane for the log. */
144 private JScrollPane log_scroll;
145 /** The scrolling pane the simple arguments are rendered within. */
146 private JScrollPane sargument_configuration_scroll;
147 /** the scrolling pane the righthand side is inside - only used for import and build options. the log and log list are not inside a scrollpane */
148 private JScrollPane scroll_pane;
149 /** The message log for the entire session. */
150 private JTextArea log_textarea;
151 /** A tree used to display the currently available option views. */
152 private OptionTree tree = null;
153 /** An array used to pass arguments with dictionary calls. */
154 private String args[] = null;
155 /** The homepage address of the current collection */
156 public String homepage = null;
157
158 /** Access to the GShell object that this CreatePane listens to events for.
159 * A handle to the GShell is needed order to interrupt any processes the GShell runs
160 * when the user cancels a build operation.
161 */
162 private GShell shell = null;
163
164 /** The constructor creates important helper classes, and initializes all the components.
165 * @see org.greenstone.gatherer.collection.CollectionManager
166 * @see org.greenstone.gatherer.gui.BuildOptions
167 * @see org.greenstone.gatherer.gui.CreatePane.BuildButtonListener
168 * @see org.greenstone.gatherer.gui.CreatePane.CancelButtonListener
169 * @see org.greenstone.gatherer.gui.CreatePane.OptionTree
170 * @see org.greenstone.gatherer.gui.OptionsPane
171 * @see org.greenstone.gatherer.shell.GBuildProgressMonitor
172 * @see org.greenstone.gatherer.shell.GImportProgressMonitor
173 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
174 */
175 public CreatePane() {
176 this.setComponentOrientation(Dictionary.getOrientation());
177 log_textarea = new JTextArea();
178 log_scroll = new JScrollPane(log_textarea);
179
180 // Create components
181 card_layout = new CardLayout();
182 // Control Pane
183 control_pane = new JPanel();
184 control_pane.setComponentOrientation(Dictionary.getOrientation());
185 tree = new OptionTree();
186 tree.setComponentOrientation(Dictionary.getOrientation());
187 button_pane = new JPanel();
188 button_pane.setComponentOrientation(Dictionary.getOrientation());
189
190 // Progress Pane
191 progress_pane = new JPanel(); // One owner of the bar component
192 progress_pane.setComponentOrientation(Dictionary.getOrientation());
193 bar_area = new JPanel(); // This component will be shared about
194 bar_area.setComponentOrientation(Dictionary.getOrientation());
195
196 progress_import_label = new JLabel(Dictionary.get("CreatePane.Import_Progress"));
197 progress_import_label.setComponentOrientation(Dictionary.getOrientation());
198
199 import_monitor = new GImportProgressMonitor();
200 Gatherer.c_man.registerImportMonitor(import_monitor);
201
202 progress_build_label = new JLabel(Dictionary.get("CreatePane.Build_Progress"));
203 progress_build_label.setComponentOrientation(Dictionary.getOrientation());
204
205 build_monitor = new GBuildProgressMonitor(import_monitor.getSharedProgress());
206 Gatherer.c_man.registerBuildMonitor(build_monitor);
207
208 //We only need a schedule monitor for text output. No use for a progress bar!
209
210 //progress_schedule_label = new JLabel(Dictionary.get("CreatePane.Schedule_Progress"));
211
212 schedule_monitor = new GScheduleProgressMonitor();
213 Gatherer.c_man.registerScheduleMonitor(schedule_monitor);
214
215 // And the simple panel
216 slower_panel = new JPanel();
217 sbutton_panel = new JPanel();
218 sinner_panel = new JPanel();
219
220 slower_panel.setComponentOrientation(Dictionary.getOrientation());
221 sbutton_panel.setComponentOrientation(Dictionary.getOrientation());
222 sinner_panel.setComponentOrientation(Dictionary.getOrientation());
223
224 //Radio buttons
225 incremental_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Minimal_Build"));
226 incremental_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
227 incremental_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Minimal_Build_Tooltip"));
228 full_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Full_Build"));
229 full_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
230 full_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Full_Build_Tooltip"));
231 sincremental_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Minimal_Build"));
232 sincremental_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
233 sincremental_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Minimal_Build_Tooltip"));
234 sfull_build_radio_button = new JRadioButton(Dictionary.get("CreatePane.Full_Build"));
235 sfull_build_radio_button.setComponentOrientation(Dictionary.getOrientation());
236 sfull_build_radio_button.setToolTipText(Dictionary.get("CreatePane.Full_Build_Tooltip"));
237
238 //keep them in sync
239 full_build_radio_button.addActionListener(
240 new ActionListener() { public void actionPerformed(ActionEvent e) {
241 sfull_build_radio_button.setSelected( full_build_radio_button.isSelected() );
242 }});
243 sfull_build_radio_button.addActionListener(
244 new ActionListener() { public void actionPerformed(ActionEvent e) {
245 full_build_radio_button.setSelected( sfull_build_radio_button.isSelected() );
246 }});
247 incremental_build_radio_button.addActionListener(
248 new ActionListener() { public void actionPerformed(ActionEvent e) {
249 sincremental_build_radio_button.setSelected( incremental_build_radio_button.isSelected() );
250 }});
251 sincremental_build_radio_button.addActionListener(
252 new ActionListener() { public void actionPerformed(ActionEvent e) {
253 incremental_build_radio_button.setSelected( sincremental_build_radio_button.isSelected() );
254 }});
255
256 // Buttons
257 BuildButtonListener bbl = new BuildButtonListener();
258 CancelButtonListener cbl = new CancelButtonListener();
259
260 build_button = new GLIButton(Dictionary.get("CreatePane.Build_Collection"), Dictionary.get("CreatePane.Build_Collection_Tooltip"));
261 build_button.addActionListener(bbl);
262
263 cancel_button = new GLIButton(Dictionary.get("CreatePane.Cancel_Build"), Dictionary.get("CreatePane.Cancel_Build_Tooltip"));
264 cancel_button.addActionListener(cbl);
265 cancel_button.setEnabled(false);
266
267 preview_button = new PreviewButton(Dictionary.get("CreatePane.Preview_Collection"), Dictionary.get("CreatePane.Preview_Collection_Tooltip"));
268 //preview_button.addActionListener(pbl);
269 if(Gatherer.c_man != null) {
270 preview_button.setEnabled(Gatherer.c_man.built());
271 }
272 else {
273 preview_button.setEnabled(false);
274 }
275
276 BuildSimpleButtonListener bsbl = new BuildSimpleButtonListener();
277 simple_build_button = new GLIButton(Dictionary.get("CreatePane.Build_Collection"), Dictionary.get("CreatePane.Build_Collection_Tooltip"));
278 simple_build_button.addActionListener(bsbl);
279
280 simple_cancel_button = new GLIButton(Dictionary.get("CreatePane.Cancel_Build"), Dictionary.get("CreatePane.Cancel_Build_Tooltip"));
281 simple_cancel_button.addActionListener(cbl);
282 simple_cancel_button.setEnabled(false);
283
284 simple_preview_button = new PreviewButton(Dictionary.get("CreatePane.Preview_Collection"), Dictionary.get("CreatePane.Preview_Collection_Tooltip"));
285 //simple_preview_button.addActionListener(pbl);
286 if(Gatherer.c_man != null) {
287 simple_preview_button.setEnabled(Gatherer.c_man.built());
288 }
289 else {
290 simple_preview_button.setEnabled(false);
291 }
292
293 bbl = null;
294 bsbl = null;
295 cbl = null;
296 //pbl = null;
297 }
298
299 public void setGShell(GShell shell) {
300 this.shell = shell;
301 }
302
303 public void destroy() {
304 if(document != null) {
305 document.destroy();
306 }
307 }
308
309 /** This method is called to actually layout the components.
310 * @see org.greenstone.gatherer.Configuration
311 * @see org.greenstone.gatherer.Gatherer
312 */
313 public void display() {
314
315 int current_mode = Configuration.getMode();
316
317 //Complete/incremental build options panel
318 ButtonGroup build_type_group = new ButtonGroup();
319 build_type_group.add(incremental_build_radio_button);
320 build_type_group.add(full_build_radio_button);
321 full_build_radio_button.setSelected(true);
322
323 ButtonGroup sbuild_type_group = new ButtonGroup();
324 sbuild_type_group.add(sincremental_build_radio_button);
325 sbuild_type_group.add(sfull_build_radio_button);
326 sfull_build_radio_button.setSelected(true);
327
328 JPanel build_type_pane = new JPanel(new GridLayout(2,1));
329 build_type_pane.setComponentOrientation(Dictionary.getOrientation());
330 build_type_pane.add(full_build_radio_button);
331 build_type_pane.add(incremental_build_radio_button);
332
333 JPanel sbuild_type_pane = new JPanel(new GridLayout(2,1));
334 sbuild_type_pane.setComponentOrientation(Dictionary.getOrientation());
335 sbuild_type_pane.add(sfull_build_radio_button);
336 sbuild_type_pane.add(sincremental_build_radio_button);
337
338
339 // Build control_pane
340 JPanel left = new JPanel();
341 left.setComponentOrientation(Dictionary.getOrientation());
342 left.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
343 left.setLayout(new BorderLayout());
344 left.add(tree, BorderLayout.CENTER);
345 left.add(build_type_pane, BorderLayout.SOUTH);
346 //left.add(full_build_radio_button, BorderLayout.SOUTH);
347
348 right = new JPanel();
349 right.setComponentOrientation(Dictionary.getOrientation());
350 right.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
351 right.setLayout(new BorderLayout());
352
353 JPanel options_area = new JPanel();
354 options_area.setComponentOrientation(Dictionary.getOrientation());
355 options_area.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createTitledBorder(Dictionary.get("CreatePane.Options_Title"))));
356 options_area.setLayout(new BorderLayout());
357 options_area.add(left, BorderLayout.LINE_START);
358 options_area.add(right, BorderLayout.CENTER);
359
360 button_pane = new JPanel();
361 button_pane.setComponentOrientation(Dictionary.getOrientation());
362 button_pane.setBorder(BorderFactory.createEmptyBorder(5,10,10,10));
363 button_pane.setLayout(new GridLayout(1,4));
364 button_pane.add(build_button);
365 button_pane.add(cancel_button);
366 button_pane.add(preview_button);
367
368 control_pane.setLayout(new BorderLayout());
369 control_pane.add(options_area, BorderLayout.CENTER);
370 control_pane.add(button_pane, BorderLayout.SOUTH);
371
372 // Build progress_pane
373 JPanel labels_pane = new JPanel();
374 labels_pane.setComponentOrientation(Dictionary.getOrientation());
375 labels_pane.setLayout(new GridLayout(2,1,0,5));
376 labels_pane.add(progress_import_label);
377 labels_pane.add(progress_build_label);
378
379 JPanel monitors_pane = new JPanel();
380 monitors_pane.setComponentOrientation(Dictionary.getOrientation());
381 monitors_pane.setLayout(new GridLayout(2,1,0,5));
382 monitors_pane.add(import_monitor.getProgress());
383 monitors_pane.add(build_monitor.getProgress());
384
385 bar_area.setBorder(BorderFactory.createEmptyBorder(10,10,5,10));
386 bar_area.setLayout(new BorderLayout(5,5));
387 bar_area.add(labels_pane, BorderLayout.LINE_START);
388 bar_area.add(monitors_pane, BorderLayout.CENTER);
389
390 progress_pane.setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
391 progress_pane.setLayout(new BorderLayout());
392 progress_pane.add(bar_area, BorderLayout.NORTH);
393 if(current_mode >= THRESHOLD) {
394 progress_pane.add(log_scroll, BorderLayout.CENTER);
395 }
396
397 // Simple panel
398 sbutton_panel.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
399 sbutton_panel.setLayout(new GridLayout(1,3));
400 sbutton_panel.add(simple_build_button);
401 sbutton_panel.add(simple_cancel_button);
402 sbutton_panel.add(simple_preview_button);
403
404 JPanel simple_bar_area = new JPanel(new GridLayout(2,1));
405 simple_bar_area.setComponentOrientation(Dictionary.getOrientation());
406 simple_bar_area.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
407 simple_bar_area.add(import_monitor.getSharedProgress());
408 simple_bar_area.add(sbutton_panel);
409
410 sinner_panel.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
411 sinner_panel.setLayout(new BorderLayout());
412 sinner_panel.add(sbuild_type_pane, BorderLayout.LINE_START);
413 //sinner_panel.add(full_build_radio_button, BorderLayout.WEST);
414 sinner_panel.add(simple_bar_area, BorderLayout.CENTER);
415
416 if(options_pane != null) {
417 sargument_configuration_panel = options_pane.buildImport(null);
418 sargument_configuration_panel = options_pane.buildBuild(sargument_configuration_panel);
419 }
420 else {
421 sargument_configuration_panel = new JPanel();
422 }
423 sargument_configuration_panel.setComponentOrientation(Dictionary.getOrientation());
424 sargument_configuration_scroll = new JScrollPane(sargument_configuration_panel);
425 sargument_configuration_scroll.setPreferredSize(ARGUMENT_SIZE);
426 sargument_configuration_scroll.setComponentOrientation(Dictionary.getOrientation());
427
428 slower_panel.setLayout(new BorderLayout());
429 slower_panel.add(sinner_panel, BorderLayout.NORTH);
430 if(current_mode < THRESHOLD) {
431 slower_panel.add(log_scroll, BorderLayout.CENTER);
432 }
433
434 simple_panel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, sargument_configuration_scroll, slower_panel);
435 simple_panel.setComponentOrientation(Dictionary.getOrientation());
436 // Main pane
437 main_pane = new JPanel(card_layout);
438 main_pane.setComponentOrientation(Dictionary.getOrientation());
439 if(current_mode < THRESHOLD) { // Simple mode - add first
440 main_pane.add(simple_panel, SIMPLE);
441 }
442 main_pane.add(control_pane, CONTROL);
443 main_pane.add(progress_pane, PROGRESS);
444 if(current_mode >= THRESHOLD) { // Expert mode - add last
445 main_pane.add(simple_panel, SIMPLE);
446 }
447
448 this.setLayout(new BorderLayout());
449 this.add(main_pane, BorderLayout.CENTER);
450 }
451
452 /** This method is called whenever the 'Create' tab is selected from the view bar. It allows this view to perform any preactions required prior to display. In this case this entails gathering up to date information about the status of the collection including number of documents etc.
453 * @see org.greenstone.gatherer.Gatherer
454 * @see org.greenstone.gatherer.collection.CollectionManager
455 * @see org.greenstone.gatherer.gui.CreatePane.OptionTree
456 */
457 public void gainFocus() {
458 if(Configuration.getMode() < THRESHOLD) {
459 card_layout.show(main_pane, SIMPLE);
460 }
461 else if(!processing) {
462 // Move the buttons to control
463 control_pane.add(button_pane, BorderLayout.SOUTH);
464 card_layout.show(main_pane, CONTROL);
465 }
466 else {
467 // Move the buttons to progress
468 progress_pane.add(button_pane, BorderLayout.SOUTH);
469 card_layout.show(main_pane, PROGRESS);
470 }
471 // Refresh the set of controls.
472 TreePath path = tree.getSelectionPath();
473 tree.clearSelection();
474 tree.setSelectionPath(path);
475
476 }
477
478 /** We are informed when this view pane loses focus so we can update build options. */
479 public void loseFocus() {
480 tree.valueChanged(null);
481 }
482
483 /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
484 * @param event A <strong>GShellEvent</strong> that contains, amoung other things, the message.
485 */
486 public synchronized void message(GShellEvent event) {
487 // Ignore the messages from RecPlug (used for progress bars)
488 if (event.getMessage().startsWith("import.pl> RecPlug - ")) {
489 return;
490 }
491 document.appendLine(event.getMessage());
492 log_textarea.setCaretPosition(document.getLengthToNearestLine());
493 }
494
495 /** Called when the detail mode has changed which in turn may cause several import/build configuration arguments to be available/hidden
496 * @param mode the new mode as an int
497 */
498 public void modeChanged(int mode) {
499 // If we are below the complexity threshold ensure the simple controls are being shown
500 if(Configuration.getMode() < THRESHOLD) {
501 // Update the arguments
502 simple_panel.remove(sargument_configuration_scroll);
503 if(options_pane != null) {
504 sargument_configuration_panel = options_pane.buildImport(null);
505 sargument_configuration_panel = options_pane.buildBuild(sargument_configuration_panel);
506 }
507 else {
508 sargument_configuration_panel = new JPanel();
509 }
510 sargument_configuration_scroll = new JScrollPane(sargument_configuration_panel);
511 sargument_configuration_panel.setComponentOrientation(Dictionary.getOrientation());
512 sargument_configuration_scroll.setComponentOrientation(Dictionary.getOrientation());
513 sargument_configuration_scroll.setPreferredSize(ARGUMENT_SIZE);
514 simple_panel.setTopComponent(sargument_configuration_scroll);
515 // Remove the shared components from the expert panels
516 progress_pane.remove(log_scroll);
517 // And add to simple one
518 slower_panel.add(log_scroll, BorderLayout.CENTER);
519 // And bring the card to the front
520 card_layout.show(main_pane, SIMPLE);
521 }
522 // And if we are above the threshold change to the complex controls
523 else {
524 // Remove the shared components from the simple panel
525 slower_panel.remove(log_scroll);
526 // And add then to the expert ones
527 progress_pane.add(log_scroll, BorderLayout.CENTER);
528 // And bring the appropriate card to the front
529 if(!processing) {
530 control_pane.add(button_pane, BorderLayout.SOUTH);
531 card_layout.show(main_pane, CONTROL);
532 }
533 else {
534 progress_pane.add(button_pane, BorderLayout.SOUTH);
535 card_layout.show(main_pane, PROGRESS);
536 }
537 }
538 tree.valueChanged(null); // Ensure tree argument panels are rebuilt
539 }
540
541 /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
542 * @param event A <strong>GShellEvent</strong> that contains details of the initial state of the <strong>GShell</strong> before task comencement.
543 */
544 public synchronized void processBegun(GShellEvent event) {
545 // We don't care. We'll get a more acurate start from the progress monitors.
546 }
547 /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
548 * @param event A <strong>GShellEvent</strong> that contains details of the final state of the <strong>GShell</strong> after task completion.
549 */
550 public synchronized void processComplete(GShellEvent event) {
551 DebugStream.println("In CreatePane::processComplete(" + event.getType() + ")...");
552 if(event.getStatus() == GShell.OK) {
553 if(event.getType() == GShell.SCHEDULE) {
554
555 processing = false;
556 build_button.setEnabled(true);
557 cancel_button.setEnabled(false);
558 //preview_button.setEnabled(true);
559 //only enable preview if the collection has been built.
560 preview_button.setEnabled(Gatherer.c_man.built());
561 simple_build_button.setEnabled(true);
562 simple_cancel_button.setEnabled(false);
563 simple_preview_button.setEnabled(Gatherer.c_man.built());
564 int status = event.getStatus();
565 document.setSpecialCharacter(OptionsPane.SCHEDULED);
566 options_pane.resetFileEntry();
567 build_monitor.reset();
568 import_monitor.reset();
569 if(Configuration.getMode() >= THRESHOLD) {
570 control_pane.add(button_pane, BorderLayout.SOUTH);
571 card_layout.show(main_pane, CONTROL);
572 }
573 }
574 else if(event.getType() == GShell.BUILD) {
575 processing = false;
576 build_button.setEnabled(true);
577 cancel_button.setEnabled(false);
578 preview_button.setEnabled(Gatherer.c_man.built());
579 simple_build_button.setEnabled(true);
580 simple_cancel_button.setEnabled(false);
581 simple_preview_button.setEnabled(Gatherer.c_man.built());
582 int status = event.getStatus();
583 document.setSpecialCharacter(OptionsPane.SUCCESSFUL);
584 options_pane.resetFileEntry();
585 build_monitor.reset();
586 import_monitor.reset();
587 if(Configuration.getMode() >= THRESHOLD) {
588 control_pane.add(button_pane, BorderLayout.SOUTH);
589 card_layout.show(main_pane, CONTROL);
590 }
591 }
592 // Otherwise its completed import but still got build to go
593 }
594 else {
595 processing = false;
596 cancel_button.setEnabled(false);
597 build_button.setEnabled(true);
598 // The build may have failed, but a previous index may still be in place
599 preview_button.setEnabled(Gatherer.c_man.built());
600
601 simple_build_button.setEnabled(true);
602 simple_cancel_button.setEnabled(false);
603 simple_preview_button.setEnabled(Gatherer.c_man.built());
604 if(event.getStatus() == GShell.CANCELLED) {
605 document.setSpecialCharacter(OptionsPane.CANCELLED);
606 }
607 else {
608 document.setSpecialCharacter(OptionsPane.UNSUCCESSFUL);
609 }
610 options_pane.resetFileEntry();
611 if(Configuration.getMode() >= THRESHOLD) {
612 control_pane.add(button_pane, BorderLayout.SOUTH);
613 card_layout.show(main_pane, CONTROL);
614 }
615 }
616 }
617
618
619 /** This method is invoked at any time there has been a significant change in the collection, such as saving, loading or creating.
620 * @param ready A <strong>boolean</strong> indicating if a collection is currently available.
621 * @see org.greenstone.gatherer.Gatherer
622 * @see org.greenstone.gatherer.collection.CollectionManager
623 * @see org.greenstone.gatherer.gui.BuildOptions
624 * @see org.greenstone.gatherer.gui.OptionsPane
625 */
626 public void refresh(int refresh_reason, boolean ready)
627 {
628 if (Gatherer.c_man == null || !ready) {
629 return;
630 }
631 Collection current_collection = Gatherer.c_man.getCollection();
632 if (current_collection != previous_collection && !Gatherer.c_man.isImporting()) {
633 this.options_pane = new OptionsPane(current_collection.import_options, current_collection.build_options, current_collection.schedule_options);
634 if (previous_collection != null) {
635 // clear the log
636 Document log_document = log_textarea.getDocument();
637 if (log_document instanceof AppendLineOnlyFileDocument) {
638 ((AppendLineOnlyFileDocument) log_document).destroy();
639 }
640 }
641 previous_collection = current_collection;
642
643 }
644 // If we are in simple mode, we have to rebuild the simple arguments list
645 if(Configuration.getMode() < THRESHOLD) {
646 simple_panel.remove(sargument_configuration_scroll);
647 sargument_configuration_panel = options_pane.buildImport(null);
648 sargument_configuration_panel = options_pane.buildBuild(sargument_configuration_panel);
649 //if(CollectionManager.canDoScheduling()) {
650 //sargument_configuration_panel = options_pane.buildSchedule(sargument_configuration_panel);
651 //}
652 sargument_configuration_scroll = new JScrollPane(sargument_configuration_panel);
653 sargument_configuration_scroll.setComponentOrientation(Dictionary.getOrientation());
654 sargument_configuration_scroll.setPreferredSize(ARGUMENT_SIZE);
655 simple_panel.setTopComponent(sargument_configuration_scroll);
656 }
657 tree.valueChanged(null);
658
659 // Validate the preview button
660 if (Gatherer.c_man.built() && Configuration.library_url != null) {
661 preview_button.setEnabled(true);
662 simple_preview_button.setEnabled(true);
663 }
664 else {
665 preview_button.setEnabled(false);
666 simple_preview_button.setEnabled(false);
667 }
668 }
669
670 /** This class serves as the listener for actions on the build button. */
671 private class BuildButtonListener
672 implements ActionListener {
673 /**
674 * This method checks to see what needs to be done for a build, and starts the process off.
675 * A complete build proccess is started by {@link CollectionManager.importCollection()}, which then imports and builds the collection.
676 * However, this doesn't always happen, as sometimes an incremental build will do :-)
677 * @param event An <strong>ActionEvent</strong> who, thanks to the power of object oriented programming, we don't give two hoots about.
678 * @see org.greenstone.gatherer.Gatherer
679 * @see org.greenstone.gatherer.collection.CollectionManager
680 * @see org.greenstone.gatherer.gui.BuildOptions
681 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
682 */
683 public void actionPerformed(ActionEvent event) {
684 Collection collection = Gatherer.c_man.getCollection();
685 String collection_name = collection.getName();
686
687 //if button is labelled for Building
688 if(event.getActionCommand().equals(Dictionary.get("CreatePane.Build_Collection"))) {
689
690 //set a static variable marking whether this is a "Complete" or "Minimal" build
691 CollectionDesignManager.setCompleteBuild( full_build_radio_button.isSelected() );
692
693 //do the import and build, skipping import if possible
694 /*if( !CollectionDesignManager.isCompleteBuild() &&
695 !collection.getMetadataChanged() && !collection.getFilesChanged() && Gatherer.c_man.imported() &&
696 CollectionDesignManager.getRebuildTypeRequired() <= CollectionDesignManager.BUILDCOL
697 ) {
698 // Just do build (no import)
699 DebugStream.println("Just want to run buildcol.pl");
700 prepareForBuild();
701 Gatherer.c_man.buildCollection();
702 }
703 else {
704 */
705 prepareForBuild();
706
707 // Starting building,
708 // For now, for a GS3 solr collection, we need to stop the GS3 server before building
709 // and restart it after building is complete. Restart is done in CollectionManager.processComplete()
710 if(CollectionManager.isSolrCollection()) {
711 /*GS3ServerThread thread = new GS3ServerThread(Configuration.gsdl3_src_path, "stop");
712 thread.start();*/
713 }
714
715 Gatherer.c_man.importCollection(); //This starts the building process.
716 /*
717 }
718 */
719
720 } else { //button is labelled for setting up scheduling
721
722 //this needs to be called to configure buttons... but no building is done
723 prepareForBuild();
724 Gatherer.c_man.scheduleBuild();
725 }
726 //Re-setting the rebuildTypeRequired is handled by CollectionManager.processComplete(GShellEvent)
727 }
728
729
730 /**
731 * This does some stuff that is needed before a collection can be built.
732 * For example, buttons are enabled/disabled, and certain flags are set.
733 * This is called from {@link actionPerformed(ActionEvent)}
734 */
735 private void prepareForBuild() {
736 // First we force the build options to be updated if we haven't already.
737 tree.valueChanged(null);
738
739 // Remember that for lower thresholds the above doesn't work, so try this instead
740 if(Configuration.getMode() < THRESHOLD) {
741 options_pane.update(sargument_configuration_panel);
742 }
743
744 // Now go about building.
745 build_button.setEnabled(false);
746 cancel_button.setEnabled(true);
747 preview_button.setEnabled(false);
748
749 simple_build_button.setEnabled(false);
750 simple_cancel_button.setEnabled(true);
751 simple_preview_button.setEnabled(false);
752
753 document = options_pane.createNewLogDocument();
754 log_textarea.setDocument(document);
755 options_pane.log_textarea.setDocument(document);
756 // Change the view.
757 processing = true;
758 if(Configuration.getMode() >= THRESHOLD) {
759 progress_pane.add(button_pane, BorderLayout.SOUTH);
760 card_layout.show(main_pane, PROGRESS);
761 }
762 // Reset the stop flag in all the process monitors, just incase.
763 ((GBuildProgressMonitor)build_monitor).reset();
764 import_monitor.setStop(false);
765 }
766 }
767
768 /** I hope this works. Wendy */
769 private class BuildSimpleButtonListener
770 implements ActionListener {
771 /**
772 * This method checks to see what needs to be done for a build, and starts the process off.
773 * A complete build proccess is started by {@link CollectionManager.importCollection()}, which then imports and builds the collection.
774 * However, this doesn't always happen, as sometimes an incremental build will do :-)
775 * @param event An <strong>ActionEvent</strong> who, thanks to the power of object oriented programming, we don't give two hoots about.
776 * @see org.greenstone.gatherer.Gatherer
777 * @see org.greenstone.gatherer.collection.CollectionManager
778 * @see org.greenstone.gatherer.gui.BuildOptions
779 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
780 */
781 public void actionPerformed(ActionEvent event) {
782 Collection collection = Gatherer.c_man.getCollection();
783 String collection_name = collection.getName();
784
785 //set a static variable marking whether this is a "Complete" or "Minimal" build
786 CollectionDesignManager.setCompleteBuild( full_build_radio_button.isSelected() );
787
788 //do the import and build, skipping import if possible
789 /*if( !CollectionDesignManager.isCompleteBuild() &&
790 !collection.getMetadataChanged() && !collection.getFilesChanged() && Gatherer.c_man.imported() &&
791 CollectionDesignManager.getRebuildTypeRequired() <= CollectionDesignManager.BUILDCOL
792 ) {
793 // Just do build (no import)
794 DebugStream.println("Just want to run buildcol.pl");
795 prepareForBuild();
796 Gatherer.c_man.buildCollection();
797 }
798 else {
799 */
800 prepareForBuild();
801 Gatherer.c_man.importCollection(); //This starts the building process.
802 /*
803 }
804 */
805
806 //Re-setting the rebuildTypeRequired is handled by CollectionManager.processComplete(GShellEvent)
807 }
808
809 /**
810 * This does some stuff that is needed before a collection can be built.
811 * For example, buttons are enabled/disabled, and certain flags are set.
812 * This is called from {@link actionPerformed(ActionEvent)}
813 */
814 private void prepareForBuild() {
815 // First we force the build options to be updated if we haven't already.
816 tree.valueChanged(null);
817
818 // Remember that for lower thresholds the above doesn't work, so try this instead
819 if(Configuration.getMode() < THRESHOLD) {
820 options_pane.update(sargument_configuration_panel);
821 }
822
823 // Now go about building.
824 build_button.setEnabled(false);
825 cancel_button.setEnabled(true);
826 preview_button.setEnabled(false);
827
828 simple_build_button.setEnabled(false);
829 simple_cancel_button.setEnabled(true);
830 simple_preview_button.setEnabled(false);
831
832 document = options_pane.createNewLogDocument();
833 log_textarea.setDocument(document);
834 options_pane.log_textarea.setDocument(document);
835 // Change the view.
836 processing = true;
837 if(Configuration.getMode() >= THRESHOLD) {
838 progress_pane.add(button_pane, BorderLayout.SOUTH);
839 card_layout.show(main_pane, PROGRESS);
840 }
841 // Reset the stop flag in all the process monitors, just incase.
842 ((GBuildProgressMonitor)build_monitor).reset();
843 import_monitor.setStop(false);
844 }
845 }
846
847
848 /** This class serves as the listener for actions on the cancel button. */
849 private class CancelButtonListener
850 implements ActionListener {
851 /** This method attempts to cancel the current GShell task. It does this by first telling CollectionManager not to carry out any further action.
852 * Previously, this would in turn tell the GShell to break from the current job immediately, without waiting for the processEnded message, and then kills the thread in an attempt to stop the process. The results of such an action are debatable.
853 * Now, pressing cancel will send an interrupt to the GShell thread, which is the thread in which the external process is run (via the SafeProcess object). Interrupting a running SafeProcess will then interrupt any worker threads and destroy the process, with SafeProcess cleaning up after itself after its worker threads finished cleaning up after themselves. Tested on Linux.
854 * @param event An <strong>ActionEvent</strong> who, thanks to the power of object oriented programming, we don't give two hoots about.
855 * @see org.greenstone.gatherer.Gatherer
856 * @see org.greenstone.gatherer.collection.CollectionManager
857 * @see org.greenstone.gatherer.gui.BuildOptions
858 * @see org.greenstone.gatherer.shell.GShellProgressMonitor
859 */
860 public void actionPerformed(ActionEvent event) {
861 build_button.setEnabled(false);
862 cancel_button.setEnabled(false);
863 preview_button.setEnabled(false);
864
865 simple_build_button.setEnabled(false);
866 simple_cancel_button.setEnabled(false);
867 simple_preview_button.setEnabled(false);
868
869 processing = false;
870 document.setSpecialCharacter(OptionsPane.CANCELLED);
871 if(Configuration.getMode() >= THRESHOLD) {
872 control_pane.add(button_pane, BorderLayout.SOUTH);
873 card_layout.show(main_pane, CONTROL);
874 }
875 // Set the stop flag in all the process monitor.
876 import_monitor.setStop(true);
877 import_monitor.reset();
878 build_monitor.setStop(true);
879 build_monitor.reset();
880 // Tell the GShell to cleanly stop running its external process
881 if(CreatePane.this.shell != null) {
882 // We can call GShell.cancel() even if the GShell thread is blocking when running a process,
883 // because this CreatePane is running in its own separate GUI thread. This is because the
884 // process blocking call (process.waitFor()) and cancel() are not sequential operations.
885 CreatePane.this.shell.cancel();
886 }
887
888 // Remove the collection lock.
889 //Gatherer.g_man.lockCollection(false, false);
890
891 // Cancelling building:
892 // For now, for a GS3 solr collection, we need to stop the GS3 server before building
893 // and restart it after building is complete. If a cancel is pressed during build
894 // then we also restart the GS3 server
895 if(CollectionManager.isSolrCollection()) {
896 /*GS3ServerThread thread = new GS3ServerThread(Configuration.gsdl3_src_path, "restart");
897 thread.start();*/
898 }
899
900
901 }
902 }
903
904
905 /** The OptionTree is simply a tree structure that has a root node labelled "Options" and then has a child node for each available options screen. These screens are either combinations of the available import and build arguments, or a message log detailing the shell processes progress. */
906 private class OptionTree
907 extends JTree
908 implements TreeSelectionListener {
909 /** The model behind the tree. */
910 private DefaultTreeModel model = null;
911 /** The previous options view displayed, which is sometimes needed to refresh properly. */
912 private JPanel previous_pane = null;
913 /** The node corresponding to the building options view. */
914 private OptionTreeNode building = null;
915 /** The node corresponding to the importing options view. */
916 private OptionTreeNode importing = null;
917 /** The node corresponding to the scheduling options view. */
918 private OptionTreeNode scheduling = null;
919 /** The node corresponding to the log view. */
920 private OptionTreeNode log = null;
921 /** The root node of the options tree, which has no associated options view. */
922 private OptionTreeNode options = null;
923 /** Constructor.
924 * @see org.greenstone.gatherer.gui.CreatePane.OptionTreeNode
925 */
926 public OptionTree() {
927 super();
928 this.setComponentOrientation(Dictionary.getOrientation());
929 ToolTipManager.sharedInstance().registerComponent(this);
930 addTreeSelectionListener(this);
931 getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
932 setCellRenderer(new ToolTipTreeCellRenderer());
933 setRootVisible(false);
934 setToggleClickCount(1);
935
936 // Create tree.
937 building = new OptionTreeNode(Dictionary.get("CreatePane.Build"));
938 building.setToolTipText(Dictionary.get("CreatePane.Build_Tooltip"));
939 importing = new OptionTreeNode(Dictionary.get("CreatePane.Import"));
940 importing.setToolTipText(Dictionary.get("CreatePane.Import_Tooltip"));
941 if (CollectionManager.canDoScheduling()) {
942 scheduling = new OptionTreeNode(Dictionary.get("CreatePane.Schedule"));
943 scheduling.setToolTipText(Dictionary.get("CreatePane.Schedule_Tooltip"));
944
945 }
946 log = new OptionTreeNode(Dictionary.get("CreatePane.Log"));
947 log.setToolTipText(Dictionary.get("CreatePane.Log_Tooltip"));
948 options = new OptionTreeNode(Dictionary.get("CreatePane.Options"));
949
950 model = new DefaultTreeModel(options);
951 setModel(model);
952 model.insertNodeInto(importing, options, 0);
953 model.insertNodeInto(building, options, 1);
954 if (CollectionManager.canDoScheduling()) {
955 model.insertNodeInto(scheduling, options, 2);
956 model.insertNodeInto(log, options, 3);
957 } else {
958 model.insertNodeInto(log, options, 2);
959 }
960 // Expand the root node
961 expandPath(new TreePath(options));
962 }
963 /** Any implementation of TreeSelectionListener must include this method to allow this listener to know when the selection has changed. Here we swap the options view depending on the selected OptionTreeNode.
964 * @param event A <Strong>TreeSelectionEvent</strong> which contains all the information garnered when the event occured.
965 * @see org.greenstone.gatherer.gui.CreatePane.OptionTreeNode
966 */
967 public void valueChanged(TreeSelectionEvent event) {
968 TreePath path = null;
969 OptionTreeNode node = null;
970 //if(event != null) {
971 //path = event.getPath();
972 path = getSelectionPath();
973 if(path != null) {
974 node = (OptionTreeNode)path.getLastPathComponent();
975 }
976 //}
977 if(previous_pane != null) {
978 //target_pane.remove(previous_pane);
979 options_pane.update(previous_pane);
980 if(scroll_pane != null) {
981 right.remove(scroll_pane);
982 }
983 else {
984 right.remove(previous_pane);
985 }
986 previous_pane = null;
987 scroll_pane = null;
988 }
989 if(node != null && node.equals(log)) {
990 build_button.setActionCommand(Dictionary.get("CreatePane.Build_Collection"));
991 build_button.setText(Dictionary.get("CreatePane.Build_Collection"));
992 }
993 if(node != null && node.equals(building)) {
994 build_button.setActionCommand(Dictionary.get("CreatePane.Build_Collection"));
995 build_button.setText(Dictionary.get("CreatePane.Build_Collection"));
996
997
998 previous_pane = options_pane.buildBuild(null);
999 scroll_pane = new JScrollPane(previous_pane);
1000 right.add(scroll_pane, BorderLayout.CENTER);
1001 //target_pane.add(previous_pane, BorderLayout.CENTER);
1002 }
1003 else if(node != null && node.equals(importing)) {
1004 build_button.setActionCommand(Dictionary.get("CreatePane.Build_Collection"));
1005 build_button.setText(Dictionary.get("CreatePane.Build_Collection"));
1006
1007 previous_pane = options_pane.buildImport(null);
1008 scroll_pane = new JScrollPane(previous_pane);
1009 right.add(scroll_pane, BorderLayout.CENTER);
1010 //target_pane.add(previous_pane, BorderLayout.CENTER);
1011 }
1012 else if(node != null && CollectionManager.canDoScheduling() && node.equals(scheduling)) {
1013 build_button.setActionCommand(Dictionary.get("CreatePane.Schedule_Build"));
1014 build_button.setText(Dictionary.get("CreatePane.Schedule_Build"));
1015
1016 previous_pane = options_pane.buildSchedule(null);
1017 scroll_pane = new JScrollPane(previous_pane);
1018 right.add(scroll_pane, BorderLayout.CENTER);
1019 }
1020 else {
1021 if (options_pane != null) {
1022 previous_pane = options_pane.buildLog();
1023 right.add(previous_pane, BorderLayout.CENTER);
1024 right.updateUI(); // we need to repaint the log pane, cos it hasn't changed since last time
1025 ///ystem.err.println("I've added the log back to the right pane again.");
1026 //target_pane.add(previous_pane, BorderLayout.CENTER);
1027 }
1028 }
1029 //scroll_pane.setViewportView(previous_pane);
1030 //previous_pane.validate();
1031 right.validate();
1032 //System.err.println("Current pane size: " + previous_pane.getSize());
1033 //System.err.println("While its preferred size is: " + previous_pane.getPreferredSize());
1034 }
1035 }
1036
1037 /** The <strong>OptionTree</strong> is built from these nodes, each of which has several methods used in creating the option panes.
1038 */
1039 private class OptionTreeNode
1040 extends DefaultMutableTreeNode
1041 implements Comparable {
1042 /** The text label given to this node in the tree. */
1043 private String title = null;
1044 /** a tool tip to be used for this node in the tree */
1045 private String tool_tip = null;
1046 /** Constructor.
1047 * @param title The <strong>String</strong> which serves as this nodes title.
1048 */
1049 public OptionTreeNode(String title) {
1050 super();
1051 this.title = title;
1052 }
1053
1054 /** This method compares two nodes for ordering.
1055 * @param obj The <strong>Object</strong> to compare to.
1056 * @return An <strong>int</strong> of one of the possible values -1, 0 or 1 indicating if this node is less than, equal to or greater than the target node respectively.
1057 */
1058 public int compareTo(Object obj) {
1059 return title.compareTo(obj.toString());
1060 }
1061
1062 /** This method checks if two nodes are equivalent.
1063 * @param obj The <strong>Object</strong> to be tested against.
1064 * @return A <strong>boolean</strong> which is <i>true</i> if the objects are equal, <i>false</i> otherwise.
1065 */
1066 public boolean equals(Object obj) {
1067 if(compareTo(obj) == 0) {
1068 return true;
1069 }
1070 return false;
1071 }
1072
1073 /** get the tool tip */
1074 public String getToolTipText() {
1075 return tool_tip;
1076 }
1077
1078 /** set the tool tip */
1079 public void setToolTipText(String tip) {
1080 tool_tip = tip;
1081 }
1082
1083 /** Method to translate this node into a textual representation.
1084 * @return A <strong>String</strong> which in this case is the title.
1085 */
1086 public String toString() {
1087 return title;
1088 }
1089 }
1090
1091 // Adds tooltips to the tree nodes
1092 private class ToolTipTreeCellRenderer
1093 extends DefaultTreeCellRenderer {
1094
1095 public Component getTreeCellRendererComponent(JTree tree,
1096 Object value,
1097 boolean sel,
1098 boolean expanded,
1099 boolean leaf,
1100 int row,
1101 boolean hasFocus) {
1102
1103 super.getTreeCellRendererComponent(tree, value, sel,
1104 expanded, leaf, row,
1105 hasFocus);
1106 if (value instanceof OptionTreeNode) {
1107 String tip = ((OptionTreeNode) value).getToolTipText();
1108 if (tip != null) {
1109 setToolTipText(tip);
1110 }
1111 }
1112 return this;
1113 }
1114 }
1115}
Note: See TracBrowser for help on using the repository browser.