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

Last change on this file since 6823 was 6823, checked in by mdewsnip, 20 years ago

Refreshing trees should not need to do MetaEditPane, since its tree is the same as the one on the Collect pane. This was causing the collection tree to be refreshed twice per action, contributing to the "lightning storm" effect.

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