source: trunk/gli/src/org/greenstone/gatherer/gui/GUIManager.java@ 6643

Last change on this file since 6643 was 6643, checked in by jmt12, 20 years ago

Yet another valiant, but in vain, attempt to solve the smooty spooty hangs when a modal dialog displayed at the same time you exit bug. Reinstated the code which determines if a modal dialog is being/about to be displayed, and prevents exit. This time I remembered to change the default close operation of the frame as well - not that it helped much.

  • Property svn:keywords set to Author Date Id Revision
File size: 40.1 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.datatransfer.*;
41import java.awt.event.*;
42import java.io.*;
43import java.lang.*;
44import java.net.*;
45import java.util.*;
46import javax.swing.*;
47import javax.swing.event.*;
48import javax.swing.plaf.*;
49import javax.swing.text.*;
50import org.greenstone.gatherer.Configuration;
51import org.greenstone.gatherer.Dictionary;
52import org.greenstone.gatherer.Gatherer;
53import org.greenstone.gatherer.collection.Collection;
54import org.greenstone.gatherer.collection.DeleteCollectionPrompt;
55import org.greenstone.gatherer.collection.ExportCollectionPrompt;
56import org.greenstone.gatherer.collection.SaveCollectionBox;
57import org.greenstone.gatherer.file.FileNode;
58import org.greenstone.gatherer.file.FileOpenActionListener;
59import org.greenstone.gatherer.gui.AboutDialog;
60//import org.greenstone.gatherer.gui.BrowsingPane;
61import org.greenstone.gatherer.gui.CollectionPane;
62import org.greenstone.gatherer.gui.CreatePane;
63import org.greenstone.gatherer.gui.Filter;
64import org.greenstone.gatherer.gui.GConfigPane;
65import org.greenstone.gatherer.gui.MenuBar;
66import org.greenstone.gatherer.gui.MetaEditPane;
67import org.greenstone.gatherer.gui.MirrorPane;
68import org.greenstone.gatherer.gui.OpenCollectionDialog;
69import org.greenstone.gatherer.gui.Preferences;
70//import org.greenstone.gatherer.gui.PreviewPane;
71import org.greenstone.gatherer.gui.SimpleOpenCollectionDialog;
72import org.greenstone.gatherer.gui.metaaudit.MetaAuditFrame;
73import org.greenstone.gatherer.gui.tree.DragTree;
74import org.greenstone.gatherer.gui.tree.WorkspaceTree;
75import org.greenstone.gatherer.help.HelpFrame;
76import org.greenstone.gatherer.mem.MetadataEditorManager;
77import org.greenstone.gatherer.msm.ElementWrapper;
78import org.greenstone.gatherer.msm.Metadata;
79import org.greenstone.gatherer.shell.GShell;
80import org.greenstone.gatherer.util.StaticStrings;
81import org.greenstone.gatherer.util.TreeSynchronizer;
82import org.greenstone.gatherer.util.Utility;
83
84/** The GUIManager is in charge of creating the Gatherer window frame then filling it with the goodness of the view panes. GUIManager not only creates these panes, but allows some messaging between them. Furthermore GUIManager includes functionality from menu driven choices, simply as it was easier to put it here once and have it accessible from all pane children. */
85public class GUIManager
86 extends JFrame
87 implements ActionListener, ChangeListener {
88 /** The browsing pane behaves much like an internet browser, or at least will some day. */
89 //public BrowsingPane browser_pane = null;
90 /** The collection pane is more like a file manager where you drag files from one tree to another. */
91 public CollectionPane collection_pane = null;
92 /** The create pane contains scripting options for importing and building collections into libraries. */
93 public CreatePane create_pane = null;
94
95 public FileOpenActionListener foa_listener = new FileOpenActionListener();
96
97 /** The configuration pane allows you to edit the design of the library in terms of the collection configuration file. */
98 public GConfigPane config_pane = null;
99 /** A reference to the currently instantiated help window, if any. */
100 public HelpFrame help = null;
101 /** The menu bar. */
102 public MenuBar menu_bar = null;
103 public MetaAuditFrame meta_audit;
104 /** The metaedit pane is used to assign, edit and remove metadata from files within the collection. */
105 public MetaEditPane metaedit_pane = null;
106 /** The mirror pane contains controls for mirroring internet sites. */
107 public MirrorPane mirror_pane = null;
108 /** The preview pane contains a preview of your build collection. */
109 //public PreviewPane preview_pane = null;
110 /** Are certain panes currently locked? */
111 private boolean locked = false;
112 /** The size of the Gatherer window. */
113 private Dimension size = null;
114 /** The filters used to dynamically filter the trees at user request. */
115 private HashMap filters = new HashMap();
116 /** The panel within the window that other components are placed on. */
117 private JPanel content_pane = null;
118 /** The dummy export pane. */
119 private JPanel export_pane = new JPanel();
120 /** The last view pane selected. */
121 private JPanel previous_pane;
122 /** The main tab pane containing the different views, available here to trap view change events. */
123 private JTabbedPane tab_pane = null;
124 /** A reference to any existing search and replace module/dialog. */
125 // private SearchAndReplace sar = null;
126 /** A threaded tab changer to try and avoid NPE on exit. */
127 private TabUpdater tab_updater = null;
128 /** The thread group this manager, and hence its child graphical rendering threads, belong to. In a vain attempts to make the Dictionary work across threads.
129 * @see org.greenstone.gatherer.Dictionary
130 */
131 private ThreadGroup thread_group = null;
132 /** Ensures that expansion events between like collection trees are synchronized. */
133 private TreeSynchronizer collection_tree_sync = null;
134 /** Ensures that expansion events between like workspace trees are synchronized. */
135 private TreeSynchronizer workspace_tree_sync = null;
136 /**Constructor. Enable window events and arranges all other components.
137 * @param size The intial <strong>Dimension</strong> of the screen.
138 * @param graphic The default <strong>GraphicsConfiguration</strong> for this platform.
139 */
140 public GUIManager(Dimension size) {
141 super();
142 // Initialization
143 this.help = new HelpFrame();
144 this.size = size;
145 this.collection_tree_sync = new TreeSynchronizer();
146 this.meta_audit = new MetaAuditFrame(collection_tree_sync, null);
147 this.workspace_tree_sync = new TreeSynchronizer();
148
149 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
150
151 // Add a focus listener to ourselves. Thus if we gain focus when a Modal Dialog should instead have it, we can try to bring the modal dialog to the fore.
152 this.addFocusListener(new GLIGUIFocusListener());
153
154 // Make the Tool tip hang around for a rediculous amount of time.
155 ToolTipManager.sharedInstance().setDismissDelay(10000);
156 // Get a reference to the main thread group. Create a new thread, which defaults into the same thread group, then get its thread group. Easy.
157 Thread bob = new Thread();
158 thread_group = bob.getThreadGroup();
159 // Set up some other UI stuff. (fonts handled in Gatherer.main())
160 UIManager.put("FileChooser.lookInLabelText", Dictionary.get("SaveCollectionBox.Look_In"));
161 UIManager.put("FileChooser.filesOfTypeLabelText", Dictionary.get("SaveCollectionBox.Files_Of_Type"));
162 UIManager.put("FileChooser.fileNameLabelText", Dictionary.get("SaveCollectionBox.File_Name"));
163 }
164
165 private class GLIGUIFocusListener
166 extends FocusAdapter {
167 public void focusGained(FocusEvent e) {
168 if(Gatherer.current_modal != null) {
169 Gatherer.current_modal.makeVisible();
170 Gatherer.current_modal.toFront();
171 }
172 }
173 }
174
175 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured. In this case we are listening to actions from the menu-bar, and should react appropriately.
176 * @param event An <strong>ActionEvent</strong> containing information about the action that has occured.
177 */
178 public void actionPerformed(ActionEvent event) {
179 boolean cont = true;
180 Object esrc = event.getSource();
181 // *************
182 // File Options.
183 // *************
184 if(esrc == menu_bar.file_associations) {
185 Gatherer.assoc_man.edit();
186 }
187 else if(esrc == menu_bar.file_close) {
188 // if(!Gatherer.c_man.saved()) {
189 cont = showSaveCollectionBox(true, false);
190 // }
191 if(cont) {
192 tab_pane.setSelectedComponent(collection_pane);
193 }
194 }
195 else if(esrc == menu_bar.file_delete) {
196 DeleteCollectionPrompt dcp = new DeleteCollectionPrompt();
197 if(dcp.display()) {
198 Gatherer.c_man.closeCollection();
199 }
200 dcp.destroy();
201 dcp = null;
202 System.gc();
203 }
204 else if(esrc == menu_bar.file_export) {
205 ExportCollectionPrompt ecp = new ExportCollectionPrompt();
206 ecp.display();
207 ecp.destroy();
208 ecp = null;
209 }
210 else if(esrc == menu_bar.file_exit) {
211 menu_bar.exit();
212 exit();
213 }
214 else if(esrc == menu_bar.file_new) {
215 showNewCollectionPrompt();
216 }
217 else if(esrc == menu_bar.file_open) {
218 if (showLoadCollectionBox()) {
219 tab_pane.setSelectedComponent(collection_pane);
220 }
221 }
222 else if(esrc == menu_bar.file_options) {
223 // Just incase the user has edited the GeneralSettings of a collection without losing focus afterwards. Well I'm forever losing foc... ooh shiney.
224 config_pane.loseFocus();
225 // And spawn a new preferences.
226 new Preferences();
227 }
228 else if(esrc == menu_bar.file_save) {
229 Gatherer.c_man.saveCollection(false, false);
230 }
231// else if(esrc == menu_bar.file_save_as) {
232// String name = JOptionPane.showInputDialog(this, "Enter new collection filename.");
233// if(name != null) {
234// Gatherer.c_man.saveCollectionAs(name);
235// }
236// }
237 // *************
238 // Edit Options.
239 // *************
240 else if(esrc == menu_bar.edit_copy) {
241 try {
242 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
243 // Get the component with selected text as a JTextComponent
244 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();//getFocusOwner();
245 text.copy();
246 }
247 catch (Exception cce) {
248 // If the component is not a text component ignore the copy command
249 Gatherer.println(cce.toString());
250 }
251 }
252 else if(esrc == menu_bar.edit_cut) {
253 try {
254 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
255 // Get the component with selected text as a JTextComponent
256 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
257 // Cut the text to the clipboard
258 text.cut();
259 }
260 catch (ClassCastException cce) {
261 // If the component is not a text component ignore the cut command
262 Gatherer.println(cce.toString());
263 }
264 }
265 else if(esrc == menu_bar.edit_paste) {
266 try {
267 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
268 // Get the component with selected text as a JTextComponent
269 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
270 // Cut the text to the clipboard
271 text.paste();
272 }
273 catch (ClassCastException cce) {
274 // If the component is not a text component ignore the paste command
275 Gatherer.println(cce.toString());
276 }
277 }
278// else if(esrc == menu_bar.edit_search) {
279// if(sar != null) {
280// sar.dispose();
281// }
282// sar = new SearchAndReplace(false);
283// }
284// else if(esrc == menu_bar.edit_replace) {
285// if(sar != null) {
286// sar.dispose();
287// }
288// sar = new SearchAndReplace(true);
289// }
290
291 // *************
292 // Tools Options.
293 // *************
294// else if(esrc == menu_bar.tools_size) {
295// FileNode records[] = metaedit_pane.getSelectedNode();
296// if(records != null && records.length > 0) {
297// ArrayList metadatum = Gatherer.c_man.getCollection().gdm.getMetadata(records[0].getFile());
298// if(metadatum.size() >= 4) {
299// Metadata metadata = (Metadata) metadatum.get(0);
300// ElementWrapper ew = metadata.getElement();
301
302// int SIZE = 5000;
303// Object[] array = new Object[SIZE];
304// Runtime.getRuntime().gc();
305// long start = Runtime.getRuntime().freeMemory();
306// for (int i = 0; i < SIZE; i++) {
307// array[i] = ew.copy();
308// }
309// Runtime.getRuntime().gc();
310// long end = Runtime.getRuntime().freeMemory();
311// long difference = (( start - end ) / SIZE);
312// ///ystem.out.println(difference + " bytes used." );
313// }
314// }
315// }
316
317 // *************
318 // Help Options.
319 // *************
320 else if(esrc == menu_bar.help_about) {
321 new AboutDialog(this);
322 }
323// else if(esrc == menu_bar.help_browse) {
324// help.setView("huntingforfiles");
325// }
326 else if(esrc == menu_bar.help_build) {
327 help.setView("producingthecollection");
328 }
329 else if(esrc == menu_bar.help_collect) {
330 help.setView("collectingfiles");
331 }
332 else if(esrc == menu_bar.help_design) {
333 help.setView("designingacollection");
334 }
335 else if(esrc == menu_bar.help_general) {
336 help.setView("introduction");
337 }
338 else if(esrc == menu_bar.help_metaedit) {
339 help.setView("enrichingacollection");
340 }
341 else if(esrc == menu_bar.help_mirror) {
342 help.setView("downloadingfiles");
343 }
344 else if(esrc == menu_bar.help_preview) {
345 help.setView("previewingthecollection");
346 }
347 // *****************
348 // Metadata Options.
349 // *****************
350 else if(esrc == menu_bar.metadata_import) {
351 Gatherer.c_man.getCollection().msm.importMDS();
352 }
353 else if(esrc == menu_bar.metadata_edit) {
354 showEditMetadataBox();
355 }
356 else if(esrc == menu_bar.metadata_export) {
357 Gatherer.c_man.getCollection().msm.exportMDS();
358 }
359 else if(esrc == menu_bar.metadata_view) {
360 showMetaAuditBox();
361 }
362 }
363 /** Any actions that should happen after the display of the Gatherer window can be called here. Currently only updates the browser pane if it is active to work around bug in Mozilla renderer implementation.
364 */
365 public void afterDisplay() {
366// if(Gatherer.config.get("workflow.browse", true)) {
367// browser_pane.afterDisplay();
368// }
369 if(mirror_pane != null) {
370 mirror_pane.afterDisplay();
371 }
372 metaedit_pane.afterDisplay();
373 }
374 /** Once a collection has been made available to Gatherer, either by its creation or by it being reloaded, we need to inform all the visual components to update necessary data components (such as tree models), enable certain controls that depend on a collection being accessible, and refresh themselves.
375 * @param ready <i>true</i> if the collection is ready for editing, <i>false</i> otherwise.
376 */
377 public void collectionChanged(final boolean ready) {
378 if(Gatherer.config.get("workflow.mirror", true)) {
379 mirror_pane.collectionChanged(ready);
380 }
381 menu_bar.collectionChanged(ready); // Inform the menu bar that the collections changed.
382 collection_pane.collectionChanged(ready); // Used to update the collection workspace.
383 metaedit_pane.collectionChanged(ready); // Very important that metaedit pane shows current collection and is listening to latest msm.
384 config_pane.collectionChanged(ready); // Also important config pane is listening to latest msm.
385 create_pane.collectionChanged(ready); // Used to indicate a new BuildOptions model should be loaded.
386 //preview_pane.collectionChanged(ready); // preview should know when teh coll has changed so it can reload the home page
387
388 if(!locked) {
389 // Now enable tabs as necessary. Do this on event queue to prevent crazy NPEs
390 if(tab_updater == null) {
391 tab_updater = new TabUpdater(tab_pane, ready);
392 }
393 else {
394 tab_updater.setReady(ready);
395 }
396 SwingUtilities.invokeLater(tab_updater);
397 }
398 // Set the title
399 String collection_title = null;
400 String collection_name = null;
401 if (ready) {
402 Collection collection = Gatherer.c_man.getCollection();
403 collection_title = collection.getTitle();
404 collection_name = collection.getName();
405 collection = null;
406 }
407 setTitle(collection_title, collection_name);
408 collection_title = null;
409 collection_name = null;
410 // Now is a good time to force a garbage collect.
411 ///ystem.err.println("Calling garbage collection.");
412 System.gc();
413 }
414
415 public void destroy() {
416 // Destroying create pane ensures the latest log has been closed
417 if(create_pane != null) {
418 create_pane.destroy();
419 }
420 }
421
422 /** Enabled events on the window to be trapped, creates all the visual components, then builds the tab and other layouts.
423 */
424 public void display() {
425 content_pane = (JPanel) this.getContentPane();
426 // Enable window-type events to be fired.
427 enableEvents(AWTEvent.WINDOW_EVENT_MASK);
428 // Initialise and layout sub-components, plus other window dressing.
429 try {
430 this.setSize(size);
431
432 // Set the title
433 String collection_title = null;
434 String collection_name = null;
435 if (Gatherer.c_man.ready()) {
436 Collection collection = Gatherer.c_man.getCollection();
437 collection_title = collection.getTitle();
438 collection_name = collection.getName();
439 collection = null;
440 }
441 setTitle(collection_title, collection_name);
442 collection_title = null;
443 collection_name = null;
444
445 // Pretty corner icon
446 this.setIconImage(Utility.getImage("gatherer_small.gif").getImage());
447 // BorderLayout for the main screen. I'll try my best to avoid these in subcomponents as they're space greedy.
448 content_pane.setLayout(new BorderLayout());
449 // Create the menu-bar and stick it up the top.
450 menu_bar = new MenuBar(new MenuListenerImpl());
451 content_pane.add(menu_bar, BorderLayout.NORTH);
452 // Create the tabbed pane and plop it in the center where it will
453 // expand to consume all available space like any good gas would.
454 tab_pane = new JTabbedPane();
455 tab_pane.addChangeListener(this);
456 tab_pane.setFont(Gatherer.config.getFont("general.font", false));
457
458// // May have to play with the order in which tabs are added.
459// if(Gatherer.config.get("workflow.browse", true)) {
460// browser_pane = new BrowsingPane();
461// tab_pane.addTab("GUI.Hunt", Utility.getImage("browsing.gif"), browser_pane);
462// tab_pane.setEnabledAt(tab_pane.indexOfComponent(browser_pane), Gatherer.config.get("workflow.browse", false));
463// }
464
465 if(Gatherer.config.get(StaticStrings.WORKFLOW_MIRROR, true)) {
466 mirror_pane = new MirrorPane();
467 tab_pane.addTab("GUI.Mirror", Utility.getImage("mirroring.gif"), mirror_pane);
468 tab_pane.setEnabledAt(tab_pane.indexOfComponent(mirror_pane), Gatherer.config.get(StaticStrings.WORKFLOW_MIRROR, false));
469 }
470
471 collection_pane = new CollectionPane(workspace_tree_sync, collection_tree_sync);
472 collection_pane.display();
473 if(Gatherer.config.get("workflow.gather", true)) {
474 tab_pane.addTab("GUI.Gather", Utility.getImage("collection.gif"), collection_pane);
475 tab_pane.setEnabledAt(tab_pane.indexOfComponent(collection_pane), Gatherer.config.get("workflow.gather", false));
476 }
477
478 metaedit_pane = new MetaEditPane(collection_tree_sync);
479 metaedit_pane.display();
480 if(Gatherer.config.get("workflow.enrich", true)) {
481 tab_pane.addTab("GUI.Enrich", Utility.getImage("metaedit.gif"), metaedit_pane);
482 tab_pane.setEnabledAt(tab_pane.indexOfComponent(metaedit_pane), false);
483 }
484
485 config_pane = new GConfigPane();
486 config_pane.display();
487 if(Gatherer.config.get("workflow.design", true)) {
488 tab_pane.addTab("GUI.Design", Utility.getImage("build.gif"), config_pane);
489 tab_pane.setEnabledAt(tab_pane.indexOfComponent(config_pane), false);
490 }
491
492 if(Gatherer.config.get("workflow.export", true)) {
493 tab_pane.addTab("GUI.Export", Utility.getImage("export.gif"), export_pane);
494 tab_pane.setEnabledAt(tab_pane.indexOfComponent(export_pane), false);
495 }
496
497 create_pane = new CreatePane();
498 create_pane.display();
499 if(Gatherer.config.get("workflow.create", true)) {
500 tab_pane.addTab("GUI.Create", Utility.getImage("build session.gif"), create_pane);
501 tab_pane.setEnabledAt(tab_pane.indexOfComponent(create_pane), false);
502 }
503
504 //preview_pane = new PreviewPane();
505 //preview_pane.display();
506 //if(Gatherer.config.get("workflow.preview", true)) {
507 //tab_pane.addTab("GUI.Preview", Utility.getImage("final.gif"), preview_pane);
508 //tab_pane.setEnabledAt(tab_pane.indexOfComponent(preview_pane), false);
509 //}
510
511 // Select the collect pane if it is available
512 if(Gatherer.config.get("workflow.gather", false)) {
513 tab_pane.setSelectedComponent(collection_pane);
514 }
515 // Otherwise find the first tab that is enabled and select that.
516 else {
517 boolean found = false;
518 for(int i = 0; !found && i < tab_pane.getTabCount(); i++) {
519 if(tab_pane.isEnabledAt(i)) {
520 tab_pane.setSelectedIndex(i);
521 found = true;
522 }
523 }
524 }
525
526 Dictionary.register(tab_pane);
527 content_pane.add(tab_pane, BorderLayout.CENTER);
528 // Drive a sessionReady event to update all controls to reflect current collection status.
529 collectionChanged(Gatherer.c_man.ready());
530 }
531 catch (Exception e) {
532 Gatherer.printStackTrace(e);
533 // The GUI failing to build is a app killer
534 e.printStackTrace();
535 System.exit(1);
536 }
537 }
538 /** When called this method ensures that all the things needing saving are saved before Gatherer.exit() is called. This includes a save collection prompt if necessary.
539 */
540 public void exit() {
541 // First of all an exit can only happen if no modal dialog is currently being displayed. Something is creating a modal dialog but not disposing of it.
542 if(Gatherer.current_modal != null) {
543 System.err.println("There is a modal dialog open.");
544 Gatherer.current_modal.makeVisible();
545 Gatherer.current_modal.toFront();
546 return;
547 }
548
549 boolean cont = true;
550 if(Gatherer.c_man.ready() && !Gatherer.c_man.saved()) {
551 cont = showSaveCollectionBox(false, true);
552 }
553 else {
554 // Deal to help
555 if(help != null) {
556 help.destroy();
557 help = null;
558 }
559 Gatherer.self.exit();
560 }
561 }
562 /** Retrieve the filter, or if one already exists, spawn a linked copy. */
563 public Filter getFilter(DragTree tree) {
564 Filter filter = (Filter) filters.get(tree.getModel());
565 if (filter == null) {
566 filter = new Filter(tree, null);
567 filters.put(tree.getModel(), filter);
568 return filter;
569 }
570 return filter.spawn(tree);
571 }
572
573 public Component getSelectedView() {
574 return tab_pane.getSelectedComponent();
575 }
576 /** This method is called when the collection is being built, and is used to disable all controls in all pane which could change the state of the collection.
577 */
578 public void lockCollection(boolean import_stage, boolean lock) {
579 locked = lock;
580 if(import_stage) {
581 int collection_pos = tab_pane.indexOfComponent(collection_pane);
582 int metaedit_pos = tab_pane.indexOfComponent(metaedit_pane);
583 int config_pos = tab_pane.indexOfComponent(config_pane);
584 tab_pane.setEnabledAt(collection_pos, !lock);
585 tab_pane.setEnabledAt(metaedit_pos, !lock);
586 tab_pane.setEnabledAt(config_pos, !lock);
587 }
588 else {
589 int config_pos = tab_pane.indexOfComponent(config_pane);
590 tab_pane.setEnabledAt(config_pos, !lock);
591 }
592 }
593
594 public void modeChanged(int mode) {
595 // Set the title
596 String collection_title = null;
597 String collection_name = null;
598 if (Gatherer.c_man.ready()) {
599 Collection collection = Gatherer.c_man.getCollection();
600 collection_title = collection.getTitle();
601 collection_name = collection.getName();
602 collection = null;
603 }
604 setTitle(collection_title, collection_name);
605 collection_title = null;
606 collection_name = null;
607 // Now pass on the message to anyone who cares
608 if(collection_pane != null) {
609 collection_pane.modeChanged(mode);
610 }
611 if(config_pane != null) {
612 config_pane.modeChanged(mode);
613 }
614 if(create_pane != null) {
615 create_pane.modeChanged(mode);
616 }
617 if(metaedit_pane != null) {
618 metaedit_pane.modeChanged(mode);
619 }
620 }
621
622 public void refreshTrees(int refresh_reason)
623 {
624 if(collection_pane != null) {
625 collection_pane.refreshWorkspaceTree(refresh_reason);
626 collection_pane.refreshCollectionTree(refresh_reason);
627 }
628 if(metaedit_pane != null) {
629 metaedit_pane.refreshTrees();
630 }
631 }
632
633 /** Allows the system to programatically set the selected tab.
634 * @param component The view you wish to make visable in the tab pane as a <strong>Component</strong>.
635 */
636 public void setSelectedView(Component component) {
637 tab_pane.setSelectedComponent(component);
638 }
639
640 /** Specifies whether a certain tab is enabled or not. */
641 public void setTabEnabled(String rawname, boolean state) {
642 // Retrieve the dictionary based name.
643 String name = Dictionary.get("GUI." + rawname);
644 int index = tab_pane.indexOfTab(name);
645 // Of course we may not have this tab available.
646 if(index != -1) {
647 // Some tabs are also dependant on if a collection is ready
648 Component component = tab_pane.getComponentAt(index);
649 //if(component == preview_pane) {
650 //tab_pane.setEnabledAt(index, state && Gatherer.c_man != null && Gatherer.c_man.ready() && Gatherer.c_man.built());
651 //}
652 //else
653 if(component == metaedit_pane || component == config_pane || component == export_pane || component == create_pane) {
654 tab_pane.setEnabledAt(index, state && Gatherer.c_man != null && Gatherer.c_man.ready());
655 }
656 else {
657 tab_pane.setEnabledAt(index, state);
658 }
659 // If this was the currently selected tab and it is now disabled, change the view to the first enabled tab.
660 if(tab_pane.getSelectedIndex() == index && !state) {
661 boolean found = false;
662 for(int i = 0; !found && i < tab_pane.getTabCount(); i++) {
663 if(tab_pane.isEnabledAt(i)) {
664 tab_pane.setSelectedIndex(i);
665 found = true;
666 }
667 }
668 // If there are no tabs enabled, which should be impossible, then select the first tab
669 if(!found) {
670 tab_pane.setSelectedIndex(0);
671 }
672 }
673 }
674 // If the rawname was mirror then rebuild workspace tree to remove caches.
675 if (rawname.equals("Mirror")) {
676 collection_pane.refreshWorkspaceTree(WorkspaceTree.MAPPED_DIRECTORIES_CHANGED);
677 }
678 }
679
680 /** Change the string shown in the title bar of the main gui frame. If either value is null, the 'No Collection' string is shown instead.
681 * @param title
682 * @param name
683 */
684 public void setTitle(String title, String name) {
685 // Finally display the collection name in the title bar.
686 StringBuffer title_buffer = new StringBuffer(Utility.PROGRAM_NAME);
687 title_buffer.append(StaticStrings.SPACE_CHARACTER);
688 title_buffer.append(StaticStrings.SPACE_CHARACTER);
689 // Describe the current user mode
690 title_buffer.append(StaticStrings.MODE_STR);
691 title_buffer.append(Gatherer.config.getModeAsString());
692 title_buffer.append(StaticStrings.SPACE_CHARACTER);
693 title_buffer.append(StaticStrings.SPACE_CHARACTER);
694 // Now for the current collection
695 title_buffer.append(StaticStrings.COLLECTION_STR);
696 if (title != null && name != null) {
697 title_buffer.append(title);
698 title_buffer.append(StaticStrings.SPACE_CHARACTER);
699 title_buffer.append(StaticStrings.OPEN_PARENTHESIS_CHARACTER);
700 title_buffer.append(name);
701 title_buffer.append(StaticStrings.CLOSE_PARENTHESIS_CHARACTER);
702 }
703 else {
704 title_buffer.append(Dictionary.get("Collection.No_Collection"));
705 }
706 this.setTitle(title_buffer.toString());
707 title_buffer = null;
708 }
709
710 /** When the edit metadata option is choosen from the menu, this method is called to ensure we only edit the metadata if there is metadata loaded.
711 */
712 public void showEditMetadataBox() {
713 if(Gatherer.c_man.getCollection() != null) {
714 Gatherer.c_man.getCollection().msm.editMDS(null, MetadataEditorManager.NORMAL);
715 }
716 }
717 /** When the load collection option is choosen this method is called to produce the modal file load prompt.
718 */
719 public boolean showLoadCollectionBox() {
720 boolean result = false;
721 // We first try the simple open collection dialog
722 SimpleOpenCollectionDialog dialog = new SimpleOpenCollectionDialog();
723 int user_choice = dialog.display();
724 String filename = null;
725 // The user may choose to go to the advanced 'browse' dialog
726 if(user_choice == SimpleOpenCollectionDialog.OK_OPTION) {
727 filename = dialog.getFileName();
728 }
729 else if(user_choice == SimpleOpenCollectionDialog.BROWSE_OPTION) {
730 File file;
731 if(Gatherer.config.gsdl_path != null) {
732 file = new File(Utility.getCollectionDir(Gatherer.config.gsdl_path));
733 }
734 else {
735 file = new File(Utility.BASE_DIR);
736 }
737 OpenCollectionDialog chooser = new OpenCollectionDialog(file);
738 file = null;
739 filename = chooser.getFileName();
740 chooser.destroy();
741 chooser = null;
742 }
743 dialog.destroy();
744 dialog = null;
745 // User can cancel action.
746 if(filename != null) {
747 // If there is already a collection open, save and close it.
748 if(Gatherer.c_man.ready()) {
749 showSaveCollectionBox(true, false);
750 // Wait until it is closed.
751 try {
752 synchronized(this) {
753 while(Gatherer.c_man.reallyReady()) {
754 wait(10);
755 }
756 }
757 }
758 catch(Exception error) {
759 Gatherer.println("Exception: " + error);
760 Gatherer.printStackTrace(error);
761 }
762 }
763 result = Gatherer.c_man.loadCollection(filename);
764 filename = null;
765 }
766 return result;
767 }
768
769 /** When called this method causes the MetaAuditBox class in CollectionManager to display a nice dialog box which contains all the metadata assigned in the collection.
770 */
771 public void showMetaAuditBox() {
772 wait(true);
773 meta_audit.display();
774 wait(false);
775 }
776 /** This method is used to open the new collection box on the screen.
777 */
778 public void showNewCollectionPrompt() {
779 NewCollectionMetadataPrompt ncm_prompt = null;
780 // Create the collection details prompt from new collection prompt
781 NewCollectionDetailsPrompt ncd_prompt = new NewCollectionDetailsPrompt();
782 // If no previous collection was indicated as a model design, then show the metadata selection prompt from new collection prompt
783 if(!ncd_prompt.isCancelled() && (ncd_prompt.getBase() == null)) {
784 ncm_prompt = new NewCollectionMetadataPrompt();
785 }
786 // Create the new collection (if not cancelled) in a new thread.
787 if(!ncd_prompt.isCancelled() && (ncm_prompt == null || !ncm_prompt.isCancelled())) {
788 // If there is already a collection open, save and close it.
789 if(Gatherer.c_man.ready()) {
790 showSaveCollectionBox(true, false);
791 // Wait until it is closed.
792 try {
793 synchronized(this) {
794 while(Gatherer.c_man.reallyReady()) {
795 wait(10);
796 }
797 }
798 }
799 catch(Exception error) {
800 Gatherer.println("Exception: " + error);
801 Gatherer.printStackTrace(error);
802 }
803 }
804
805 // Create new collection.
806 CreationTask task = new CreationTask(ncd_prompt, ncm_prompt);
807 // SwingUtilities.invokeLater(task);
808 task.start();
809 // Close prompt.
810 }
811 // Done
812 ncd_prompt = null;
813 ncm_prompt = null;
814 }
815 private class CreationTask
816 extends Thread {
817 private NewCollectionDetailsPrompt ncd_prompt = null;
818 private NewCollectionMetadataPrompt ncm_prompt = null;
819 public CreationTask(NewCollectionDetailsPrompt ncd_prompt, NewCollectionMetadataPrompt ncm_prompt) {
820 this.ncd_prompt = ncd_prompt;
821 this.ncm_prompt = ncm_prompt;
822 }
823
824 public void run() {
825 ///ystem.err.println("Running CreationTask...");
826 if(ncm_prompt == null) {
827 Gatherer.c_man.createCollection(ncd_prompt.getDescription(), Gatherer.config.getEmail(), ncd_prompt.getName(), ncd_prompt.getTitle(), ncd_prompt.getBase(), null);
828 }
829 else {
830 Gatherer.c_man.createCollection(ncd_prompt.getDescription(), Gatherer.config.getEmail(), ncd_prompt.getName(), ncd_prompt.getTitle(), null, ncm_prompt.getSets());
831 }
832 // Now that the collection specific settings are loaded we can set the 'view extracted metadata' property
833 // do we want to have this in here???
834 //Gatherer.config.set("general.view_extracted_metadata", Configuration.COLLECTION_SPECIFIC, true);
835 ncd_prompt.dispose();
836 ncd_prompt = null;
837 if(ncm_prompt != null) {
838 ncm_prompt.dispose();
839 ncm_prompt = null;
840 }
841 }
842 }
843
844
845 /** This method is used to open the save collection box/prompt on the screen.
846 * @return A <i>boolean</i> which is <i>true</i> if the collection was saved successfully, <i>false</i> otherwise.
847 */
848 public boolean showSaveCollectionBox(boolean close_after, boolean exit_after) {
849 //SaveCollectionBox save_collection_box = new SaveCollectionBox();
850 //Rectangle bounds = save_collection_box.getBounds();
851 //int result = save_collection_box.getUserOption(Gatherer.c_man.getCollection().getName());
852 //switch(result) {
853 //case SaveCollectionBox.SAVE_YES:
854 Gatherer.c_man.saveCollection(close_after, exit_after);
855 // Wait until it is closed.
856 try {
857 synchronized(this) {
858 while(Gatherer.c_man.reallyReady()) {
859 wait(10);
860 }
861 }
862 }
863 catch(Exception error) {
864 Gatherer.println("Exception: " + error);
865 Gatherer.printStackTrace(error);
866 }
867
868 //content_pane.paintImmediately(bounds);
869 return true;
870 //case SaveCollectionBox.SAVE_NO:
871 // Close collection.
872 // if(close_after) {
873 // tab_pane.setSelectedComponent(collection_pane);
874 // Gatherer.c_man.closeCollection();
875 // }
876 // if(exit_after) {
877 // Gatherer.self.exit();
878 // }
879 // return true;
880 //default:
881 // return false;
882 //}
883 }
884 /** Any implementation of ChangeListener must include this method so we can be informed when the state of one of the registered objects changes. In this case we are listening to view changes within the tabbed pane.
885 * @param event A ChangeEvent containing information about the event that fired this call.
886 */
887 public void stateChanged(ChangeEvent event) {
888 if(previous_pane != null) {
889 if(previous_pane == create_pane) {
890 create_pane.loseFocus();
891 }
892 else if(previous_pane == config_pane) {
893 config_pane.loseFocus();
894 }
895 }
896 // "View assigned metadata" menu item is disabled by default
897 menu_bar.metadata_view.setCanEnable(false);
898 menu_bar.setMetaAuditSuffix(null);
899
900 menu_bar.tabSelected(tab_pane.getSelectedIndex());
901 int selected_index = tab_pane.getSelectedIndex();
902 if( selected_index == tab_pane.indexOfComponent(collection_pane)) {
903 collection_pane.gainFocus();
904 // "View assigned metadata" menu item is enabled for the "Gather" pane
905 menu_bar.metadata_view.setCanEnable(true);
906 }
907 else if(selected_index == tab_pane.indexOfComponent(metaedit_pane)) {
908 metaedit_pane.gainFocus();
909 // "View assigned metadata" menu item is enabled for the "Enrich" pane
910 menu_bar.metadata_view.setCanEnable(true);
911 }
912 else if(selected_index == tab_pane.indexOfComponent(config_pane)) {
913 config_pane.gainFocus();
914 }
915 else if(selected_index == tab_pane.indexOfComponent(create_pane)) {
916 create_pane.gainFocus();
917 }
918 else if(selected_index == tab_pane.indexOfComponent(mirror_pane)) {
919 mirror_pane.gainFocus();
920 }
921 //else if(tab_pane.getSelectedIndex() == tab_pane.indexOfComponent(preview_pane)) {
922 //config_pane.saveConfiguration();
923 //preview_pane.gainFocus();
924 //}
925
926 previous_pane = (JPanel) tab_pane.getSelectedComponent();
927 }
928
929 private MouseListener mouse_blocker_listener = new MouseAdapter() {};
930
931 public void wait(boolean waiting) {
932 Component glass_pane = getGlassPane();
933 if(waiting) {
934 // Show wait cursor.
935 glass_pane.addMouseListener(mouse_blocker_listener);
936 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
937 glass_pane.setVisible(true);
938 }
939 else {
940 // Hide wait cursor.
941 glass_pane.setVisible(false);
942 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
943 glass_pane.removeMouseListener(mouse_blocker_listener);
944 }
945 glass_pane = null;
946 }
947
948 public void workflowUpdate(String raw, boolean state) {
949 WorkflowUpdater task = new WorkflowUpdater(raw, state);
950 SwingUtilities.invokeLater(task);
951 task = null;
952 }
953
954 /** Called to determine if we should wait for a thread to finish before continuing. We wait for threads if they are named: GSHELL_BUILD, GSHELL_IMPORT, or GSHELL_NEW.
955 * @return <i>true</i> if we should wait for a thread, <i>false</i> if it is safe to continue.
956 */
957 private boolean waitForThread() {
958 Thread active[] = new Thread[thread_group.activeCount()];
959 int size = thread_group.enumerate(active);
960 for(int i = 0; i < size; i++) {
961 if(active[i].getName().equals(GShell.GSHELL_BUILD) ||
962 active[i].getName().equals(GShell.GSHELL_IMPORT) ||
963 active[i].getName().equals(GShell.GSHELL_NEW)) {
964 return true;
965 }
966 }
967 return false;
968 }
969
970
971 /**Overridden from JFrame so we can exit safely when window is closed (or destroyed).
972 * @param event A <strong>WindowEvent</strong> containing information about the event that fired this call.
973 */
974 protected void processWindowEvent(WindowEvent event) {
975 if(event.getID() == WindowEvent.WINDOW_CLOSING) {
976 exit();
977 }
978 }
979 /** Listens to actions upon the menu bar, and if it detects a click over the help menu brings the help window to the front if it has become hidden.
980 */
981 private class MenuListenerImpl
982 implements MenuListener {
983 /** Called whenever a popup menu is hidden, but we don't care.
984 * @param e Some <strong>MenuEvent</strong> that we could care less about.
985 */
986 public void menuCanceled(MenuEvent e) {
987 }
988 /** Called whenever a menu header (ie button) becomes unselected, but we don't care.
989 * @param e Some <strong>MenuEvent</strong> that we could care less about.
990 */
991 public void menuDeselected(MenuEvent e) {
992 }
993 /** This method, when a menu is first opened, is the only one we respond to by bringing the help window to the front if possible, but only if there is a help window and the help menu is the one opening.
994 * @param e The <strong>MenuEvent</strong> whose source is checked.
995 */
996 public void menuSelected(MenuEvent e) {
997 if(e.getSource() == menu_bar.help) {
998 if(menu_bar.help.isSelected()) {
999 menu_bar.help.doClick(10);
1000 }
1001 }
1002 }
1003 }
1004 private class TabUpdater
1005 implements Runnable {
1006 private boolean ready = false;
1007 //private int browse_pos = -1;
1008 private int mirror_pos = -1;
1009 private int config_pos = -1;
1010 private int create_pos = -1;
1011 private int export_pos = -1;
1012 private int metaedit_pos = -1;
1013 //private int preview_pos = -1;
1014 private JTabbedPane tab_pane = null;
1015 public TabUpdater(JTabbedPane tab_pane, boolean ready) {
1016 this.ready = ready;
1017 this.tab_pane = tab_pane;
1018 //browse_pos = tab_pane.indexOfComponent(browser_pane);
1019 mirror_pos = tab_pane.indexOfComponent(mirror_pane);
1020 metaedit_pos = tab_pane.indexOfComponent(metaedit_pane);
1021 config_pos = tab_pane.indexOfComponent(config_pane);
1022 export_pos = tab_pane.indexOfComponent(export_pane);
1023 create_pos = tab_pane.indexOfComponent(create_pane);
1024 //preview_pos = tab_pane.indexOfComponent(preview_pane);
1025 }
1026
1027 public void run() {
1028// if(browse_pos != -1) {
1029// if(ready) {
1030// tab_pane.setEnabledAt(browse_pos, Gatherer.config.get("workflow.browse", false));
1031// }
1032// else {
1033// tab_pane.setEnabledAt(browse_pos, Gatherer.config.get("workflow.browse", true));
1034// }
1035// }
1036 if(mirror_pos != -1) {
1037 if(ready) {
1038 tab_pane.setEnabledAt(mirror_pos, Gatherer.config.get("workflow.mirror", false));
1039 }
1040 else {
1041 tab_pane.setEnabledAt(mirror_pos, Gatherer.config.get("workflow.mirror", true));
1042 }
1043 }
1044 if(metaedit_pos != -1) {
1045 tab_pane.setEnabledAt(metaedit_pos, ready && Gatherer.config.get("workflow.enrich", false));
1046 }
1047 if(config_pos != -1) {
1048 tab_pane.setEnabledAt(config_pos, ready && Gatherer.config.get("workflow.design", false) && Gatherer.config.getMode() > Configuration.ASSISTANT_MODE);
1049 }
1050 if(export_pos != -1) {
1051 tab_pane.setEnabledAt(export_pos, ready && Gatherer.config.get("workflow.export", false));
1052 }
1053 if(create_pos != -1) {
1054 tab_pane.setEnabledAt(create_pos, ready && Gatherer.config.get("workflow.create", false));
1055 }
1056// if(preview_pos != -1) {
1057// tab_pane.setEnabledAt(preview_pos, Gatherer.c_man != null && Gatherer.c_man.built() && Gatherer.config.get("workflow.preview", false));
1058// }
1059 }
1060 public void setReady(boolean ready) {
1061 this.ready = ready;
1062 }
1063 }
1064
1065 private class WorkflowUpdater
1066 implements Runnable {
1067 private boolean state;
1068 private String raw;
1069 public WorkflowUpdater(String raw, boolean state) {
1070 this.raw = raw;
1071 this.state = state;
1072 }
1073 public void run() {
1074 setTabEnabled(raw, state);
1075 }
1076 }
1077}
1078
1079
Note: See TracBrowser for help on using the repository browser.