source: trunk/gli/src/org/greenstone/gatherer/gui/CollectionPane.java@ 4802

Last change on this file since 4802 was 4802, checked in by jmt12, 21 years ago

bug#2030156: Pressing [Enter] will no longer cause NPE because the FileNode selected had no file

  • Property svn:keywords set to Author Date Id Revision
File size: 35.0 KB
Line 
1package org.greenstone.gatherer.gui;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * <BR><BR>
10 *
11 * Author: John Thompson, Greenstone Digital Library, University of Waikato
12 *
13 * <BR><BR>
14 *
15 * Copyright (C) 1999 New Zealand Digital Library Project
16 *
17 * <BR><BR>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * <BR><BR>
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * <BR><BR>
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 *########################################################################
37 */
38import java.awt.*;
39import java.awt.event.*;
40import java.io.*;
41import java.util.*;
42import javax.swing.*;
43import javax.swing.event.*;
44import javax.swing.tree.*;
45import org.greenstone.gatherer.Gatherer;
46import org.greenstone.gatherer.file.FileNode;
47import org.greenstone.gatherer.file.FileOpenActionListener;
48import org.greenstone.gatherer.file.FileQueue;
49import org.greenstone.gatherer.file.FileSystemModel;
50import org.greenstone.gatherer.gui.Filter;
51import org.greenstone.gatherer.gui.GComboBox;
52import org.greenstone.gatherer.gui.tree.DragTree;
53import org.greenstone.gatherer.undo.UndoManager;
54import org.greenstone.gatherer.util.DragComponent;
55import org.greenstone.gatherer.util.DragGroup;
56import org.greenstone.gatherer.util.TreeSynchronizer;
57import org.greenstone.gatherer.util.Utility;
58import org.greenstone.gatherer.util.WinRegistry;
59/** The collection pane is analogous with a file manager. It is there that the user chooses which files to include in their collection and what structure the file hierarchy should take. The later aspect is not important for the Greenstone Suite, but is usefull for grouping files for ease of metadata markup. The view essentially consists of two file trees, one denoting the entire source workspace and the other the files within your collection. The trees themselves have a title bar at the top, a filter control at the bottom, and are coloured to indicate activity (grey for disabled). The remainder of the screen is taken by a status area, to indicate current file job progress during copying etc, and three buttons for controlling features of the view.
60 * @author John Thompson, Greenstone Digital Library, University of Waikato
61 * @version 2.3
62 */
63public class CollectionPane
64 extends JPanel
65 implements ActionListener, FocusListener {
66 /** The group encompassing all of the components available as drop targets for drag and drop actions. Required so that only one component renders the ghost and higlights itself as a target, which the other members are restored to their original, pristine, condition. */
67 private DragGroup group = null;
68 /** The tree showing the files within the collection. */
69 private DragTree collection_tree = null;
70 /** The tree showing the available source workspace. */
71 private DragTree workspace_tree = null;
72 /** The threaded queue that handles the actually movement of files, so that the gui remains responsive. */
73 private FileQueue file_queue = null;
74 /** The filter currently applied to the collection tree. */
75 private Filter collection_filter = null;
76 /** The filter currently applied to the workspace tree. */
77 private Filter workspace_filter = null;
78 /** The collection model which is used to build, and hold the data of, the collection tree. */
79 private TreeModel collection = null;
80 /** The GTree model used as the data source for the workspace tree. */
81 private TreeModel workspace = null;
82 /** The button used to cancel all pending file queue jobs. */
83 private JButton cancel_action = null;
84 /** The button used to create a new folder in the collection tree. */
85 private JButton new_folder = null;
86 /** The label shown at the top of the collection tree. */
87 private JLabel collection_label = null;
88 /** The label shown in the status area explaining the file apon which action is taking place. */
89 private JLabel filename_label = null;
90 /** The label shown explaining the current state of the file queue thread. */
91 private JLabel status_label = null;
92 /** The label at the top of the workspace tree. */
93 private JLabel workspace_label = null;
94 /** The panel that contains the collection tree. */
95 private JPanel collection_pane = null;
96 /** The panel that contains the various controls including the status area. */
97 private JPanel control_pane = null;
98 /** The panel that contains the workspace tree. */
99 private JPanel workspace_pane = null;
100 /** The scrollable area into which the collection tree is placed. */
101 private JScrollPane collection_scroll = null;
102 /** The scrollable area into which the workspace tree is placed. */
103 private JScrollPane workspace_scroll = null;
104 /** A split pane seperating the two trees, allowing for the screen real-estate for each to be changed. */
105 private JSplitPane tree_pane = null;
106 /** Text fragment arguments used to fill in phrases returned from the dictionary. */
107 private String args[] = null;
108 /** Ensures that expansion and selection events between collection trees based on the same model are synchronized. */
109 private TreeSynchronizer collection_tree_sync = null;
110 /** Ensures that expansion and selection events between workspace trees based on the same model are synchronized. */
111 private TreeSynchronizer workspace_tree_sync = null;
112 /** The button used to delete files, which also doubles as a drop target for files from the Trees. */
113 private UndoManager bin_button = null;
114 /** The default size of a label in the interface. */
115 static final private Dimension LABEL_SIZE = new Dimension(100,30);
116 /** The default size of a special mapping dialog. */
117 static final Dimension DIALOG_SIZE = new Dimension(400, 120);
118 /** The minimum size a gui component can become. */
119 static private Dimension MIN_SIZE = new Dimension( 90, 90);
120 /** The default size of the status area. */
121 static private Dimension STATUS_SIZE = new Dimension(450, 120);
122 /** The initial size of the trees. */
123 static private Dimension TREE_SIZE = new Dimension(400, 430);
124 /* Constructor.
125 * @param tree_sync Ensures that expansion events between like trees are synchronized.
126 * @see org.greenstone.gatherer.file.FileManager
127 * @see org.greenstone.gatherer.file.FileQueue
128 */
129 public CollectionPane(TreeSynchronizer workspace_tree_sync, TreeSynchronizer collection_tree_sync) {
130 this.group = new DragGroup();
131 this.file_queue = Gatherer.f_man.getQueue();
132 this.collection_tree_sync = collection_tree_sync;
133 this.workspace_tree_sync = workspace_tree_sync;
134
135 // Create components.
136 cancel_action = new JButton(Utility.getImage("big_stop.gif"));
137 cancel_action.addActionListener(this);
138 cancel_action.setEnabled(false);
139 cancel_action.setMinimumSize(MIN_SIZE);
140 cancel_action.setPreferredSize(MIN_SIZE);
141
142 new_folder = new JButton(Utility.getImage("folder.gif"));
143 new_folder.addActionListener(this);
144 new_folder.setEnabled(false);
145 new_folder.setMinimumSize(MIN_SIZE);
146 new_folder.setPreferredSize(MIN_SIZE);
147 }
148 /** Any implementation of ActionListener requires this method so that when an action is performed the appropriate effect can occur. In this case there are three valid possibilities. If the action occured on the recycle bin, then delete the current selection from the collection tree. If the action instead occured on the new folder button, then create a new folder under the current (single) selection in the collection tree. And finally if the cancel button was pressed, cancel the current, and remaining, jobs on the file queue. */
149 public void actionPerformed(ActionEvent event) {
150 // If a user has clicked on the bin button directly remove whatever
151 // files are selected in the active tree.
152 if(event.getSource() == bin_button) {
153 if(!bin_button.ignore()) {
154 // Find the active tree (you've made selections in).
155 DragTree tree = (DragTree) group.getActive();
156 // Fudge things a bit
157 group.setSource(tree);
158 // Determine the selection.
159 TreePath paths[] = tree.getSelectionPaths();
160 if(paths != null) {
161 FileNode[] source_nodes = new FileNode[paths.length];
162 for(int i = 0; i < paths.length; i++) {
163 source_nodes[i] = (FileNode)(paths[i].getLastPathComponent());
164 }
165 Gatherer.f_man.action(tree, source_nodes, bin_button, null);
166 }
167 }
168 }
169 // If a user has clicked on new_folder create a new folder under
170 // whatever node is selected.
171 else if(event.getSource() == new_folder && collection_tree != null) {
172 int count = collection_tree.getSelectionCount();
173 boolean error = false;
174 if(count == 1) {
175 TreePath path = collection_tree.getSelectionPath();
176 FileNode node = (FileNode) path.getLastPathComponent();
177 if(node.getAllowsChildren()) {
178 Gatherer.f_man.newFolder(collection_tree, node);
179 }
180 else {
181 // try the parent
182 FileNode parent = (FileNode)node.getParent();
183 if (parent!=null && parent.getAllowsChildren()) {
184 Gatherer.f_man.newFolder(collection_tree, parent);
185 } else {
186 error = true;
187 }
188 }
189 }
190 else {
191 error = true;
192 }
193 if(error) {
194 // instead of an error, we now create a new folder at the root
195 FileNode node = (FileNode) collection_tree.getModel().getRoot();
196 Gatherer.f_man.newFolder(collection_tree, node);
197 //JOptionPane.showMessageDialog(Gatherer.g_man, Gatherer.dictionary.get("FileActions.No_Parent_For_New_Folder"), Gatherer.dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
198 }
199 }
200 else if(event.getSource() == cancel_action) {
201 file_queue.cancelAction();
202 }
203 }
204 /** Called whenever a significant change occurs in the current collections state, such as a new collection being loaded or the current one being closed. Several actions must occur in the GUI to indicate this change to the user, such as en/disabling the collection tree.
205 * @param ready <i>true</i> if a collection is loaded and ready to be modified, <i>false</i> otherwise.
206 * @see org.greenstone.gatherer.Configuration
207 * @see org.greenstone.gatherer.Gatherer
208 * @see org.greenstone.gatherer.collection.CollectionManager
209 * @see org.greenstone.gatherer.gui.Coloring
210 * @see org.greenstone.gatherer.gui.Filter
211 * @see org.greenstone.gatherer.util.TreeSynchronizer
212 */
213 public void collectionChanged(boolean ready) {
214 // Try to retrieve the collections record set.
215 collection = Gatherer.c_man.getRecordSet();
216 if(collection != null) {
217 args = new String[1];
218 args[0] = Gatherer.c_man.getCollection().getName();
219 collection_label.setText(get("Collection", args));
220 collection_tree.setModel(collection);
221 collection_tree.repaint();
222 collection_filter.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
223 }
224 else {
225 String args[] = new String[1];
226 args[0] = get("Collection.No_Collection");
227 collection_label.setText(get("Collection", args));
228 args = null;
229 collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Error")));
230 collection_filter.setBackground(Color.lightGray);
231 }
232
233 collection_tree.setEnabled(ready);
234 collection_filter.setEnabled(ready);
235
236 // Change the label at the top of collection tree.
237 setEnabled(collection_label, ready, Gatherer.config.getColor("coloring.collection_heading_foreground", false), Gatherer.config.getColor("coloring.collection_heading_background", false));
238 // Ensure that this tree view of the collection record set is synchronized with any others.
239 collection_tree_sync.add(collection_tree);
240
241 workspace = Gatherer.c_man.getWorkspace();
242 workspace_tree.setModel(workspace);
243 workspace_tree_sync.add(workspace_tree);
244
245 // Enable or disable the control buttons
246 bin_button.setEnabled(ready);
247 cancel_action.setEnabled(ready);
248 new_folder.setEnabled(ready);
249 }
250 /** Generates the pane on controls used to 'collect' files into the collection. Resposible for creating, connecting and laying out these controls. */
251 public void display() {
252 // Create Components.
253 KeyListenerImpl key_listener = new KeyListenerImpl();
254 MouseListenerImpl mouse_listener = new MouseListenerImpl();
255 this.addKeyListener(key_listener);
256
257 // Workspace Tree
258 workspace_pane = new JPanel();
259 workspace_pane.setMinimumSize(MIN_SIZE);
260 workspace_pane.setPreferredSize(TREE_SIZE);
261 workspace_pane.setSize(TREE_SIZE);
262
263 workspace_label = new JLabel(get("Workspace"));
264 workspace_label.setOpaque(true);
265 workspace_label.setBackground(Gatherer.config.getColor("coloring.workspace_heading_background", false));
266 workspace_label.setForeground(Gatherer.config.getColor("coloring.workspace_heading_foreground", false));
267
268 workspace = Gatherer.c_man.getWorkspace();
269 workspace_tree = new DragTree(Utility.WORKSPACE_TREE, workspace, null, true);
270 group.add(workspace_tree);
271 workspace_tree.addFocusListener(this);
272 workspace_tree.addKeyListener(key_listener);
273 workspace_tree.addMouseListener(mouse_listener);
274 workspace_tree.addMouseListener(Gatherer.g_man.foa_listener);
275 workspace_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
276 workspace_tree.addTreeSelectionListener(file_queue);
277 workspace_tree.putClientProperty("JTree.lineStyle", "Angled");
278 workspace_tree.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.workspace_tree_background", false));
279 workspace_tree.setTextNonSelectionColor(Gatherer.config.getColor("coloring.workspace_tree_foreground", false));
280 workspace_tree.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.workspace_selection_background", false));
281 workspace_tree.setTextSelectionColor(Gatherer.config.getColor("coloring.workspace_selection_foreground", false));
282 workspace_tree.setRootVisible(false);
283
284 workspace_scroll = new JScrollPane(workspace_tree);
285
286 workspace_filter = Gatherer.g_man.getFilter(workspace_tree);
287 workspace_filter.setBackground(Gatherer.config.getColor("coloring.workspace_heading_background", false));
288 // Change the default colours of this filters combobox.
289 GComboBox fcb = workspace_filter.getComboBox();
290 fcb.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.editable", false));
291 fcb.setTextNonSelectionColor(Gatherer.config.getColor("coloring.workspace_tree_foreground", false));
292 fcb.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
293 fcb.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
294 fcb = null;
295
296 // Collection Tree
297 collection_pane = new JPanel();
298 collection_pane.setMinimumSize(MIN_SIZE);
299 collection_pane.setPreferredSize(TREE_SIZE);
300 collection_pane.setSize(TREE_SIZE);
301
302 args = new String[1];
303 args[0] = get("Collection.No_Collection");
304 collection_label = new JLabel(get("Collection", args));
305 collection_label.setOpaque(true);
306
307 collection = Gatherer.c_man.getRecordSet();
308 if(collection != null) {
309 collection_tree = new DragTree(Utility.COLLECTION_TREE, collection, null, true);
310 collection_tree.setEnabled(true);
311 }
312 else {
313 collection_tree = new DragTree(Utility.COLLECTION_TREE, null, true);
314 collection_tree.setEnabled(false);
315 }
316 group.add(collection_tree);
317 collection_tree.addFocusListener(this);
318 collection_tree.addKeyListener(key_listener);
319 collection_tree.addMouseListener(mouse_listener);
320 collection_tree.addMouseListener(Gatherer.g_man.foa_listener);
321 collection_tree.addTreeSelectionListener(file_queue);
322 collection_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
323 collection_tree.putClientProperty("JTree.lineStyle", "Angled");
324 collection_tree.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.collection_tree_background", false));
325 collection_tree.setTextNonSelectionColor(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
326 collection_tree.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
327 collection_tree.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
328 collection_tree.setRootVisible(false);
329
330 collection_scroll = new JScrollPane(collection_tree);
331
332 collection_filter = Gatherer.g_man.getFilter(collection_tree);
333 if(collection != null) {
334 collection_filter.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
335 }
336 else {
337 collection_filter.setBackground(Color.lightGray);
338 }
339 // Change the default colours of this filters combobox.
340 fcb = collection_filter.getComboBox();
341 fcb.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.editable", false));
342 fcb.setTextNonSelectionColor(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
343 fcb.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
344 fcb.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
345 fcb = null;
346
347 tree_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
348
349 // Status pane
350 control_pane = new JPanel();
351
352 JPanel inner_pane = new JPanel();
353 inner_pane.setSize(STATUS_SIZE);
354
355 JPanel file_pane = new JPanel();
356 //file_pane.setBackground(Color.white);
357 //JPanel job_pane = new JPanel();
358 //job_pane.setBackground(Color.white);
359 JPanel progress_pane = new JPanel();
360 //progress_pane.setBackground(Color.white);
361 JLabel file_status = file_queue.getFileStatus();
362 //JLabel job_status = job_queue.getJobStatus();
363
364 JProgressBar progress = file_queue.getProgress();
365
366 JPanel button_pane = new JPanel();
367
368 bin_button = Gatherer.c_man.undo;
369 bin_button.addActionListener(this);
370 bin_button.setEnabled(false);
371 bin_button.setMinimumSize(MIN_SIZE);
372 bin_button.setPreferredSize(MIN_SIZE);
373 group.add(bin_button);
374
375 // Layout Components.
376 workspace_pane.setLayout(new BorderLayout());
377 workspace_pane.add(workspace_label, BorderLayout.NORTH);
378 workspace_pane.add(workspace_scroll, BorderLayout.CENTER);
379 workspace_pane.add(workspace_filter, BorderLayout.SOUTH);
380
381 collection_pane.setLayout(new BorderLayout());
382 collection_pane.add(collection_label, BorderLayout.NORTH);
383 collection_pane.add(collection_scroll, BorderLayout.CENTER);
384 collection_pane.add(collection_filter, BorderLayout.SOUTH);
385
386 tree_pane.add(workspace_pane, JSplitPane.LEFT);
387 tree_pane.add(collection_pane, JSplitPane.RIGHT);
388 tree_pane.setDividerLocation(TREE_SIZE.width - 10);
389
390 //job_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
391 //job_pane.setLayout(new BorderLayout());
392 //job_pane.add(job_status, BorderLayout.CENTER);
393
394 file_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
395 file_pane.setLayout(new BorderLayout());
396 file_pane.add(file_status, BorderLayout.CENTER);
397
398 progress_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
399 progress_pane.setLayout(new BorderLayout());
400 progress_pane.add(progress, BorderLayout.CENTER);
401
402 inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10,10,10,10), BorderFactory.createLoweredBevelBorder()));
403 inner_pane.setLayout(new GridLayout(2,1));
404 //inner_pane.add(job_pane);
405 inner_pane.add(file_pane);
406 inner_pane.add(progress_pane);
407
408 button_pane.add(cancel_action);
409 button_pane.add(new_folder);
410 button_pane.add(bin_button);
411
412 control_pane.setLayout(new BorderLayout());
413 //control_pane.add(new_folder, BorderLayout.WEST);
414 control_pane.add(inner_pane, BorderLayout.CENTER);
415 control_pane.add(button_pane, BorderLayout.EAST);
416
417 this.setLayout(new BorderLayout());
418 this.add(tree_pane, BorderLayout.CENTER);
419 this.add(control_pane, BorderLayout.SOUTH);
420 }
421 /** This method ensures that a certain tree path is visible and selected within the collection tree, expanding nodes if necessary. If the method is successful the bounds of the new selection are returned. */
422 public Rectangle expandPath(TreePath path) {
423 collection_tree.setImmediate(true);
424 collection_tree.scrollPathToVisible(path);
425 collection_tree.setSelectionPath(path);
426 collection_tree.setImmediate(false);
427 return collection_tree.getRowBounds(collection_tree.getRowForPath(path));
428 }
429 /** Called whenever this pane gains focus, this method ensures that the various tree renderers are correctly colouring the tree (as these settings sometimes get lost).
430 * @param event A <strong>FocusEvent</strong> containing details about the focus action performed.
431 */
432 public void focusGained(FocusEvent event) {
433 DefaultTreeCellRenderer def = new DefaultTreeCellRenderer();
434 DefaultTreeCellRenderer w = (DefaultTreeCellRenderer)workspace_tree.getCellRenderer();
435 DefaultTreeCellRenderer c = (DefaultTreeCellRenderer)collection_tree.getCellRenderer();
436 if(event.getSource() == workspace_tree) {
437 w.setBackgroundSelectionColor(def.getBackgroundSelectionColor());
438 c.setBackgroundSelectionColor(Color.lightGray);
439 }
440 else if(event.getSource() == collection_tree) {
441 c.setBackgroundSelectionColor(def.getBackgroundSelectionColor());
442 w.setBackgroundSelectionColor(Color.lightGray);
443 }
444 repaint();
445 }
446 /** Implementation side-effect, not used in any way.
447 * @param event A <strong>FocusEvent</strong> containing details about the focus action performed.
448 */
449 public void focusLost(FocusEvent event) {
450 }
451
452 /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
453 * @see org.greenstone.gatherer.tree.GTree
454 */
455 public void gainFocus() {
456 // Update the menubar's idea of whats been selected
457 if (collection_tree != null) {
458 if (collection_tree.isSelectionEmpty()) {
459 Gatherer.g_man.menu_bar.setMetaAuditSuffix(null);
460 }
461 else {
462 Gatherer.g_man.menu_bar.setMetaAuditSuffix(collection_tree.getSelectionDetails());
463 }
464 }
465 // Update the meta-audit view to show the current selection, if any.
466 Gatherer.g_man.meta_audit.setRecords(getSelected());
467 }
468
469 /** Retrieve a list of the currently selected file records in the active tree. */
470 public FileNode[] getSelected() {
471 TreePath paths[] = collection_tree.getSelectionPaths();
472 FileNode records[] = null;
473 if(paths != null) {
474 records = new FileNode[paths.length];
475 for(int i = 0; i < records.length; i++) {
476 records[i] = (FileNode) paths[i].getLastPathComponent();
477 }
478 }
479 return records;
480 }
481
482 public String getSelectionDetails() {
483 return collection_tree.getSelectionDetails();
484 }
485
486 public DragTree getWorkspaceTree() {
487 return workspace_tree;
488 }
489
490 public void refreshTrees() {
491 collection_tree.refresh(null);
492 }
493
494 /** Retrieve a phrase from the dictionary based on the key.
495 * @param key A <strong>String</strong> used to uniquely identify the phrase to be retrieved.
496 */
497 private String get(String key) {
498 return get(key, null);
499 }
500 /** Retrieve a phrase from the dictionary based on the key, and filled in using text fragments from an arguments array.
501 * @param key A <strong>String</strong> used to uniquely identify the phrase to be retrieved.
502 * @param args A <strong>String[]</strong> used as parameters to be inserted in the phrase retrieved.
503 * @see org.greenstone.gatherer.Dictionary
504 * @see org.greenstone.gatherer.Gatherer
505 */
506 private String get(String key, String args[]) {
507 if(key.indexOf('.') == -1) {
508 key = "Collection." + key;
509 }
510 return Gatherer.dictionary.get(key, args);
511 }
512 /** Used to set the enabled state, and hence the colouring, of the two tree labels.
513 * @param label The <strong>JLabel</strong> to be affected.
514 * @param state <i>true</i> for enabled, i.e. when a collection is ready, <i>false</i> otherwise.
515 * @param foreground The <strong>Color</strong> to make the foreground text of the label when enabled.
516 * @param background The <strong>Color</strong> to make the background of the label when enabled.
517 */
518 private void setEnabled(JLabel label, boolean state, Color foreground, Color background) {
519 ///ystem.err.println("Setting the label color to state " + state);
520 if(state) {
521 label.setBackground(background);
522 label.setForeground(foreground);
523 }
524 else {
525 label.setBackground(Color.lightGray);
526 label.setForeground(Color.black);
527 }
528 label.repaint();
529 ///ystem.err.println("Color is now " + label.getBackground());
530 }
531 /** When a user right-clicks within the trees on the collection pane view they are presented with a small popup menu of context based options. This class provides such functionality.
532 */
533 private class GPopupMenu
534 extends JPopupMenu
535 implements ActionListener {
536 /** The tree over which the right click action occured. */
537 private DragTree tree = null;
538 /** The file record over which the right click action occured, if any. */
539 private FileNode node = null;
540 /** A menu item enabled if a delete action is appropriate in the menus current context. */
541 private JMenuItem delete = null;
542 /** A menu item enabled if a special directory mapping is appropriate in the menus current context. */
543 private JMenuItem map = null;
544 /** A menu item enabled if a new folder action is appropriate in the menus current context. */
545 private JMenuItem new_folder = null;
546 /** A menu item enabled if a show meta-audit dialog action is appropriate in the menus current context. */
547 private JMenuItem show_metaaudit = null;
548 /** A menu item allowing a user to unmap a special directory, if and only if a special directory is selected. */
549 private JMenuItem unmap = null;
550 /** Constructor. */
551 public GPopupMenu(DragTree tree, MouseEvent event) {
552 super();
553 this.tree = tree;
554 TreePath path = tree.getClosestPathForLocation(event.getX(), event.getY());
555 node = (FileNode)path.getLastPathComponent();
556 // Set Options based on selection and tree
557 if(tree.getSelectionCount() != 0 && tree == collection_tree) {
558 show_metaaudit = new JMenuItem(get("Menu.Metadata_View") + " " + collection_tree.getSelectionDetails(), KeyEvent.VK_V);
559 show_metaaudit.addActionListener(this);
560 add(show_metaaudit);
561 }
562 if(tree == collection_tree && node != null && node.getFile() != null && node.getFile().isDirectory() && !node.isReadOnly()) {
563 new_folder = new JMenuItem(get("New_Folder"), KeyEvent.VK_N);
564 new_folder.addActionListener(this);
565 add(new_folder);
566 add(new JSeparator());
567 }
568 if(node == null || (node != null && !node.isReadOnly())) {
569 delete = new JMenuItem(get("Delete"), KeyEvent.VK_D);
570 delete.addActionListener(this);
571 add(delete);
572 }
573 if(tree == workspace_tree && node != null && !node.isLeaf()) {
574 String node_name = node.toString();
575 FileNode root = (FileNode) tree.getModel().getRoot();
576 if(!node_name.equals(get("Tree.World")) && !node_name.equals(get("Tree.Root")) && !node_name.equals(get("Tree.Public")) && !node_name.equals(get("Tree.Private"))) {
577 // You can unmap 1st level nodes.
578 if(root.getIndex(node) != -1) {
579 unmap = new JMenuItem(get("MappingPrompt.Unmap"), KeyEvent.VK_U);
580 unmap.addActionListener(this);
581 add(unmap);
582 }
583 // Or map any other level directories.
584 else {
585 map = new JMenuItem(get("MappingPrompt.Map"), KeyEvent.VK_M);
586 map.addActionListener(this);
587 add(map);
588 }
589 }
590 }
591 show(tree, event.getX(), event.getY());
592 }
593 /** Called whenever one of the menu items is actioned apon, this method then causes the appropriate effect. */
594 public void actionPerformed(ActionEvent event) {
595 if(event.getSource() == delete) {
596 // Retrieve the selection. Of course this gets a bit tricky as the user may have right clicked over a node not in the current selection, in which case we remove only that node.
597 TreePath[] selection_paths = tree.getSelectionPaths();
598 if(selection_paths != null) {
599 FileNode[] source_nodes = new FileNode[selection_paths.length];
600 boolean found = false;
601 for(int i = 0; i < selection_paths.length; i++) {
602 source_nodes[i] = (FileNode) selection_paths[i].getLastPathComponent();
603 if(node != null) {
604 found = found || source_nodes[i].equals(node);
605 }
606 }
607 if(node != null && !found) {
608 source_nodes = null;
609 source_nodes = new FileNode[1];
610 source_nodes[0] = node;
611 }
612 // Fire a delete action
613 Gatherer.f_man.action(tree, source_nodes, bin_button, null);
614 source_nodes = null;
615 }
616 selection_paths = null;
617 }
618 else if(event.getSource() == map && node != null) {
619 MappingPrompt mp = new MappingPrompt(node.getFile());
620 mp.destroy();
621 mp = null;
622 }
623 else if(event.getSource() == new_folder && node != null) {
624 Gatherer.f_man.newFolder(tree, node);
625 }
626 else if(event.getSource() == show_metaaudit) {
627 Gatherer.g_man.showMetaAuditBox();
628 }
629 else if(event.getSource() == unmap && node != null) {
630 Gatherer.c_man.removeDirectoryMapping(node);
631 }
632 }
633 /** Retrieve a phrase from the dictionary based on the key.
634 * @param key A <strong>String</strong> used to uniquely identify the phrase to be retrieved.
635 */
636 private String get(String key) {
637 return get(key, null);
638 }
639 /** Retrieve a phrase from the dictionary based on the key, and filled in using text fragments from an arguments array.
640 * @param key A <strong>String</strong> used to uniquely identify the phrase to be retrieved.
641 * @param args A <strong>String[]</strong> used as parameters to be inserted in the phrase retrieved.
642 * @see org.greenstone.gatherer.Dictionary
643 * @see org.greenstone.gatherer.Gatherer
644 */
645 private String get(String key, String args[]) {
646 if(key.indexOf('.') == -1) {
647 key = "CollectionPopupMenu." + key;
648 }
649 return Gatherer.dictionary.get(key, args);
650 }
651 }
652 /** This class listens for certain key presses, such as [Enter] or [Delete], and responds appropriately. */
653 private class KeyListenerImpl
654 extends KeyAdapter {
655 /** Called whenever a key that was pressed is released, it is this action that will cause the desired effects (this allows for the key event itself to be processed prior to this listener dealing with it). */
656 public void keyReleased(KeyEvent event) {
657 ///ystem.err.println("Key Release detected. " + event.getKeyCode());
658 if(event.getKeyCode() == KeyEvent.VK_DELETE) {
659 // Get the selected files from the tree and removal them using the default dnd removal method.
660 // Find the active tree (you've made selections in).
661 DragTree tree = (DragTree) group.getActive();
662 // Fudge things a bit
663 group.setSource(tree);
664 // Determine the selection.
665 TreePath paths[] = tree.getSelectionPaths();
666 if(paths != null) {
667 FileNode[] source_nodes = new FileNode[paths.length];
668 for(int i = 0; i < source_nodes.length; i++) {
669 source_nodes[i] = (FileNode) paths[i].getLastPathComponent();
670 }
671 Gatherer.f_man.action(tree, source_nodes, bin_button, null);
672 source_nodes = null;
673 }
674 }
675 else if(event.getKeyCode() == KeyEvent.VK_ENTER) {
676 // Get the first selected file.
677 DragTree tree = (DragTree)event.getSource();
678 TreePath path = tree.getSelectionPath();
679 if(path != null) {
680 File file = ((FileNode)path.getLastPathComponent()).getFile();
681 if(file != null && file.isFile()) {
682 Gatherer.self.spawnApplication(file);
683 }
684 else {
685 if(!tree.isExpanded(path)) {
686 tree.expandPath(path);
687 }
688 else {
689 tree.collapsePath(path);
690 }
691 }
692 }
693 }
694 }
695 }
696
697 /** This provides a small prompt for gathering addition details about a special directory mapping such as its symbolic name. */
698 private class MappingPrompt
699 extends JDialog
700 implements ActionListener, KeyListener {
701 private boolean cancelled = false;
702 private JButton cancel_button = null;
703 private JButton ok_button = null;
704 private JTextField name_field = null;
705 public MappingPrompt(File file) {
706 super(Gatherer.g_man);
707 setModal(true);
708 setSize(DIALOG_SIZE);
709 setTitle(get("MappingPrompt.Title"));
710 // Creation
711 JPanel content_pane = (JPanel) getContentPane();
712 JPanel center_pane = new JPanel();
713 JPanel file_pane = new JPanel();
714 JLabel file_label = new JLabel(get("MappingPrompt.File"));
715 file_label.setPreferredSize(LABEL_SIZE);
716 JLabel file_field = new JLabel(file.getAbsolutePath());
717 JPanel name_pane = new JPanel();
718 JLabel name_label = new JLabel(get("MappingPrompt.Name"));
719 name_label.setPreferredSize(LABEL_SIZE);
720 name_field = new JTextField(file.getName());
721 JPanel button_pane = new JPanel();
722 ok_button = new JButton(get("General.OK"));
723 ok_button.setEnabled(name_field.getText().length() > 0);
724 cancel_button = new JButton(get("General.Cancel"));
725 // Connection
726 cancel_button.addActionListener(this);
727 ok_button.addActionListener(this);
728 name_field.addKeyListener(this);
729 // Layout
730 file_pane.setLayout(new BorderLayout());
731 file_pane.add(file_label, BorderLayout.WEST);
732 file_pane.add(file_field, BorderLayout.CENTER);
733
734 name_pane.setLayout(new BorderLayout());
735 name_pane.add(name_label, BorderLayout.WEST);
736 name_pane.add(name_field, BorderLayout.CENTER);
737
738 center_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
739 center_pane.setLayout(new GridLayout(2,1,5,5));
740 center_pane.add(file_pane);
741 center_pane.add(name_pane);
742
743 button_pane.setLayout(new GridLayout(1,2,5,5));
744 button_pane.add(ok_button);
745 button_pane.add(cancel_button);
746
747 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
748 content_pane.setLayout(new BorderLayout());
749 content_pane.add(center_pane, BorderLayout.CENTER);
750 content_pane.add(button_pane, BorderLayout.SOUTH);
751 // Display
752 Dimension screen_size = Gatherer.config.screen_size;
753 setLocation((screen_size.width - DIALOG_SIZE.width) / 2, (screen_size.height - DIALOG_SIZE.height) / 2);
754 show();
755 // If not cancelled create mapping.
756 if(!cancelled) {
757 Gatherer.c_man.addDirectoryMapping(name_field.getText(), file);
758 }
759 }
760 public void actionPerformed(ActionEvent event) {
761 if(event.getSource() == cancel_button) {
762 cancelled = true;
763 }
764 dispose();
765 }
766 public void destroy() {
767 cancel_button = null;
768 ok_button = null;
769 name_field = null;
770 }
771 public void keyPressed(KeyEvent event) {
772 }
773 public void keyReleased(KeyEvent event) {
774 ok_button.setEnabled(name_field.getText().length() > 0);
775
776 }
777 public void keyTyped(KeyEvent event) {
778 }
779 }
780 /** This class listens for mouse clicks and responds right mouse button clicks (popup menu). */
781 private class MouseListenerImpl
782 extends MouseAdapter {
783 /** Any subclass of MouseAdapter can override this method to respond to mouse click events. In this case we want to open a pop-up menu if we detect a right mouse click over one of our registered components, and start an external application if someone double clicks on a certain file record. */
784 public void mouseClicked(MouseEvent event) {
785 if(SwingUtilities.isRightMouseButton(event)) {
786 new GPopupMenu((DragTree)event.getSource(), event);
787 }
788 }
789 }
790}
Note: See TracBrowser for help on using the repository browser.