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

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

Moved some code around in preparation for removing the msm package.

  • Property svn:keywords set to Author Date Id Revision
File size: 38.3 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * <BR><BR>
9 *
10 * Author: John Thompson, Greenstone Digital Library, University of Waikato
11 *
12 * <BR><BR>
13 *
14 * Copyright (C) 1999 New Zealand Digital Library Project
15 *
16 * <BR><BR>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * <BR><BR>
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * <BR><BR>
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 *########################################################################
36 */
37package org.greenstone.gatherer.gui;
38
39import java.awt.*;
40import java.awt.datatransfer.*;
41import java.awt.event.*;
42import java.io.File;
43import java.lang.*;
44import java.net.*;
45import java.util.*;
46import javax.swing.*;
47import javax.swing.event.*;
48import javax.swing.filechooser.*;
49import javax.swing.plaf.*;
50import javax.swing.text.*;
51import org.greenstone.gatherer.Configuration;
52import org.greenstone.gatherer.Dictionary;
53import org.greenstone.gatherer.Gatherer;
54import org.greenstone.gatherer.collection.Collection;
55import org.greenstone.gatherer.collection.DeleteCollectionPrompt;
56import org.greenstone.gatherer.collection.ExportCollectionPrompt;
57import org.greenstone.gatherer.collection.SaveCollectionBox;
58import org.greenstone.gatherer.file.FileNode;
59import org.greenstone.gatherer.file.FileOpenActionListener;
60import org.greenstone.gatherer.gui.AboutDialog;
61import org.greenstone.gatherer.gui.GatherPane;
62import org.greenstone.gatherer.gui.CreatePane;
63import org.greenstone.gatherer.gui.Filter;
64import org.greenstone.gatherer.gui.DesignPane;
65import org.greenstone.gatherer.gui.MenuBar;
66import org.greenstone.gatherer.gui.EnrichPane;
67import org.greenstone.gatherer.gui.MirrorPane;
68import org.greenstone.gatherer.gui.OpenCollectionDialog;
69import org.greenstone.gatherer.gui.Preferences;
70import org.greenstone.gatherer.gui.SimpleOpenCollectionDialog;
71import org.greenstone.gatherer.gui.metaaudit.MetaAuditFrame;
72import org.greenstone.gatherer.gui.tree.DragTree;
73import org.greenstone.gatherer.gui.tree.WorkspaceTree;
74import org.greenstone.gatherer.help.HelpFrame;
75import org.greenstone.gatherer.mem.MetadataEditorManager;
76import org.greenstone.gatherer.shell.GShell;
77import org.greenstone.gatherer.util.StaticStrings;
78import org.greenstone.gatherer.util.TreeSynchronizer;
79import org.greenstone.gatherer.util.Utility;
80
81/** 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. */
82public class GUIManager
83 extends JFrame
84 implements ActionListener, ChangeListener {
85 /** The mirror pane contains controls for mirroring internet sites. */
86 public MirrorPane mirror_pane = null;
87 /** The gather pane is more like a file manager where you drag files from one tree to another. */
88 private GatherPane gather_pane = null;
89 /** The enrich pane is used to assign, edit and remove metadata from files within the collection. */
90 public EnrichPane enrich_pane = null;
91 /** The design pane allows you to edit the design of the library in terms of the collection configuration file. */
92 public DesignPane design_pane = null;
93 /** The create pane contains scripting options for importing and building collections into libraries. */
94 public CreatePane create_pane = null;
95
96 public FileOpenActionListener foa_listener = new FileOpenActionListener();
97
98 /** A reference to the currently instantiated help window, if any. */
99 public HelpFrame help = null;
100 /** The menu bar. */
101 public MenuBar menu_bar = null;
102 public MetaAuditFrame meta_audit;
103 /** Are certain panes currently locked? */
104 private boolean locked = false;
105 /** The size of the Gatherer window. */
106 private Dimension size = null;
107 /** The filters used to dynamically filter the trees at user request. */
108 private HashMap filters = new HashMap();
109 /** The panel within the window that other components are placed on. */
110 private JPanel content_pane = null;
111 /** The dummy export pane. */
112 private JPanel export_pane = new JPanel();
113 /** The last view pane selected. */
114 private JPanel previous_pane;
115 /** The main tab pane containing the different views, available here to trap view change events. */
116 private JTabbedPane tab_pane = null;
117 /** A threaded tab changer to try and avoid NPE on exit. */
118 private TabUpdater tab_updater = null;
119 /** 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.
120 * @see org.greenstone.gatherer.Dictionary
121 */
122 private ThreadGroup thread_group = null;
123 /** Ensures that expansion events between like collection trees are synchronized. */
124 private TreeSynchronizer collection_tree_sync = null;
125 /** Ensures that expansion events between like workspace trees are synchronized. */
126 private TreeSynchronizer workspace_tree_sync = null;
127 /**Constructor. Enable window events and arranges all other components.
128 * @param size The intial <strong>Dimension</strong> of the screen.
129 */
130 public GUIManager(Dimension size) {
131 super();
132 // Initialization
133 this.help = new HelpFrame();
134 this.size = size;
135 this.collection_tree_sync = new TreeSynchronizer();
136 this.meta_audit = new MetaAuditFrame(collection_tree_sync, null);
137 this.workspace_tree_sync = new TreeSynchronizer();
138
139 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
140
141 // 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.
142 this.addFocusListener(new GLIGUIFocusListener());
143
144 // Make the Tool tip hang around for a rediculous amount of time.
145 ToolTipManager.sharedInstance().setDismissDelay(10000);
146 // 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.
147 Thread bob = new Thread();
148 thread_group = bob.getThreadGroup();
149 // Set up some other UI stuff. (fonts handled in Gatherer.main())
150 UIManager.put("FileChooser.lookInLabelText", Dictionary.get("SaveCollectionBox.Look_In"));
151 UIManager.put("FileChooser.filesOfTypeLabelText", Dictionary.get("SaveCollectionBox.Files_Of_Type"));
152 UIManager.put("FileChooser.fileNameLabelText", Dictionary.get("SaveCollectionBox.File_Name"));
153 }
154
155 private class GLIGUIFocusListener
156 extends FocusAdapter {
157 public void focusGained(FocusEvent e) {
158 if(Gatherer.current_modal != null) {
159 Gatherer.current_modal.makeVisible();
160 Gatherer.current_modal.toFront();
161 }
162 }
163 }
164
165 /** 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.
166 * @param event An <strong>ActionEvent</strong> containing information about the action that has occured.
167 */
168 public void actionPerformed(ActionEvent event) {
169 boolean cont = true;
170 Object esrc = event.getSource();
171 // *************
172 // File Options.
173 // *************
174 if(esrc == menu_bar.file_associations) {
175 Gatherer.assoc_man.edit();
176 }
177 else if(esrc == menu_bar.file_close) {
178 // if(!Gatherer.c_man.saved()) {
179 cont = showSaveCollectionBox(true, false);
180 // }
181 if(cont) {
182 tab_pane.setSelectedComponent(gather_pane);
183 }
184 }
185 else if(esrc == menu_bar.file_delete) {
186 DeleteCollectionPrompt dcp = new DeleteCollectionPrompt();
187 if(dcp.display()) {
188 Gatherer.c_man.closeCollection();
189 }
190 dcp.destroy();
191 dcp = null;
192 System.gc();
193 }
194 else if(esrc == menu_bar.file_export) {
195 ExportCollectionPrompt ecp = new ExportCollectionPrompt();
196 ecp.display();
197 ecp.destroy();
198 ecp = null;
199 }
200 else if(esrc == menu_bar.file_exit) {
201 //menu_bar.exit();
202 System.err.println("*** menu bar file exit called");
203 exit();
204 }
205 else if(esrc == menu_bar.file_new) {
206 showNewCollectionPrompt();
207 }
208 else if(esrc == menu_bar.file_open) {
209 if (showLoadCollectionBox()) {
210 tab_pane.setSelectedComponent(gather_pane);
211 }
212 }
213 else if(esrc == menu_bar.file_options) {
214 // Just incase the user has edited the GeneralSettings of a collection without losing focus afterwards. Well I'm forever losing foc... ooh shiney.
215 design_pane.loseFocus();
216 // And spawn a new preferences.
217 new Preferences();
218 }
219 else if(esrc == menu_bar.file_save) {
220 Gatherer.c_man.saveCollection(false, false);
221 }
222// else if(esrc == menu_bar.file_save_as) {
223// String name = JOptionPane.showInputDialog(this, "Enter new collection filename.");
224// if(name != null) {
225// Gatherer.c_man.saveCollectionAs(name);
226// }
227// }
228 // *************
229 // Edit Options.
230 // *************
231 else if(esrc == menu_bar.edit_copy) {
232 try {
233 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
234 // Get the component with selected text as a JTextComponent
235 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();//getFocusOwner();
236 text.copy();
237 }
238 catch (Exception cce) {
239 // If the component is not a text component ignore the copy command
240 Gatherer.println(cce.toString());
241 }
242 }
243 else if(esrc == menu_bar.edit_cut) {
244 try {
245 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
246 // Get the component with selected text as a JTextComponent
247 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
248 // Cut the text to the clipboard
249 text.cut();
250 }
251 catch (ClassCastException cce) {
252 // If the component is not a text component ignore the cut command
253 Gatherer.println(cce.toString());
254 }
255 }
256 else if(esrc == menu_bar.edit_paste) {
257 try {
258 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
259 // Get the component with selected text as a JTextComponent
260 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
261 // Cut the text to the clipboard
262 text.paste();
263 }
264 catch (ClassCastException cce) {
265 // If the component is not a text component ignore the paste command
266 Gatherer.println(cce.toString());
267 }
268 }
269// else if(esrc == menu_bar.edit_search) {
270// if(sar != null) {
271// sar.dispose();
272// }
273// sar = new SearchAndReplace(false);
274// }
275// else if(esrc == menu_bar.edit_replace) {
276// if(sar != null) {
277// sar.dispose();
278// }
279// sar = new SearchAndReplace(true);
280// }
281
282 // *************
283 // Help Options.
284 // *************
285 else if(esrc == menu_bar.help_about) {
286 new AboutDialog(this);
287 }
288 else if(esrc == menu_bar.help_build) {
289 help.setView("producingthecollection");
290 }
291 else if(esrc == menu_bar.help_collect) {
292 help.setView("collectingfiles");
293 }
294 else if(esrc == menu_bar.help_design) {
295 help.setView("designingacollection");
296 }
297 else if(esrc == menu_bar.help_general) {
298 help.setView("introduction");
299 }
300 else if(esrc == menu_bar.help_metaedit) {
301 help.setView("enrichingacollection");
302 }
303 else if(esrc == menu_bar.help_mirror) {
304 help.setView("downloadingfiles");
305 }
306 else if(esrc == menu_bar.help_preview) {
307 help.setView("previewingthecollection");
308 }
309
310 // *****************
311 // Metadata Options.
312 // *****************
313 else if (esrc == menu_bar.metadata_import) {
314 JFileChooser chooser = new JFileChooser(new File(Utility.METADATA_DIR));
315 MDSFileFilter filter = new MDSFileFilter();
316 chooser.setFileFilter(filter);
317 int returnVal = chooser.showDialog(Gatherer.g_man, Dictionary.get("MSMPrompt.File_Import"));
318 if (returnVal == JFileChooser.APPROVE_OPTION) {
319 Gatherer.c_man.getCollection().msm.importMDS(chooser.getSelectedFile(), true);
320 }
321 }
322 else if(esrc == menu_bar.metadata_edit) {
323 showEditMetadataBox();
324 }
325 else if(esrc == menu_bar.metadata_export) {
326 Gatherer.c_man.getCollection().msm.exportMDS();
327 }
328 else if(esrc == menu_bar.metadata_view) {
329 showMetaAuditBox();
330 }
331 }
332 /** 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.
333 */
334 public void afterDisplay() {
335 if (mirror_pane != null) {
336 mirror_pane.afterDisplay();
337 }
338 enrich_pane.afterDisplay();
339 }
340 public void closeCurrentCollection() {
341
342 boolean cont = showSaveCollectionBox(true, false);
343
344 if(cont) {
345 tab_pane.setSelectedComponent(gather_pane);
346 }
347
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 gather_pane.collectionChanged(ready); // Used to update the collection workspace.
358 enrich_pane.collectionChanged(ready); // Very important that metaedit pane shows current collection.
359 design_pane.collectionChanged(ready);
360 create_pane.collectionChanged(ready); // Used to indicate a new BuildOptions model should be loaded.
361
362 if(!locked) {
363 // Now enable tabs as necessary. Do this on event queue to prevent crazy NPEs
364 if(tab_updater == null) {
365 tab_updater = new TabUpdater(tab_pane, ready);
366 }
367 else {
368 tab_updater.setReady(ready);
369 }
370 SwingUtilities.invokeLater(tab_updater);
371 }
372 // Set the title
373 String collection_title = null;
374 String collection_name = null;
375 if (ready) {
376 Collection collection = Gatherer.c_man.getCollection();
377 collection_title = collection.getTitle();
378 collection_name = collection.getName();
379 collection = null;
380 }
381 setTitle(collection_title, collection_name);
382 collection_title = null;
383 collection_name = null;
384 // Now is a good time to force a garbage collect.
385 ///ystem.err.println("Calling garbage collection.");
386 System.gc();
387 }
388
389 public void destroy() {
390 // Destroying create pane ensures the latest log has been closed
391 if(create_pane != null) {
392 create_pane.destroy();
393 }
394 }
395
396 /** Enabled events on the window to be trapped, creates all the visual components, then builds the tab and other layouts.
397 */
398 public void display() {
399 content_pane = (JPanel) this.getContentPane();
400 // Enable window-type events to be fired.
401 enableEvents(AWTEvent.WINDOW_EVENT_MASK);
402 // Initialise and layout sub-components, plus other window dressing.
403 try {
404 this.setSize(size);
405
406 // Set the title
407 String collection_title = null;
408 String collection_name = null;
409 if (Gatherer.c_man.ready()) {
410 Collection collection = Gatherer.c_man.getCollection();
411 collection_title = collection.getTitle();
412 collection_name = collection.getName();
413 collection = null;
414 }
415 setTitle(collection_title, collection_name);
416 collection_title = null;
417 collection_name = null;
418
419 // Pretty corner icon
420 this.setIconImage(Utility.getImage("gatherer_small.gif").getImage());
421 // BorderLayout for the main screen. I'll try my best to avoid these in subcomponents as they're space greedy.
422 content_pane.setLayout(new BorderLayout());
423 // Create the menu-bar and stick it up the top.
424 menu_bar = new MenuBar(new MenuListenerImpl());
425
426 //feedback changes
427 //content_pane.add(menu_bar, BorderLayout.NORTH);
428 this.setJMenuBar(menu_bar);
429 // end feedback changes
430
431 // Create the tabbed pane and plop it in the center where it will
432 // expand to consume all available space like any good gas would.
433 tab_pane = new JTabbedPane();
434 tab_pane.addChangeListener(this);
435 tab_pane.setFont(Gatherer.config.getFont("general.font", false));
436
437 if(Gatherer.config.get(StaticStrings.WORKFLOW_MIRROR, true)) {
438 mirror_pane = new MirrorPane();
439 tab_pane.addTab("GUI.Mirror", Utility.getImage("mirroring.gif"), mirror_pane);
440 tab_pane.setEnabledAt(tab_pane.indexOfComponent(mirror_pane), Gatherer.config.get(StaticStrings.WORKFLOW_MIRROR, false));
441 }
442
443 gather_pane = new GatherPane(workspace_tree_sync, collection_tree_sync);
444 gather_pane.display();
445 if(Gatherer.config.get("workflow.gather", true)) {
446 tab_pane.addTab("GUI.Gather", Utility.getImage("collection.gif"), gather_pane);
447 tab_pane.setEnabledAt(tab_pane.indexOfComponent(gather_pane), Gatherer.config.get("workflow.gather", false));
448 }
449
450 enrich_pane = new EnrichPane(collection_tree_sync);
451 enrich_pane.display();
452 if(Gatherer.config.get("workflow.enrich", true)) {
453 tab_pane.addTab("GUI.Enrich", Utility.getImage("metaedit.gif"), enrich_pane);
454 tab_pane.setEnabledAt(tab_pane.indexOfComponent(enrich_pane), false);
455 }
456
457 design_pane = new DesignPane();
458 design_pane.display();
459 if(Gatherer.config.get("workflow.design", true)) {
460 tab_pane.addTab("GUI.Design", Utility.getImage("build.gif"), design_pane);
461 tab_pane.setEnabledAt(tab_pane.indexOfComponent(design_pane), false);
462 }
463
464 if(Gatherer.config.get("workflow.export", true)) {
465 tab_pane.addTab("GUI.Export", Utility.getImage("export.gif"), export_pane);
466 tab_pane.setEnabledAt(tab_pane.indexOfComponent(export_pane), false);
467 }
468
469 create_pane = new CreatePane();
470 create_pane.display();
471 if(Gatherer.config.get("workflow.create", true)) {
472 tab_pane.addTab("GUI.Create", Utility.getImage("build session.gif"), create_pane);
473 tab_pane.setEnabledAt(tab_pane.indexOfComponent(create_pane), false);
474 }
475
476 // Select the collect pane if it is available
477 if(Gatherer.config.get("workflow.gather", false)) {
478 tab_pane.setSelectedComponent(gather_pane);
479 }
480 // Otherwise find the first tab that is enabled and select that.
481 else {
482 boolean found = false;
483 for(int i = 0; !found && i < tab_pane.getTabCount(); i++) {
484 if(tab_pane.isEnabledAt(i)) {
485 tab_pane.setSelectedIndex(i);
486 found = true;
487 }
488 }
489 }
490
491 Dictionary.register(tab_pane);
492 content_pane.add(tab_pane, BorderLayout.CENTER);
493 // Drive a sessionReady event to update all controls to reflect current collection status.
494 collectionChanged(Gatherer.c_man.ready());
495 }
496 catch (Exception e) {
497 Gatherer.printStackTrace(e);
498 // The GUI failing to build is an app killer
499 e.printStackTrace();
500 System.exit(1);
501 }
502 }
503 /** 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.
504 */
505 public void exit() {
506 // Tell everyone who cares that they are losing focus
507 System.err.println("**** GUIManager exit called!");
508
509 if(!Gatherer.c_man.ready() || design_pane.canSave()) {
510
511 if (Gatherer.isGsdlRemote) {
512 // consider saving???
513 System.err.println("**** Hiding GUI panel");
514 hide();
515 }
516 else {
517
518 boolean cont = true;
519 if(Gatherer.c_man.ready() && !Gatherer.c_man.saved()) {
520 System.err.println("***** c_man not saved");
521 cont = showSaveCollectionBox(false, true);
522 }
523 else {
524 // Deal to help
525 if(help != null) {
526 help.destroy();
527 help = null;
528 }
529 System.err.println("**** Calling Gatherer.self.exit");
530 Gatherer.self.exit();
531 }
532 }
533 }
534 }
535 /** Retrieve the filter, or if one already exists, spawn a linked copy. */
536 public Filter getFilter(DragTree tree) {
537 Filter filter = (Filter) filters.get(tree.getModel());
538 if (filter == null) {
539 filter = new Filter(tree, null);
540 filters.put(tree.getModel(), filter);
541 return filter;
542 }
543 return filter.spawn(tree);
544 }
545
546// public Component getSelectedView() {
547// return tab_pane.getSelectedComponent();
548// }
549 /** 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.
550 */
551 public void lockCollection(boolean import_stage, boolean lock) {
552 locked = lock;
553 if(import_stage) {
554 int collection_pos = tab_pane.indexOfComponent(gather_pane);
555 int metaedit_pos = tab_pane.indexOfComponent(enrich_pane);
556 int config_pos = tab_pane.indexOfComponent(design_pane);
557 tab_pane.setEnabledAt(collection_pos, !lock);
558 tab_pane.setEnabledAt(metaedit_pos, !lock);
559 tab_pane.setEnabledAt(config_pos, !lock);
560 }
561 else {
562 int config_pos = tab_pane.indexOfComponent(design_pane);
563 tab_pane.setEnabledAt(config_pos, !lock);
564 }
565 }
566
567 public void modeChanged(int mode) {
568 // Set the title
569 String collection_title = null;
570 String collection_name = null;
571 if (Gatherer.c_man.ready()) {
572 Collection collection = Gatherer.c_man.getCollection();
573 collection_title = collection.getTitle();
574 collection_name = collection.getName();
575 collection = null;
576 }
577 setTitle(collection_title, collection_name);
578 collection_title = null;
579 collection_name = null;
580 // Now pass on the message to anyone who cares
581 if(gather_pane != null) {
582 gather_pane.modeChanged(mode);
583 }
584 if(design_pane != null) {
585 design_pane.modeChanged(mode);
586 }
587 if(create_pane != null) {
588 create_pane.modeChanged(mode);
589 }
590 if(enrich_pane != null) {
591 enrich_pane.modeChanged(mode);
592 }
593 if (mirror_pane != null) {
594 mirror_pane.modeChanged(mode);
595 }
596 }
597
598
599 public void refreshCollectionTree(int refresh_reason)
600 {
601 if (gather_pane != null) {
602 gather_pane.refreshCollectionTree(refresh_reason);
603 }
604 }
605
606
607 public void refreshWorkspaceTree(int refresh_reason)
608 {
609 if (gather_pane != null) {
610 gather_pane.refreshWorkspaceTree(refresh_reason);
611 }
612 }
613
614
615 /** Returns to some "initial" pane (when no collection is loaded) */
616 public void returnToInitialPane()
617 {
618 if (gather_pane != null) {
619 tab_pane.setSelectedComponent(gather_pane);
620 }
621 }
622
623
624 /** Specifies whether a certain tab is enabled or not. */
625 private void setTabEnabled(String rawname, boolean state) {
626 // Retrieve the dictionary based name.
627 String name = Dictionary.get("GUI." + rawname);
628 int index = tab_pane.indexOfTab(name);
629 // Of course we may not have this tab available.
630 if(index != -1) {
631 // Some tabs are also dependant on if a collection is ready
632 Component component = tab_pane.getComponentAt(index);
633 if(component == enrich_pane || component == design_pane || component == export_pane || component == create_pane) {
634 tab_pane.setEnabledAt(index, state && Gatherer.c_man != null && Gatherer.c_man.ready());
635 }
636 else {
637 tab_pane.setEnabledAt(index, state);
638 }
639 // If this was the currently selected tab and it is now disabled, change the view to the first enabled tab.
640 if(tab_pane.getSelectedIndex() == index && !state) {
641 boolean found = false;
642 for(int i = 0; !found && i < tab_pane.getTabCount(); i++) {
643 if(tab_pane.isEnabledAt(i)) {
644 tab_pane.setSelectedIndex(i);
645 found = true;
646 }
647 }
648 // If there are no tabs enabled, which should be impossible, then select the first tab
649 if(!found) {
650 tab_pane.setSelectedIndex(0);
651 }
652 }
653 }
654 // If the rawname was mirror then rebuild workspace tree to remove caches.
655 if (rawname.equals("Mirror")) {
656 // gather_pane.refreshWorkspaceTree(WorkspaceTree.FOLDER_SHORTCUTS_CHANGED);
657 }
658 }
659
660 /** 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.
661 * @param title
662 * @param name
663 */
664 public void setTitle(String title, String name) {
665 // Finally display the collection name in the title bar.
666 StringBuffer title_buffer = new StringBuffer(Utility.PROGRAM_NAME);
667 title_buffer.append(StaticStrings.SPACE_CHARACTER);
668 title_buffer.append(StaticStrings.SPACE_CHARACTER);
669 // Describe the current user mode
670 title_buffer.append(StaticStrings.MODE_STR);
671 title_buffer.append(Gatherer.config.getModeAsString());
672 title_buffer.append(StaticStrings.SPACE_CHARACTER);
673 title_buffer.append(StaticStrings.SPACE_CHARACTER);
674 // Now for the current collection
675 title_buffer.append(StaticStrings.COLLECTION_STR);
676 if (title != null && name != null) {
677 title_buffer.append(title);
678 title_buffer.append(StaticStrings.SPACE_CHARACTER);
679 title_buffer.append(StaticStrings.OPEN_PARENTHESIS_CHARACTER);
680 title_buffer.append(name);
681 title_buffer.append(StaticStrings.CLOSE_PARENTHESIS_CHARACTER);
682 }
683 else {
684 title_buffer.append(Dictionary.get("Collection.No_Collection"));
685 }
686 this.setTitle(title_buffer.toString());
687 title_buffer = null;
688 }
689
690 /** 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.
691 */
692 private void showEditMetadataBox() {
693 if(Gatherer.c_man.getCollection() != null) {
694 Gatherer.c_man.getCollection().msm.editMDS(null, MetadataEditorManager.NORMAL);
695 }
696 }
697 /** When the load collection option is choosen this method is called to produce the modal file load prompt.
698 */
699 private boolean showLoadCollectionBox() {
700 boolean result = false;
701 // We first try the simple open collection dialog
702 SimpleOpenCollectionDialog dialog = new SimpleOpenCollectionDialog();
703 int user_choice = dialog.display();
704 String filename = null;
705 // The user may choose to go to the advanced 'browse' dialog
706 if(user_choice == SimpleOpenCollectionDialog.OK_OPTION) {
707 filename = dialog.getFileName();
708 }
709 else if(user_choice == SimpleOpenCollectionDialog.BROWSE_OPTION) {
710 File file;
711 if (Gatherer.GS3) {
712 if (Gatherer.config.gsdl3_path != null) {
713 file = new File(Utility.getSitesDir(Gatherer.config.gsdl3_path));
714 } else {
715 file = new File(Utility.BASE_DIR);
716 }
717
718 } else {
719
720 if(Gatherer.config.gsdl_path != null) {
721 file = new File(Utility.getCollectDir(Gatherer.config.gsdl_path));
722 }
723 else {
724 file = new File(Utility.BASE_DIR);
725 }
726 }
727 OpenCollectionDialog chooser = new OpenCollectionDialog(file);
728 file = null;
729 filename = chooser.getFileName();
730 chooser.destroy();
731 chooser = null;
732 }
733 dialog.destroy();
734 dialog = null;
735 // User can cancel action.
736 if(filename != null) {
737 // If there is already a collection open, save and close it.
738 if(Gatherer.c_man.ready()) {
739 showSaveCollectionBox(true, false);
740 // Wait until it is closed.
741 try {
742 synchronized(this) {
743 while(Gatherer.c_man.reallyReady()) {
744 wait(10);
745 }
746 }
747 }
748 catch(Exception error) {
749 Gatherer.println("Exception: " + error);
750 Gatherer.printStackTrace(error);
751 }
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 private 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 private 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(gather_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 == design_pane) {
887 design_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(gather_pane)) {
897 gather_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(enrich_pane)) {
902 enrich_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(design_pane)) {
907 design_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
916 previous_pane = (JPanel) tab_pane.getSelectedComponent();
917 }
918
919 private MouseListener mouse_blocker_listener = new MouseAdapter() {};
920
921 public void wait(boolean waiting) {
922 Component glass_pane = getGlassPane();
923 if(waiting) {
924 // Show wait cursor.
925 glass_pane.addMouseListener(mouse_blocker_listener);
926 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
927 glass_pane.setVisible(true);
928 }
929 else {
930 // Hide wait cursor.
931 glass_pane.setVisible(false);
932 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
933 glass_pane.removeMouseListener(mouse_blocker_listener);
934 }
935 glass_pane = null;
936 }
937
938 public void workflowUpdate(String raw, boolean state) {
939 WorkflowUpdater task = new WorkflowUpdater(raw, state);
940 SwingUtilities.invokeLater(task);
941 task = null;
942 }
943
944 /** 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.
945 * @return <i>true</i> if we should wait for a thread, <i>false</i> if it is safe to continue.
946 */
947 private boolean waitForThread() {
948 Thread active[] = new Thread[thread_group.activeCount()];
949 int size = thread_group.enumerate(active);
950 for(int i = 0; i < size; i++) {
951 if(active[i].getName().equals(GShell.GSHELL_BUILD) ||
952 active[i].getName().equals(GShell.GSHELL_IMPORT) ||
953 active[i].getName().equals(GShell.GSHELL_NEW)) {
954 return true;
955 }
956 }
957 return false;
958 }
959
960
961 /**Overridden from JFrame so we can exit safely when window is closed (or destroyed).
962 * @param event A <strong>WindowEvent</strong> containing information about the event that fired this call.
963 */
964 protected void processWindowEvent(WindowEvent event) {
965 if(event.getID() == WindowEvent.WINDOW_CLOSING) {
966 System.err.println("***** Processing Window Event ... closing");
967 exit();
968 }
969 }
970
971
972 private class MDSFileFilter
973 extends FileFilter
974 {
975 /** Override this method to return <i>true</i> only if the file extension is .mds.
976 * @param f A possible mds <strong>File</strong>.
977 * @return <i>true</i> if we should show this file, <i>false</i> otherwise.
978 */
979 public boolean accept(File f)
980 {
981 String file_name = f.getName().toLowerCase();
982 if (file_name.endsWith(".mds") || file_name.indexOf(".") == -1) {
983 return true;
984 }
985 return false;
986 }
987
988 /** Retrieve the description for this filter.
989 * @return The description as a <strong>String</strong>.
990 */
991 public String getDescription()
992 {
993 return Dictionary.get("MSMPrompt.File_Filter_Description");
994 }
995 }
996
997
998 /** 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.
999 */
1000 private class MenuListenerImpl
1001 implements MenuListener {
1002 /** Called whenever a popup menu is hidden, but we don't care.
1003 * @param e Some <strong>MenuEvent</strong> that we could care less about.
1004 */
1005 public void menuCanceled(MenuEvent e) {
1006 }
1007 /** Called whenever a menu header (ie button) becomes unselected, but we don't care.
1008 * @param e Some <strong>MenuEvent</strong> that we could care less about.
1009 */
1010 public void menuDeselected(MenuEvent e) {
1011 }
1012 /** 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.
1013 * @param e The <strong>MenuEvent</strong> whose source is checked.
1014 */
1015 public void menuSelected(MenuEvent e) {
1016 if(e.getSource() == menu_bar.help) {
1017 if(menu_bar.help.isSelected()) {
1018 menu_bar.help.doClick(10);
1019 }
1020 }
1021 }
1022 }
1023
1024 private class TabUpdater
1025 implements Runnable {
1026 private boolean ready = false;
1027 private int mirror_pos = -1;
1028 private int enrich_pos = -1;
1029 private int design_pos = -1;
1030 private int create_pos = -1;
1031 private int export_pos = -1;
1032 private JTabbedPane tab_pane = null;
1033
1034 public TabUpdater(JTabbedPane tab_pane, boolean ready) {
1035 this.ready = ready;
1036 this.tab_pane = tab_pane;
1037 mirror_pos = tab_pane.indexOfComponent(mirror_pane);
1038 enrich_pos = tab_pane.indexOfComponent(enrich_pane);
1039 design_pos = tab_pane.indexOfComponent(design_pane);
1040 create_pos = tab_pane.indexOfComponent(create_pane);
1041 export_pos = tab_pane.indexOfComponent(export_pane);
1042 }
1043
1044 public void run()
1045 {
1046 if (mirror_pos != -1) {
1047 if (ready) {
1048 tab_pane.setEnabledAt(mirror_pos, Gatherer.config.get("workflow.mirror", false));
1049 }
1050 else {
1051 tab_pane.setEnabledAt(mirror_pos, Gatherer.config.get("workflow.mirror", true));
1052 }
1053 }
1054 if (enrich_pos != -1) {
1055 tab_pane.setEnabledAt(enrich_pos, ready && Gatherer.config.get("workflow.enrich", false));
1056 }
1057 if (design_pos != -1) {
1058 tab_pane.setEnabledAt(design_pos, ready && Gatherer.config.get("workflow.design", false) && Gatherer.config.getMode() > Configuration.ASSISTANT_MODE);
1059 }
1060 if (create_pos != -1) {
1061 tab_pane.setEnabledAt(create_pos, ready && Gatherer.config.get("workflow.create", false));
1062 }
1063 if (export_pos != -1) {
1064 tab_pane.setEnabledAt(export_pos, ready && Gatherer.config.get("workflow.export", false));
1065 }
1066 }
1067
1068 public void setReady(boolean ready) {
1069 this.ready = ready;
1070 }
1071 }
1072
1073 private class WorkflowUpdater
1074 implements Runnable {
1075 private boolean state;
1076 private String raw;
1077 public WorkflowUpdater(String raw, boolean state) {
1078 this.raw = raw;
1079 this.state = state;
1080 }
1081 public void run() {
1082 setTabEnabled(raw, state);
1083 }
1084 }
1085}
1086
1087
Note: See TracBrowser for help on using the repository browser.