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

Last change on this file since 32699 was 32699, checked in by ak19, 5 years ago

This fixes another EDT (Event Dispatch Thread) access violation instance where the opening a collection dialog despite being GUI stuff was not done in the Swing EDT thread. The AssertJ Swing test I'm working on at present detected this and fixing it here got me through the test as well.

  • Property svn:keywords set to Author Date Id Revision
File size: 43.4 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.DebugStream;
53import org.greenstone.gatherer.Dictionary;
54import org.greenstone.gatherer.Gatherer;
55import org.greenstone.gatherer.collection.Collection;
56import org.greenstone.gatherer.collection.CollectionManager;
57import org.greenstone.gatherer.file.FileOpenActionListener;
58import org.greenstone.gatherer.file.WorkspaceTree;
59import org.greenstone.gatherer.gui.metaaudit.MetaAuditFrame;
60import org.greenstone.gatherer.gui.tree.DragTree;
61import org.greenstone.gatherer.metadata.FilenameEncoding;
62import org.greenstone.gatherer.metadata.MetadataSet;
63import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
64import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
65import org.greenstone.gatherer.util.JarTools;
66import org.greenstone.gatherer.util.StaticStrings;
67import org.greenstone.gatherer.util.Utility;
68
69
70/** 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. */
71public class GUIManager
72 extends JFrame
73 implements ActionListener, ChangeListener, WindowFocusListener{
74 /** The download pane contains controls for downloading internet sites. */
75 public DownloadPane download_pane = null;
76 /** The gather pane is more like a file manager where you drag files from one tree to another. */
77 public GatherPane gather_pane = null;
78 /** The enrich pane is used to assign, edit and remove metadata from files within the collection. */
79 public EnrichPane enrich_pane = null;
80 /** The design pane allows you to edit the design of the library in terms of the collection configuration file. - the stuff that requires rebuilding */
81 public DesignPane design_pane = null;
82 /** The create pane contains scripting options for importing and building collections into libraries. */
83 public CreatePane create_pane = null;
84 /** The format pane allows you to edit the design of the library in terms of the collection configuration file. - the stuff that doesn't require rebuilding */
85 public FormatPane format_pane = null;
86
87 public FileOpenActionListener foa_listener = new FileOpenActionListener();
88
89
90 /** A reference to the currently instantiated help window, if any. */
91 private HelpFrame help = null;
92 /** The menu bar. */
93 public MenuBar menu_bar = null;
94 public MetaAuditFrame meta_audit;
95 /** Are certain panes currently locked? */
96 private boolean locked = false;
97 /** The size of the Gatherer window. */
98 private Dimension size = null;
99 /** The panel within the window that other components are placed on. */
100 private JPanel content_pane = null;
101 /** The last view pane selected. */
102 private JPanel previous_pane;
103 /** The main tab pane containing the different views, available here to trap view change events. */
104 private JTabbedPane tab_pane = null;
105 /** A threaded tab changer to try and avoid NPE on exit. */
106 private TabUpdater tab_updater = null;
107
108 final static String newline = "\n";
109 final static String space = " ";
110
111
112 /**Constructor. Enable window events and arranges all other components.
113 * @param size The intial <strong>Dimension</strong> of the screen.
114 */
115 public GUIManager(Dimension size) {
116 super();
117 this.setComponentOrientation(Dictionary.getOrientation());
118 // Initialization
119 this.help = new HelpFrame();
120 this.size = size;
121
122 this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
123
124 // 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.
125 this.addFocusListener(new GLIGUIFocusListener());
126
127 this.addWindowFocusListener(this);
128
129 // Make the Tool tip hang around for a rediculous amount of time.
130 ToolTipManager.sharedInstance().setDismissDelay(10000);
131
132 // Set up some other UI stuff. (fonts handled in Gatherer.main())
133 UIManager.put("FileChooser.lookInLabelText", Dictionary.get("SaveCollectionBox.Look_In"));
134 UIManager.put("FileChooser.filesOfTypeLabelText", Dictionary.get("SaveCollectionBox.Files_Of_Type"));
135 UIManager.put("FileChooser.fileNameLabelText", Dictionary.get("SaveCollectionBox.File_Name"));
136
137 // JOptionPane options
138 // http://www.java2s.com/Tutorial/Java/0240__Swing/SettingJOptionPanebuttonlabelstoFrench.htm
139 UIManager.put("OptionPane.cancelButtonText", Dictionary.get("General.Cancel"));
140 UIManager.put("OptionPane.noButtonText", Dictionary.get("General.No"));
141 UIManager.put("OptionPane.okButtonText", Dictionary.get("General.OK"));
142 UIManager.put("OptionPane.yesButtonText", Dictionary.get("General.Yes"));
143 }
144
145
146 public void windowGainedFocus(WindowEvent e)
147 {
148 }
149
150
151 public void windowLostFocus(WindowEvent e)
152 {
153 // Save the loaded collection
154 if (Gatherer.c_man != null && Gatherer.c_man.ready() && Gatherer.c_man.getCollection().cdm != null) {
155 Gatherer.c_man.saveCollection();
156 }
157 }
158
159
160 private class GLIGUIFocusListener
161 extends FocusAdapter {
162 public void focusGained(FocusEvent e) {
163 if (ModalDialog.current_modal != null) {
164 ModalDialog.current_modal.makeVisible();
165 ModalDialog.current_modal.toFront();
166 }
167 }
168 }
169
170 /** 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.
171 * @param event An <strong>ActionEvent</strong> containing information about the action that has occured.
172 */
173 public void actionPerformed(ActionEvent event) {
174 Object esrc = event.getSource();
175 // *************
176 // File Options.
177 // *************
178 if (esrc == menu_bar.file_associations) {
179 Gatherer.assoc_man.edit();
180 }
181 else if (esrc == menu_bar.file_close) {
182 saveThenCloseCurrentCollection();
183 }
184 else if (esrc == menu_bar.file_delete) {
185 new DeleteCollectionTask().start();
186 }
187 else if (esrc == menu_bar.file_cdimage) {
188 WriteCDImagePrompt wcdip = new WriteCDImagePrompt();
189 wcdip.display();
190 wcdip.destroy();
191 wcdip = null;
192 }
193 else if (esrc == menu_bar.file_exportas) {
194 ExportAsPrompt eap = new ExportAsPrompt();
195 eap.display();
196 eap.destroy();
197 eap = null;
198 }
199 else if (esrc == menu_bar.file_exit) {
200 exit();
201 }
202 else if (esrc == menu_bar.file_new) {
203 new NewCollectionTask().start();
204 }
205 else if (esrc == menu_bar.file_open) {
206 //new OpenCollectionTask().start(); // will cause an EDT access violation exception
207 // since the GUI stuff of opening a collection is not done in a Swing thread
208 SwingUtilities.invokeLater(new OpenCollectionTask());
209 }
210 else if (esrc == menu_bar.file_options) {
211 new Preferences();
212 }
213 else if (esrc == menu_bar.file_save) {
214 // Very important: make sure metadata values are saved too
215 enrich_pane.stopEditingAndRebuild();
216 // Make sure all the metadata has been saved to file
217 MetadataXMLFileManager.saveMetadataXMLFiles();
218
219 Gatherer.c_man.saveCollection();
220 }
221
222 // *************
223 // Edit Options.
224 // *************
225 else if(esrc == menu_bar.edit_copy) {
226 try {
227 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
228 // Get the component with selected text as a JTextComponent
229 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();//getFocusOwner();
230 text.copy();
231 }
232 catch (Exception cce) {
233 // If the component is not a text component ignore the copy command
234 DebugStream.println(cce.toString());
235 }
236 }
237 else if(esrc == menu_bar.edit_cut) {
238 try {
239 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
240 // Get the component with selected text as a JTextComponent
241 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
242 // Cut the text to the clipboard
243 text.cut();
244 }
245 catch (ClassCastException cce) {
246 // If the component is not a text component ignore the cut command
247 DebugStream.println(cce.toString());
248 }
249 }
250 else if(esrc == menu_bar.edit_paste) {
251 try {
252 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
253 // Get the component with selected text as a JTextComponent
254 JTextComponent text = (JTextComponent) kfm.getPermanentFocusOwner();
255 // Cut the text to the clipboard
256 text.paste();
257 }
258 catch (ClassCastException cce) {
259 // If the component is not a text component ignore the paste command
260 DebugStream.println(cce.toString());
261 }
262 }
263 else if(esrc == menu_bar.edit_config) {
264 if(Gatherer.c_man.getCollection() != null) {
265
266 ConfigFileEditor configEditor = new ConfigFileEditor();
267 configEditor.setVisible(true);
268 configEditor.setSize(900,700);
269 }
270 }
271
272 // *************
273 // Help Options.
274 // *************
275 else if (esrc == menu_bar.help_general) {
276 HelpFrame.setView("introduction");
277 }
278 else if (esrc == menu_bar.help_download) {
279 HelpFrame.setView("themirrorview");
280 }
281 else if (esrc == menu_bar.help_gather) {
282 HelpFrame.setView("collectingfiles");
283 }
284 else if (esrc == menu_bar.help_enrich) {
285 HelpFrame.setView("enrichingacollection");
286 }
287 else if (esrc == menu_bar.help_design) {
288 HelpFrame.setView("designingacollection");
289 }
290 else if (esrc == menu_bar.help_create) {
291 HelpFrame.setView("producingthecollection");
292 }
293 else if (esrc == menu_bar.help_format) {
294 HelpFrame.setView("formattingacollection");
295 }
296 else if (esrc == menu_bar.help_about) {
297 new AboutDialog(this);
298 }
299 }
300
301
302 /** 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.
303 */
304 public void afterDisplay() {
305 if (download_pane != null) {
306 download_pane.afterDisplay();
307 }
308 enrich_pane.afterDisplay();
309 }
310
311
312 public void closeCurrentCollection()
313 {
314 tab_pane.setSelectedComponent(gather_pane);
315 Gatherer.c_man.closeCollection();
316 FilenameEncoding.closeCollection(); // clear filename-to-encodings map
317 }
318
319
320 public void saveThenCloseCurrentCollection()
321 {
322 Gatherer.c_man.saveCollection();
323 closeCurrentCollection();
324 }
325
326
327 /** This is called when we're absolutely finished with the GLI. It is *not* called when the applet is suspended.
328 */
329 public void destroy()
330 {
331 // Destroying create pane ensures the latest log has been closed
332 if (create_pane != null) {
333 create_pane.destroy();
334 }
335
336 // Deal to help
337 if (help != null) {
338 help.destroy();
339 help = null;
340 }
341 }
342
343
344 /** Enabled events on the window to be trapped, creates all the visual components, then builds the tab and other layouts.
345 */
346 public void display() {
347 content_pane = (JPanel) this.getContentPane();
348 content_pane.setComponentOrientation(Dictionary.getOrientation());
349
350 // Enable window-type events to be fired.
351 enableEvents(AWTEvent.WINDOW_EVENT_MASK);
352 // Initialise and layout sub-components, plus other window dressing.
353 try {
354 this.setSize(size);
355
356 // Set the title
357 String collection_title = null;
358 String collection_name = null;
359 if (Gatherer.c_man.ready()) {
360 Collection collection = Gatherer.c_man.getCollection();
361 collection_title = collection.getTitle();
362 collection_name = collection.getGroupQualifiedName(true);
363 collection = null;
364 }
365 setTitle(collection_title, collection_name);
366 collection_title = null;
367 collection_name = null;
368
369 // Pretty corner icon
370 String gsmall_image = "gatherer.png";
371 if (Configuration.fedora_info.isActive()) {
372 gsmall_image = "fli-" + gsmall_image;
373 }
374 if(Gatherer.isGsdlRemote) {
375 gsmall_image = "client-" + gsmall_image;
376 }
377 this.setIconImage(JarTools.getImage(gsmall_image).getImage());
378 // BorderLayout for the main screen. I'll try my best to avoid these in subcomponents as they're space greedy.
379 content_pane.setLayout(new BorderLayout());
380 // Create the menu-bar and stick it up the top.
381 menu_bar = new MenuBar(new MenuListenerImpl());
382 menu_bar.setComponentOrientation(Dictionary.getOrientation());
383
384 //feedback changes
385 //content_pane.add(menu_bar, BorderLayout.NORTH);
386 this.setJMenuBar(menu_bar);
387 // end feedback changes
388
389 // Create the tabbed pane and plop it in the center where it will
390 // expand to consume all available space like any good gas would.
391 tab_pane = new JTabbedPane();
392 tab_pane.setComponentOrientation(Dictionary.getOrientation());
393 tab_pane.addChangeListener(this);
394 tab_pane.setFont(Configuration.getFont("general.font", false));
395
396 if (Configuration.get("workflow.download", true) && Gatherer.isDownloadEnabled) {
397 download_pane = new DownloadPane();
398 // "GUI.Download_Tooltip" is used automatically
399 tab_pane.addTab(Dictionary.get("GUI.Download"), JarTools.getImage("download.gif"), download_pane, Dictionary.get("GUI.Download_Tooltip"));
400 tab_pane.setEnabledAt(tab_pane.indexOfComponent(download_pane), Configuration.get("workflow.download", false));
401 }
402
403 gather_pane = new GatherPane();
404 gather_pane.display();
405 if (Configuration.get("workflow.gather", true)) {
406 // "GUI.Gather_Tooltip" is used automatically
407 tab_pane.addTab(Dictionary.get("GUI.Gather"), JarTools.getImage("gather.gif"), gather_pane, Dictionary.get("GUI.Gather_Tooltip"));
408 tab_pane.setEnabledAt(tab_pane.indexOfComponent(gather_pane), Configuration.get("workflow.gather", false));
409 }
410
411 enrich_pane = new EnrichPane();
412 enrich_pane.display();
413 if (Configuration.get("workflow.enrich", true)) {
414 // "GUI.Enrich_Tooltip" is used automatically
415 tab_pane.addTab(Dictionary.get("GUI.Enrich"), JarTools.getImage("enrich.gif"), enrich_pane, Dictionary.get("GUI.Enrich_Tooltip"));
416 tab_pane.setEnabledAt(tab_pane.indexOfComponent(enrich_pane), false);
417 }
418
419 design_pane = new DesignPane();
420 design_pane.display();
421 if (Configuration.get("workflow.design", true)) {
422 // "GUI.Design_Tooltip" is used automatically
423 if (Configuration.fedora_info.isActive()) {
424 tab_pane.addTab("Plugins", JarTools.getImage("design.gif"), design_pane, Dictionary.get("GUI.Design_Tooltip"));
425 }
426 else {
427 tab_pane.addTab(Dictionary.get("GUI.Design"), JarTools.getImage("design.gif"), design_pane, Dictionary.get("GUI.Design_Tooltip"));
428 }
429
430 tab_pane.setEnabledAt(tab_pane.indexOfComponent(design_pane), false);
431 }
432
433 create_pane = new CreatePane();
434 create_pane.setComponentOrientation(Dictionary.getOrientation());
435 create_pane.display();
436 if (Configuration.get("workflow.create", true)) {
437 // "GUI.Create_Tooltip" is used automatically
438 tab_pane.addTab(Dictionary.get("GUI.Create"), JarTools.getImage("create.gif"), create_pane, Dictionary.get("GUI.Create_Tooltip"));
439 tab_pane.setEnabledAt(tab_pane.indexOfComponent(create_pane), false);
440 }
441
442 format_pane = new FormatPane();
443 format_pane.setComponentOrientation(Dictionary.getOrientation());
444 format_pane.display();
445 if (Configuration.get("workflow.format", true)) {
446 tab_pane.addTab(Dictionary.get("GUI.Format"), JarTools.getImage("format.gif"), format_pane, Dictionary.get("GUI.Format_Tooltip"));
447 tab_pane.setEnabledAt(tab_pane.indexOfComponent(format_pane), false);
448 }
449
450 // The MetaAuditFrame must be created after the gather/enrich panes but before they get focus
451 meta_audit = new MetaAuditFrame();
452 meta_audit.setComponentOrientation(Dictionary.getOrientation());
453 // Select the collect pane if it is available
454 if (tab_pane.indexOfComponent(gather_pane) != -1) {
455 tab_pane.setSelectedComponent(gather_pane);
456 }
457 // Otherwise find the first tab that is enabled and select that.
458 else {
459 for (int i = 0; i < tab_pane.getTabCount(); i++) {
460 if (tab_pane.isEnabledAt(i)) {
461 tab_pane.setSelectedIndex(i);
462 break;
463 }
464 }
465 }
466
467 content_pane.add(tab_pane, BorderLayout.CENTER);
468
469 // Add an extra progress bar at the bottom of every screen when using a remote Greenstone server
470 if (Gatherer.isGsdlRemote) {
471 JPanel remote_greenstone_server_progress_panel = new JPanel();
472 //remote_greenstone_server_progress_panel.setComponentOrientation(Dictionary.getOrientation());
473 JLabel remote_greenstone_server_progress_label = new JLabel(Dictionary.get("RemoteGreenstoneServer.Progress"));
474 //remote_greenstone_server_progress_label.setComponentOrientation(Dictionary.getOrientation());
475 remote_greenstone_server_progress_panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
476 remote_greenstone_server_progress_panel.setLayout(new BorderLayout());
477 remote_greenstone_server_progress_panel.add(remote_greenstone_server_progress_label, BorderLayout.LINE_START);
478 remote_greenstone_server_progress_panel.add(Gatherer.remoteGreenstoneServer.getProgressBar(), BorderLayout.CENTER);
479 content_pane.add(remote_greenstone_server_progress_panel, BorderLayout.SOUTH);
480 }
481
482 // Call refresh to update all controls to reflect current collection status.
483 refresh(-1, Gatherer.c_man.ready());
484 }
485 catch (Exception e) {
486 DebugStream.printStackTrace(e);
487 // The GUI failing to build is an app killer
488 e.printStackTrace();
489 System.exit(1);
490 }
491 }
492
493 public void exit()
494 {
495 exit(0);
496 }
497
498 // some cases we have already saved the collection, but don't want to
499 // override the general.open_collection value here
500 public void exitNoCollectionSave(int exit_status) {
501 // Store the current position and size of the GLI for next time
502 Configuration.setBounds("general.bounds", true, getBounds());
503
504 // Save configuration
505 Configuration.save();
506
507 // Hide the main window
508 setVisible(false);
509
510 // If we're running as an applet we don't quit here (we quit when the browser calls GathererApplet.destroy())
511 if (!Gatherer.isApplet) {
512 Gatherer.exit(exit_status);
513 }
514
515 }
516 /** This method ensures that all the things needing saving are saved before Gatherer.exit() is called.
517 */
518 public void exit(int exit_status)
519 {
520 boolean collection_open = false;
521 // If we have a collection open remember it for next time, then save it and close it
522 if (Gatherer.c_man.ready()) {
523 // but we no longer remember open collections for remote GLI, as this causes problems
524 // in applets when other users on a machine want to use an applet and GLI wants to load
525 // a collection left open from a previous session and *requires* the old user to authenticate
526 if(Gatherer.isGsdlRemote) {
527 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
528 true, "");
529 } else {
530 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
531 true, CollectionManager.getLoadedCollectionColFilePath());
532 }
533 saveThenCloseCurrentCollection();
534 collection_open = true;
535 }
536 else {
537 // if there was no open collection, then write out the collect dir path for next time
538 // IF this is not the default collect directory and not a remote GLI/applet
539 if(Gatherer.isGsdlRemote || Gatherer.getCollectDirectoryPath().equals(
540 Gatherer.getDefaultGSCollectDirectoryPath(true))) {
541 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
542 true, "");
543 } else {
544 Configuration.setString("general.open_collection"+Configuration.gliPropertyNameSuffix(),
545 true, Gatherer.getCollectDirectoryPath());
546 }
547 }
548
549
550 // Basically, if GLI coldir != gsdlsite_colhome, we ask them if they want to save GLI coldir as gsdlsite_colhome
551 // If gsdlsite_colhome = "" (meaning default GS coldir) and GLI is also set to defaultGScoldir, no saving necessary.
552 // We ONLY do this for the local included apache web server, since the issue revolves around gsdlsite.cfg containing
553 // the global collecthome when using the apache web server. This is not an issue for remote GS or server.exe (the latter
554 // has separate config files specifying collecthome for when GLI is running whereas when just the server.exe is running).
555 if(!Gatherer.isGsdlRemote && !Gatherer.isPersistentServer()) {
556
557 String defaultColDir = Gatherer.getDefaultGSCollectDirectoryPath(false); // no filesep at end
558 String gliColDir = Gatherer.getCollectDirectoryPath();
559 String serverColDir = Gatherer.gsdlsite_collecthome;
560
561 boolean showSettingsDlg = true;
562 // if collectdir was changed during GLI session (to something different from what's in gsdlsite), need to ask what to store
563 String gsdlsiteColdir = Gatherer.gsdlsite_collecthome.replace("\"", "");
564 if(gliColDir.equals(gsdlsiteColdir+File.separator)) {
565 showSettingsDlg = false;
566 } else if(gsdlsiteColdir.equals("") // both set to default coldir
567 && gliColDir.equals(defaultColDir+File.separator)) {
568 showSettingsDlg = false;
569 }
570 if(showSettingsDlg) {
571
572 // else we will be showing the Collect Directory Settings Dialog
573 if(gliColDir.endsWith(File.separator)) {
574 gliColDir = gliColDir.substring(0, gliColDir.length()-1);
575 }
576
577 if(serverColDir.equals("")) {
578 serverColDir = defaultColDir;
579 }
580 collectDirSettingsDialog(defaultColDir, serverColDir, gliColDir, collection_open);
581 }
582 }
583
584 // Store the current position and size of the GLI for next time
585 Configuration.setBounds("general.bounds", true, getBounds());
586
587 // Save configuration
588 Configuration.save();
589
590 // Hide the main window
591 setVisible(false);
592
593 // If we're running as an applet we don't quit here (we quit when the browser calls GathererApplet.destroy())
594 if (!Gatherer.isApplet) {
595 Gatherer.exit(exit_status);
596 }
597 }
598
599 public void collectDirSettingsDialog(final String defaultColDir,
600 final String from, final String to, final boolean collection_open)
601 {
602 final JDialog colHomeDialog
603 = new JDialog(this, Dictionary.get("GUI.CollectHome.title"), true); // this = Gatherer.g_man
604 colHomeDialog.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
605
606
607 JRadioButton gliYes = new JRadioButton(Dictionary.get("General.Yes"), true);
608 // the default selection for GLI, collecthome=to
609 JRadioButton serverYes = new JRadioButton(Dictionary.get("General.Yes")); // not selected for the server
610 JRadioButton gliNo = null;
611 JRadioButton gliThirdOption = null;
612 JRadioButton serverNo = null;
613 JRadioButton serverThirdOption = null;
614
615 if(from.equals(defaultColDir)) {
616 gliNo = new JRadioButton(Dictionary.get("GUI.CollectHome.resetToDefault"));
617 serverNo = new JRadioButton(Dictionary.get("GUI.CollectHome.leaveAtDefault"), true);
618 // default selection for server, collecthome=from
619 } else {
620 gliNo = new JRadioButton(Dictionary.get("General.No"));
621 serverNo = new JRadioButton(Dictionary.get("General.No"), true); // default selection for server
622 if(!to.equals(defaultColDir)) { // neither from nor to is the default GS collect dir, so give them that as the third option
623 gliThirdOption = new JRadioButton(Dictionary.get("GUI.CollectHome.reset"));
624 serverThirdOption = new JRadioButton(Dictionary.get("GUI.CollectHome.reset"));
625 }
626 }
627
628 JPanel gliPanel = new JPanel(); // flowlayout by default
629 ButtonGroup gliGroup = new ButtonGroup();
630 JPanel serverPanel = new JPanel(); // flowlayout by default
631 ButtonGroup serverGroup = new ButtonGroup();
632
633 gliGroup.add(gliYes);
634 gliPanel.add(gliYes);
635 serverGroup.add(serverYes);
636 serverPanel.add(serverYes);
637
638 gliGroup.add(gliNo);
639 gliPanel.add(gliNo);
640 serverGroup.add(serverNo);
641 serverPanel.add(serverNo);
642
643 if(gliThirdOption != null) {
644 gliThirdOption = new JRadioButton("Reset to default");
645 serverThirdOption = new JRadioButton("Reset to default");
646
647 gliGroup.add(gliThirdOption);
648 gliPanel.add(gliThirdOption);
649 serverGroup.add(serverThirdOption);
650 serverPanel.add(serverThirdOption);
651 }
652
653 // vars need to be final to use them in the actionlistener below
654 final JRadioButton gli_yes = gliYes;
655 final JRadioButton gli_no = gliNo;
656 final JRadioButton gli_optional = gliThirdOption;
657 final JRadioButton server_yes = serverYes;
658 final JRadioButton server_no = serverNo;
659 final JRadioButton server_optional = serverThirdOption;
660
661 JButton okButton = new JButton(Dictionary.get("General.OK"));
662 okButton.addActionListener(new ActionListener() {
663 public void actionPerformed(ActionEvent e) {
664 // store the option chosen for GLI
665 String gliColDir = to;
666 if(gli_optional != null && gli_optional.isSelected()) {
667 // defaultColDir
668 gliColDir = "";
669 } else if(gli_yes.isSelected()) {
670 gliColDir = to;
671 } else if (gli_no.isSelected()) {
672 gliColDir = from;
673 }
674 if(defaultColDir.equals(gliColDir)) {
675 gliColDir = "";
676 }
677 if(!(collection_open && gli_yes.isSelected())) { // don't overwrite open collections
678 Configuration.setString(
679 "general.open_collection"+Configuration.gliPropertyNameSuffix(),
680 true, gliColDir);
681 }
682
683 // store the option chosen for the server's settings
684 String serverColDir = from;
685 if(server_optional != null && server_optional.isSelected()) {
686 // defaultColDir
687 serverColDir = null;
688 } else if(server_yes.isSelected()) {
689 serverColDir = to;
690 } else if (server_no.isSelected()) {
691 serverColDir = from;
692 }
693 if(serverColDir != null && defaultColDir.equals(serverColDir)) {
694 serverColDir = null;
695 }
696
697 if(serverColDir != null) {
698 serverColDir = serverColDir.replace("\"","");
699 serverColDir = "\""+serverColDir+"\"";
700 }
701
702 Gatherer.gsdlsite_collecthome = Utility.updatePropertyConfigFile(
703 Gatherer.getGsdlSiteConfigFile(), "collecthome", serverColDir);
704
705 colHomeDialog.dispose();
706 }
707 });
708
709 //String[] dirs = {from, to};
710 //JLabel message = new JLabel(Dictionary.get("GUI.CollectHome.message", dirs));
711 JLabel message = new JLabel(Dictionary.get("GUI.CollectHome.message"));
712 JLabel fromDirLine = new JLabel(Dictionary.get("GUI.CollectHome.dir", from));
713 fromDirLine.setBorder(BorderFactory.createEmptyBorder(0,25,0,0)); // padding
714 JLabel toLine = new JLabel(Dictionary.get("GUI.CollectHome.to"));
715 JLabel toDirLine = new JLabel(Dictionary.get("GUI.CollectHome.dir", to));
716 toDirLine.setBorder(BorderFactory.createEmptyBorder(0,25,0,0)); // padding
717 JLabel gliOption = new JLabel(Dictionary.get("GUI.CollectHome.gli"));
718 JLabel serverOption = new JLabel(Dictionary.get("GUI.CollectHome.server"));
719
720 JPanel mainPanel = new JPanel();
721 mainPanel.setLayout(new GridLayout(9, 1));
722 mainPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); // padding
723 mainPanel.add(message);
724 mainPanel.add(fromDirLine);
725 mainPanel.add(toLine);
726 mainPanel.add(toDirLine);
727 mainPanel.add(gliOption);
728 mainPanel.add(gliPanel);
729 mainPanel.add(serverOption);
730 mainPanel.add(serverPanel);
731 mainPanel.add(okButton);
732 Container c = colHomeDialog.getContentPane();
733 c.setLayout(new BorderLayout());
734 c.add(mainPanel, BorderLayout.CENTER);
735 colHomeDialog.getRootPane().setDefaultButton(okButton);
736
737 colHomeDialog.pack();
738 colHomeDialog.setVisible(true);
739 }
740
741
742 /** 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.
743 */
744 public void lockCollection(boolean import_stage, boolean lock)
745 {
746 locked = lock;
747
748 if (import_stage) {
749 int gather_pos = tab_pane.indexOfComponent(gather_pane);
750 if (gather_pos != -1) {
751 tab_pane.setEnabledAt(gather_pos, !lock);
752 }
753 int enrich_pos = tab_pane.indexOfComponent(enrich_pane);
754 if (enrich_pos != -1) {
755 tab_pane.setEnabledAt(enrich_pos, !lock);
756 }
757 }
758
759 int design_pos = tab_pane.indexOfComponent(design_pane);
760 if (design_pos != -1) {
761 tab_pane.setEnabledAt(design_pos, !lock);
762 }
763 }
764
765
766 public void modeChanged(int mode) {
767 // Set the title
768 String collection_title = null;
769 String collection_name = null;
770 if (Gatherer.c_man.ready()) {
771 Collection collection = Gatherer.c_man.getCollection();
772 collection_title = collection.getTitle();
773 collection_name = collection.getGroupQualifiedName(true);
774 collection = null;
775 }
776 setTitle(collection_title, collection_name);
777 collection_title = null;
778 collection_name = null;
779 // Now pass on the message to anyone who cares
780 if (download_pane != null) {
781 download_pane.modeChanged(mode);
782 }
783 if (gather_pane != null) {
784 gather_pane.modeChanged(mode);
785 }
786 if (enrich_pane != null) {
787 enrich_pane.modeChanged(mode);
788 }
789 if (design_pane != null) {
790 design_pane.modeChanged(mode);
791 }
792 if (create_pane != null) {
793 create_pane.modeChanged(mode);
794 }
795 if (format_pane != null) {
796 format_pane.modeChanged(mode);
797 }
798 }
799
800
801 public void refresh(int refresh_reason, boolean collection_loaded)
802 {
803 // Set the collection information in the title bar
804 if (collection_loaded) {
805 Collection collection = Gatherer.c_man.getCollection();
806 setTitle(collection.getTitle(), collection.getGroupQualifiedName(true));
807 }
808 else {
809 setTitle(null, null);
810 }
811
812 // Update the menu bar
813 menu_bar.refresh(refresh_reason, collection_loaded);
814
815 // Update the loaded panes
816 if (download_pane != null) {
817 download_pane.refresh(refresh_reason, collection_loaded);
818 }
819 if (gather_pane != null) {
820 gather_pane.refresh(refresh_reason, collection_loaded);
821 }
822 if (enrich_pane != null) {
823 enrich_pane.refresh(refresh_reason, collection_loaded);
824 }
825 if (design_pane != null) {
826 design_pane.refresh(refresh_reason, collection_loaded);
827 }
828 if (create_pane != null) {
829 create_pane.refresh(refresh_reason, collection_loaded);
830 }
831 if (format_pane != null) {
832 format_pane.refresh(refresh_reason, collection_loaded);
833 }
834
835 // Now enable tabs as necessary. Do this on event queue to prevent crazy NPEs
836 if (!locked) {
837 if (tab_updater == null) {
838 tab_updater = new TabUpdater(tab_pane, collection_loaded);
839 }
840 else {
841 tab_updater.setReady(collection_loaded);
842 }
843 SwingUtilities.invokeLater(tab_updater);
844 }
845 }
846
847
848 public void refreshCollectionTree(int refresh_reason)
849 {
850 if (gather_pane != null) {
851 gather_pane.refreshCollectionTree(refresh_reason);
852 }
853 }
854
855
856 public void refreshWorkspaceTree(int refresh_reason)
857 {
858 if (gather_pane != null) {
859 gather_pane.refreshWorkspaceTree(refresh_reason);
860 }
861 }
862
863 public void refreshWorkspaceTreeGreenstoneCollections()
864 {
865 refreshWorkspaceTree(WorkspaceTree.LIBRARY_CONTENTS_CHANGED);
866 }
867
868 /** Specifies whether a certain tab is enabled or not. */
869 private void setTabEnabled(String rawname, boolean state) {
870 // Retrieve the dictionary based name.
871 String name = Dictionary.get("GUI." + rawname);
872 int index = tab_pane.indexOfTab(name);
873 // Of course we may not have this tab available.
874 if(index != -1) {
875 // Some tabs are also dependant on if a collection is ready
876 Component component = tab_pane.getComponentAt(index);
877 if(component == enrich_pane || component == design_pane || component == create_pane || component == format_pane) {
878 tab_pane.setEnabledAt(index, state && Gatherer.c_man != null && Gatherer.c_man.ready());
879 }
880 else {
881 tab_pane.setEnabledAt(index, state);
882 }
883 // If this was the currently selected tab and it is now disabled, change the view to the first enabled tab.
884 if(tab_pane.getSelectedIndex() == index && !state) {
885 boolean found = false;
886 for(int i = 0; !found && i < tab_pane.getTabCount(); i++) {
887 if(tab_pane.isEnabledAt(i)) {
888 tab_pane.setSelectedIndex(i);
889 found = true;
890 }
891 }
892 // If there are no tabs enabled, which should be impossible, then select the first tab
893 if(!found) {
894 tab_pane.setSelectedIndex(0);
895 }
896 }
897 }
898 }
899
900 /** 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.
901 * @param title
902 * @param name
903 */
904 public void setTitle(String title, String name) {
905
906 // Finally display the collection name in the title bar.
907 StringBuffer title_buffer = new StringBuffer(Configuration.getApplicationTitle());
908 title_buffer.append(StaticStrings.SPACE_CHARACTER);
909 title_buffer.append(Gatherer.PROGRAM_VERSION);
910 title_buffer.append(StaticStrings.SPACE_CHARACTER);
911 title_buffer.append(StaticStrings.SPACE_CHARACTER);
912 // Server version information
913 title_buffer.append(Gatherer.getServerVersionAsString());
914 title_buffer.append(StaticStrings.SPACE_CHARACTER);
915 title_buffer.append(StaticStrings.SPACE_CHARACTER);
916 // Describe the current user mode
917 title_buffer.append(StaticStrings.MODE_STR);
918 title_buffer.append(Configuration.getModeAsString());
919 title_buffer.append(StaticStrings.SPACE_CHARACTER);
920 title_buffer.append(StaticStrings.SPACE_CHARACTER);
921 // Now for the current collection
922 title_buffer.append(StaticStrings.COLLECTION_STR);
923 if (title != null && name != null) {
924 title_buffer.append(title);
925 title_buffer.append(StaticStrings.SPACE_CHARACTER);
926 title_buffer.append(StaticStrings.OPEN_PARENTHESIS_CHARACTER);
927 title_buffer.append(name);
928 title_buffer.append(StaticStrings.CLOSE_PARENTHESIS_CHARACTER);
929 }
930 else {
931 title_buffer.append(Dictionary.get("Collection.No_Collection"));
932 }
933 this.setTitle(title_buffer.toString());
934 title_buffer = null;
935 }
936
937
938 private class OpenCollectionTask
939 implements Runnable //extends Thread -> If this extends Thread, it will cause an EDT access
940 // violation since the GUI stuff in run() is not done in the Swing Event Dispatch Thread/EDT.
941 {
942 public void run()
943 {
944 String collection_file_path = showOpenCollectionDialog();
945
946 // User has selected a collection to open
947 if (collection_file_path != null) {
948 // If there is already a collection open, save and close it
949 if (Gatherer.c_man.ready()) {
950 saveThenCloseCurrentCollection();
951 }
952
953 // Open the selected collection
954 Gatherer.c_man.loadCollection(collection_file_path);
955 }
956 }
957 }
958
959
960 /** When the load collection option is chosen this method is called to produce the modal file load prompt.
961 */
962 private String showOpenCollectionDialog()
963 {
964 OpenCollectionDialog dialog = new OpenCollectionDialog();
965 TestingPreparation.setNamesRecursively(dialog);
966
967 dialog.setComponentOrientation(Dictionary.getOrientation());
968 if (dialog.display() == OpenCollectionDialog.OK_OPTION) {
969 return dialog.getFileName();
970 }
971
972 // User must have cancelled the action
973 return null;
974 }
975
976
977 /** When called this method causes the MetadataAuditTable to display a nice dialog box which contains all the metadata assigned in the collection.
978 */
979 public void showMetaAuditBox() {
980 wait(true);
981 meta_audit.display();
982 wait(false);
983 }
984
985
986 private class NewCollectionTask
987 extends Thread
988 {
989 public void run()
990 {
991 // Create the collection details prompt from new collection prompt
992 NewCollectionDetailsPrompt ncd_prompt = new NewCollectionDetailsPrompt();
993
994 // Create the new collection (if not cancelled) in a new thread.
995 if (!ncd_prompt.isCancelled()) {
996 // If there is already a collection open, save and close it.
997 if (Gatherer.c_man.ready()) {
998 saveThenCloseCurrentCollection();
999 }
1000
1001 // Create new collection.
1002 Gatherer.c_man.createCollection(ncd_prompt.getDescription(), Configuration.getEmail(), ncd_prompt.getName(), ncd_prompt.getTitle(), ncd_prompt.getBase(), new ArrayList());
1003 ncd_prompt.dispose();
1004 }
1005
1006 // Done
1007 ncd_prompt = null;
1008 }
1009 }
1010
1011
1012 private class DeleteCollectionTask
1013 extends Thread
1014 {
1015 public void run()
1016 {
1017 // The rest is handled by the DeleteCollectionPrompt
1018 DeleteCollectionPrompt dc_prompt = new DeleteCollectionPrompt();
1019 if (dc_prompt.display()) {
1020 //closeCurrentCollection();
1021 }
1022 dc_prompt.destroy();
1023 dc_prompt = null;
1024 }
1025 }
1026
1027
1028 /** 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.
1029 * @param event A ChangeEvent containing information about the event that fired this call.
1030 */
1031 public void stateChanged(ChangeEvent event)
1032 {
1033 if (previous_pane != null) {
1034 if (previous_pane == gather_pane) {
1035 gather_pane.loseFocus();
1036 }
1037 else if (previous_pane == enrich_pane) {
1038 enrich_pane.loseFocus();
1039 }
1040 else if (previous_pane == design_pane) {
1041 design_pane.loseFocus();
1042 }
1043 else if (previous_pane == create_pane) {
1044 create_pane.loseFocus();
1045 }
1046 else if (previous_pane == format_pane) {
1047 format_pane.loseFocus();
1048 }
1049 }
1050
1051 menu_bar.tabSelected(tab_pane.getSelectedIndex());
1052 int selected_index = tab_pane.getSelectedIndex();
1053 if (selected_index == tab_pane.indexOfComponent(download_pane)) {
1054 download_pane.gainFocus();
1055 }
1056 else if (selected_index == tab_pane.indexOfComponent(gather_pane)) {
1057 gather_pane.gainFocus();
1058 }
1059 else if (selected_index == tab_pane.indexOfComponent(enrich_pane)) {
1060 enrich_pane.gainFocus();
1061 }
1062 else if (selected_index == tab_pane.indexOfComponent(design_pane)) {
1063 design_pane.gainFocus();
1064 }
1065 else if (selected_index == tab_pane.indexOfComponent(create_pane)) {
1066 create_pane.gainFocus();
1067 }
1068 else if (selected_index == tab_pane.indexOfComponent(format_pane)) {
1069 format_pane.gainFocus();
1070 }
1071
1072 previous_pane = (JPanel) tab_pane.getSelectedComponent();
1073
1074 }
1075
1076
1077 private MouseListener mouse_blocker_listener = new MouseAdapter() {};
1078
1079 public void updateUI()
1080 {
1081 JPanel pane = (JPanel) getContentPane();
1082 pane.updateUI();
1083 // Also update all of the tabs according to workflow.
1084 workflowUpdate("Download", Configuration.get("workflow.download", false));
1085 workflowUpdate("Gather", Configuration.get("workflow.gather", false));
1086 workflowUpdate("Enrich", Configuration.get("workflow.enrich", false));
1087 workflowUpdate("Design", Configuration.get("workflow.design", false));
1088 workflowUpdate("Create", Configuration.get("workflow.create", false));
1089 workflowUpdate("Format", Configuration.get("workflow.format", false));
1090 }
1091
1092 public void wait(boolean waiting) {
1093 Component glass_pane = getGlassPane();
1094 if(waiting) {
1095 // Show wait cursor.
1096 glass_pane.addMouseListener(mouse_blocker_listener);
1097 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1098 glass_pane.setVisible(true);
1099 }
1100 else {
1101 // Hide wait cursor.
1102 glass_pane.setVisible(false);
1103 glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1104 glass_pane.removeMouseListener(mouse_blocker_listener);
1105 }
1106 glass_pane = null;
1107 }
1108
1109 public void workflowUpdate(String raw, boolean state) {
1110 WorkflowUpdater task = new WorkflowUpdater(raw, state);
1111 SwingUtilities.invokeLater(task);
1112 task = null;
1113 }
1114
1115
1116 /**Overridden from JFrame so we can exit safely when window is closed (or destroyed).
1117 * @param event A <strong>WindowEvent</strong> containing information about the event that fired this call.
1118 */
1119 protected void processWindowEvent(WindowEvent event) {
1120 if(event.getID() == WindowEvent.WINDOW_CLOSING) {
1121 exit();
1122 }
1123 }
1124
1125
1126 /** 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.
1127 */
1128 private class MenuListenerImpl
1129 implements MenuListener {
1130 /** Called whenever a popup menu is hidden, but we don't care.
1131 * @param e Some <strong>MenuEvent</strong> that we could care less about.
1132 */
1133 public void menuCanceled(MenuEvent e) {
1134 }
1135 /** Called whenever a menu header (ie button) becomes unselected, but we don't care.
1136 * @param e Some <strong>MenuEvent</strong> that we could care less about.
1137 */
1138 public void menuDeselected(MenuEvent e) {
1139 }
1140 /** 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.
1141 * @param e The <strong>MenuEvent</strong> whose source is checked.
1142 */
1143 public void menuSelected(MenuEvent e) {
1144 if(e.getSource() == menu_bar.help) {
1145 if(menu_bar.help.isSelected()) {
1146 menu_bar.help.doClick(10);
1147 }
1148 }
1149 else if(e.getSource() == menu_bar.edit) { // someone clicked the toplevel Edit menu
1150 // gray out the Edit Config File menu if there's no collection currently open.
1151 if(Gatherer.c_man.getCollection() == null) {
1152 menu_bar.edit_config.setEnabled(false);
1153 }
1154 else { // don't forget to reenable the Edit ConfigXML menu item when applicable
1155 menu_bar.edit_config.setEnabled(true);
1156 }
1157 }
1158
1159 }
1160 }
1161
1162 private class TabUpdater
1163 implements Runnable {
1164 private boolean ready = false;
1165 private int download_pos = -1;
1166 private int enrich_pos = -1;
1167 private int design_pos = -1;
1168 private int create_pos = -1;
1169 private int format_pos = -1;
1170 private int export_pos = -1;
1171 private JTabbedPane tab_pane = null;
1172
1173 public TabUpdater(JTabbedPane tab_pane, boolean ready) {
1174 this.ready = ready;
1175 this.tab_pane = tab_pane;
1176 download_pos = tab_pane.indexOfComponent(download_pane);
1177 enrich_pos = tab_pane.indexOfComponent(enrich_pane);
1178 design_pos = tab_pane.indexOfComponent(design_pane);
1179 create_pos = tab_pane.indexOfComponent(create_pane);
1180 format_pos = tab_pane.indexOfComponent(format_pane);
1181 }
1182
1183 public void run()
1184 {
1185 if (download_pos != -1) {
1186 if (ready) {
1187 tab_pane.setEnabledAt(download_pos, Configuration.get("workflow.download", false));
1188 }
1189 else {
1190 tab_pane.setEnabledAt(download_pos, Configuration.get("workflow.download", true));
1191 }
1192 }
1193 if (enrich_pos != -1) {
1194 tab_pane.setEnabledAt(enrich_pos, ready && Configuration.get("workflow.enrich", false));
1195 }
1196 if (design_pos != -1) {
1197 tab_pane.setEnabledAt(design_pos, ready && Configuration.get("workflow.design", false) && Configuration.getMode() > Configuration.ASSISTANT_MODE);
1198 }
1199 if (create_pos != -1) {
1200 tab_pane.setEnabledAt(create_pos, ready && Configuration.get("workflow.create", false));
1201 }
1202 if (format_pos != -1) {
1203 tab_pane.setEnabledAt(format_pos, ready && Configuration.get("workflow.format", false) && Configuration.getMode() > Configuration.ASSISTANT_MODE);
1204 }
1205 }
1206
1207 public void setReady(boolean ready) {
1208 this.ready = ready;
1209 }
1210 }
1211
1212 private class WorkflowUpdater
1213 implements Runnable {
1214 private boolean state;
1215 private String raw;
1216 public WorkflowUpdater(String raw, boolean state) {
1217 this.raw = raw;
1218 this.state = state;
1219 }
1220 public void run() {
1221 setTabEnabled(raw, state);
1222 }
1223 }
1224}
Note: See TracBrowser for help on using the repository browser.