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

Last change on this file since 5564 was 5564, checked in by mdewsnip, 21 years ago

Many more small improvements and tooltips added. Still more to come!

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