source: trunk/gli/src/org/greenstone/gatherer/gui/GatherPane.java@ 8853

Last change on this file since 8853 was 8853, checked in by mdewsnip, 19 years ago

Initial work on allowing metadata databases to be exploded from within the GLI. These are marked with a different icon in the collection tree and a new item is added to the right-click menu for these files. Clicking this will eventually run the explode_metadata_databases.pl script to explode these files.

  • Property svn:keywords set to Author Date Id Revision
File size: 34.7 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.event.*;
41import java.io.*;
42import java.util.*;
43import javax.swing.*;
44import javax.swing.event.*;
45import javax.swing.tree.*;
46import org.greenstone.gatherer.Configuration;
47import org.greenstone.gatherer.DebugStream;
48import org.greenstone.gatherer.Dictionary;
49import org.greenstone.gatherer.Gatherer;
50import org.greenstone.gatherer.collection.CollectionTree;
51import org.greenstone.gatherer.collection.CollectionTreeNode;
52import org.greenstone.gatherer.file.FileNode;
53import org.greenstone.gatherer.file.FileOpenActionListener;
54import org.greenstone.gatherer.file.FileQueue;
55import org.greenstone.gatherer.file.FileSystemModel;
56import org.greenstone.gatherer.file.RecycleBin;
57import org.greenstone.gatherer.file.WorkspaceTree;
58import org.greenstone.gatherer.file.WorkspaceTreeNode;
59import org.greenstone.gatherer.gui.tree.DragTree;
60import org.greenstone.gatherer.util.DragComponent;
61import org.greenstone.gatherer.util.DragGroup;
62import org.greenstone.gatherer.util.TreeSynchronizer;
63import org.greenstone.gatherer.util.Utility;
64
65/** 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.
66 * @author John Thompson, Greenstone Digital Library, University of Waikato
67 * @version 2.3
68 */
69public class GatherPane
70 extends JPanel
71 implements ActionListener, FocusListener {
72 /** 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. */
73 private DragGroup group = null;
74 /** The tree showing the files within the collection. */
75 private CollectionTree collection_tree = null;
76 /** The threaded queue that handles the actually movement of files, so that the gui remains responsive. */
77 private FileQueue file_queue = null;
78 /** The filter currently applied to the collection tree. */
79 private Filter collection_filter = null;
80 /** The filter currently applied to the workspace tree. */
81 private Filter workspace_filter = null;
82 /** The button used to cancel all pending file queue jobs. */
83 private JButton stop_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 /** The button used to delete files, which also doubles as a drop target for files from the Trees. */
111 private RecycleBin bin_button = null;
112 /** The default size of a special mapping dialog. */
113 static final Dimension DIALOG_SIZE = new Dimension(400, 120);
114 /** The minimum size a gui component can become. */
115 static private Dimension MIN_SIZE = new Dimension( 90, 90);
116 /** The default size of the status area. */
117 static private Dimension STATUS_SIZE = new Dimension(450, 120);
118 /** The initial size of the trees. */
119 static private Dimension TREE_SIZE = new Dimension(400, 430);
120
121 /** The tree showing the available source workspace. */
122 private WorkspaceTree workspace_tree = null;
123
124
125 /* Constructor.
126 * @param tree_sync Ensures that expansion events between like trees are synchronized.
127 * @see org.greenstone.gatherer.file.FileManager
128 * @see org.greenstone.gatherer.file.FileQueue
129 */
130 public GatherPane(TreeSynchronizer collection_tree_sync) {
131 this.group = new DragGroup();
132 this.file_queue = Gatherer.f_man.getQueue();
133 this.collection_tree_sync = collection_tree_sync;
134
135 // Create components.
136 stop_action = new GLIButton();
137 stop_action.addActionListener(this);
138 stop_action.setEnabled(false);
139 stop_action.setMnemonic(KeyEvent.VK_S);
140 file_queue.registerStopButton(stop_action);
141 Dictionary.registerBoth(stop_action, "Collection.Stop", "Collection.Stop_Tooltip");
142
143 new_folder = new GLIButton(Utility.getImage("folder.gif"));
144 new_folder.addActionListener(this);
145 new_folder.setEnabled(false);
146 new_folder.setMinimumSize(MIN_SIZE);
147 new_folder.setMnemonic(KeyEvent.VK_N);
148 new_folder.setPreferredSize(MIN_SIZE);
149 Dictionary.registerTooltip(new_folder, "Collection.New_Folder_Tooltip");
150 }
151
152 /** 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. */
153 public void actionPerformed(ActionEvent event) {
154 // If a user has clicked on the bin button directly remove whatever
155 // files are selected in the active tree.
156 if(event.getSource() == bin_button) {
157 if(!bin_button.ignore()) {
158 // Find the active tree (you've made selections in).
159 DragTree tree = (DragTree) group.getActive();
160 // Fudge things a bit
161 group.setSource(tree);
162 // Determine the selection.
163 TreePath paths[] = tree.getSelectionPaths();
164 if(paths != null) {
165 FileNode[] source_nodes = new FileNode[paths.length];
166 for(int i = 0; i < paths.length; i++) {
167 source_nodes[i] = (FileNode)(paths[i].getLastPathComponent());
168 }
169 Gatherer.f_man.action(tree, source_nodes, bin_button, null);
170 }
171 }
172 }
173 // If a user has clicked on new_folder create a new folder under
174 // whatever node is selected.
175 else if(event.getSource() == new_folder && collection_tree != null) {
176 int count = collection_tree.getSelectionCount();
177 boolean error = false;
178 if(count == 1) {
179 TreePath path = collection_tree.getSelectionPath();
180 CollectionTreeNode node = (CollectionTreeNode) path.getLastPathComponent();
181 if (node.getAllowsChildren()) {
182 Gatherer.f_man.newFolder(collection_tree, node);
183 }
184 else {
185 // try the parent
186 CollectionTreeNode parent = (CollectionTreeNode) node.getParent();
187 if (parent!=null && parent.getAllowsChildren()) {
188 Gatherer.f_man.newFolder(collection_tree, parent);
189 } else {
190 error = true;
191 }
192 }
193 }
194 else {
195 error = true;
196 }
197 if(error) {
198 // instead of an error, we now create a new folder at the root
199 CollectionTreeNode node = (CollectionTreeNode) collection_tree.getModel().getRoot();
200 Gatherer.f_man.newFolder(collection_tree, node);
201 }
202 }
203 else if(event.getSource() == stop_action) {
204 file_queue.cancelAction();
205 }
206 }
207
208
209 /** Generates the pane on controls used to 'collect' files into the collection. Resposible for creating, connecting and laying out these controls. */
210 public void display() {
211 // Create Components.
212 KeyListenerImpl key_listener = new KeyListenerImpl();
213 MouseListenerImpl mouse_listener = new MouseListenerImpl();
214 this.addKeyListener(key_listener);
215
216 // Workspace Tree
217 workspace_pane = new JPanel();
218 workspace_pane.setMinimumSize(MIN_SIZE);
219 workspace_pane.setPreferredSize(TREE_SIZE);
220 workspace_pane.setSize(TREE_SIZE);
221
222 workspace_label = new JLabel();
223 workspace_label.setOpaque(true);
224 workspace_label.setBackground(Configuration.getColor("coloring.workspace_heading_background", false));
225 workspace_label.setForeground(Configuration.getColor("coloring.workspace_heading_foreground", false));
226 Dictionary.registerText(workspace_label, "Collection.Workspace");
227
228 workspace_tree = new WorkspaceTree(Utility.WORKSPACE_TREE);
229 group.add(workspace_tree);
230 workspace_tree.addFocusListener(this);
231 workspace_tree.addKeyListener(key_listener);
232 workspace_tree.addMouseListener(mouse_listener);
233 workspace_tree.addMouseListener(Gatherer.g_man.foa_listener);
234 workspace_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
235 workspace_tree.putClientProperty("JTree.lineStyle", "Angled");
236 workspace_tree.setBackgroundNonSelectionColor(Configuration.getColor("coloring.workspace_tree_background", false));
237 workspace_tree.setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
238 workspace_tree.setBackgroundSelectionColor(Configuration.getColor("coloring.workspace_selection_background", false));
239 workspace_tree.setTextSelectionColor(Configuration.getColor("coloring.workspace_selection_foreground", false));
240 workspace_tree.setRootVisible(false);
241
242 workspace_scroll = new JScrollPane(workspace_tree);
243
244 workspace_filter = Gatherer.g_man.getFilter(workspace_tree);
245 workspace_filter.setBackground(Configuration.getColor("coloring.workspace_heading_background", false));
246 workspace_filter.setEditable(Configuration.getMode() > Configuration.LIBRARIAN_MODE);
247 Dictionary.registerTooltip(workspace_filter.getComboBox(), "Collection.Filter_Tooltip");
248
249 // Collection Tree
250 collection_pane = new JPanel();
251 collection_pane.setMinimumSize(MIN_SIZE);
252 collection_pane.setPreferredSize(TREE_SIZE);
253 collection_pane.setSize(TREE_SIZE);
254
255 collection_label = new JLabel();
256 collection_label.setOpaque(true);
257 Dictionary.registerText(collection_label, "Collection.No_Collection");
258
259 collection_tree = new CollectionTree(Utility.COLLECTION_TREE, Gatherer.c_man.getCollectionTreeModel(), true);
260 collection_tree.setEnabled(Gatherer.c_man.getCollectionTreeModel() != null);
261 group.add(collection_tree);
262 collection_tree.addFocusListener(this);
263 collection_tree.addKeyListener(key_listener);
264 collection_tree.addMouseListener(mouse_listener);
265 collection_tree.addMouseListener(Gatherer.g_man.foa_listener);
266 collection_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
267 collection_tree.putClientProperty("JTree.lineStyle", "Angled");
268 collection_tree.setBackgroundNonSelectionColor(Configuration.getColor("coloring.collection_tree_background", false));
269 collection_tree.setTextNonSelectionColor(Configuration.getColor("coloring.collection_tree_foreground", false));
270 collection_tree.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
271 collection_tree.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
272 collection_tree.setRootVisible(false);
273
274 collection_scroll = new JScrollPane(collection_tree);
275
276 collection_filter = Gatherer.g_man.getFilter(collection_tree);
277 collection_filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
278 collection_filter.setEditable(Configuration.getMode() > Configuration.LIBRARIAN_MODE);
279 Dictionary.registerTooltip(collection_filter.getComboBox(), "Collection.Filter_Tooltip");
280
281 tree_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
282
283 // Status pane
284 control_pane = new JPanel();
285
286 JPanel inner_pane = new JPanel();
287 inner_pane.setSize(STATUS_SIZE);
288
289 JPanel file_pane = new JPanel();
290 JPanel progress_pane = new JPanel();
291 JLabel file_status = file_queue.getFileStatus();
292
293 JProgressBar progress_bar = file_queue.getProgressBar();
294
295 JPanel button_pane = new JPanel();
296
297 bin_button = new RecycleBin();
298 bin_button.addActionListener(this);
299 bin_button.setEnabled(false);
300 bin_button.setMinimumSize(MIN_SIZE);
301 bin_button.setPreferredSize(MIN_SIZE);
302 Dictionary.registerTooltip(bin_button, "Collection.Delete_Tooltip");
303 group.add(bin_button);
304
305 // Layout Components.
306 workspace_pane.setLayout(new BorderLayout());
307 workspace_pane.add(workspace_label, BorderLayout.NORTH);
308 workspace_pane.add(workspace_scroll, BorderLayout.CENTER);
309 workspace_pane.add(workspace_filter, BorderLayout.SOUTH);
310
311 collection_pane.setLayout(new BorderLayout());
312 collection_pane.add(collection_label, BorderLayout.NORTH);
313 collection_pane.add(collection_scroll, BorderLayout.CENTER);
314 collection_pane.add(collection_filter, BorderLayout.SOUTH);
315
316 tree_pane.add(workspace_pane, JSplitPane.LEFT);
317 tree_pane.add(collection_pane, JSplitPane.RIGHT);
318 tree_pane.setDividerLocation(TREE_SIZE.width - 10);
319
320 file_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
321 file_pane.setLayout(new BorderLayout());
322 file_pane.add(file_status, BorderLayout.CENTER);
323 file_pane.add(stop_action, BorderLayout.EAST);
324
325 progress_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
326 progress_pane.setLayout(new BorderLayout());
327 progress_pane.add(progress_bar, BorderLayout.CENTER);
328
329 inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10,10,10,10), BorderFactory.createLoweredBevelBorder()));
330 inner_pane.setLayout(new GridLayout(2,1));
331 inner_pane.add(file_pane);
332 inner_pane.add(progress_pane);
333
334 button_pane.add(new_folder);
335 button_pane.add(bin_button);
336
337 control_pane.setLayout(new BorderLayout());
338 control_pane.add(inner_pane, BorderLayout.CENTER);
339 control_pane.add(button_pane, BorderLayout.EAST);
340
341 this.setLayout(new BorderLayout());
342 this.add(tree_pane, BorderLayout.CENTER);
343 this.add(control_pane, BorderLayout.SOUTH);
344 }
345 /** 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. */
346 public Rectangle expandPath(TreePath path) {
347 collection_tree.setImmediate(true);
348 collection_tree.scrollPathToVisible(path);
349 collection_tree.setSelectionPath(path);
350 collection_tree.setImmediate(false);
351 return collection_tree.getRowBounds(collection_tree.getRowForPath(path));
352 }
353 /** 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).
354 * @param event A <strong>FocusEvent</strong> containing details about the focus action performed.
355 */
356 public void focusGained(FocusEvent event) {
357 DefaultTreeCellRenderer def = new DefaultTreeCellRenderer();
358 DefaultTreeCellRenderer w = (DefaultTreeCellRenderer)workspace_tree.getCellRenderer();
359 DefaultTreeCellRenderer c = (DefaultTreeCellRenderer)collection_tree.getCellRenderer();
360 if(event.getSource() == workspace_tree) {
361 w.setBackgroundSelectionColor(def.getBackgroundSelectionColor());
362 c.setBackgroundSelectionColor(Color.lightGray);
363 }
364 else if(event.getSource() == collection_tree) {
365 c.setBackgroundSelectionColor(def.getBackgroundSelectionColor());
366 w.setBackgroundSelectionColor(Color.lightGray);
367 }
368 repaint();
369 }
370 /** Implementation side-effect, not used in any way.
371 * @param event A <strong>FocusEvent</strong> containing details about the focus action performed.
372 */
373 public void focusLost(FocusEvent event) {
374 }
375
376 /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
377 */
378 public void gainFocus() {
379 // Update the meta-audit view to show the current selection, if any.
380 Gatherer.g_man.meta_audit.setRecords(getCollectionTreeSelection());
381 }
382
383 /** Retrieve a list of the currently selected file records in the collection tree. */
384 private CollectionTreeNode[] getCollectionTreeSelection()
385 {
386 TreePath paths[] = collection_tree.getSelectionPaths();
387 CollectionTreeNode records[] = null;
388 if (paths != null) {
389 records = new CollectionTreeNode[paths.length];
390 for (int i = 0; i < records.length; i++) {
391 records[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
392 }
393 }
394 return records;
395 }
396
397
398 /** Called whenever the detail mode changes to ensure the filters are at an appropriate level (ie only editable by those that understand regular expression matching)
399 * @param mode the mode level as an int
400 */
401 public void modeChanged(int mode) {
402 collection_filter.setEditable(mode > Configuration.LIBRARIAN_MODE);
403 workspace_filter.setEditable(mode > Configuration.LIBRARIAN_MODE);
404 }
405
406
407 /** Refresh this pane, depending on what has just happened (refresh_reason). */
408 public void refresh(int refresh_reason, boolean collection_loaded)
409 {
410 if (collection_loaded) {
411 // Update collection label
412 Dictionary.registerText(collection_label, "Collection.Collection");
413 collection_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
414 collection_label.setForeground(Configuration.getColor("coloring.collection_heading_foreground", false));
415
416 // Update collection tree
417 if (refresh_reason == Gatherer.COLLECTION_OPENED) {
418 collection_tree.setModel(Gatherer.c_man.getCollectionTreeModel());
419 }
420
421 // Update collection filter
422 collection_filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
423 }
424 else {
425 // Update collection label
426 String[] args = new String[1];
427 args[0] = Dictionary.get("Collection.No_Collection");
428 Dictionary.registerText(collection_label, "Collection.Collection", args);
429 collection_label.setBackground(Color.lightGray);
430 collection_label.setForeground(Color.black);
431
432 // Update collection tree
433 collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Error")));
434
435 // Update collection filter
436 collection_filter.setBackground(Color.lightGray);
437 }
438
439 // Enable or disable the controls
440 workspace_tree.setEnabled(true);
441 collection_tree.setEnabled(collection_loaded);
442 collection_filter.setEnabled(collection_loaded);
443 bin_button.setEnabled(collection_loaded);
444 new_folder.setEnabled(collection_loaded);
445
446 // Ensure that this collection tree view is synchronized with all others
447 collection_tree_sync.add(collection_tree);
448 }
449
450
451 public void refreshCollectionTree(int refresh_reason) {
452 collection_tree.refresh(null);
453 }
454
455
456 public void refreshWorkspaceTree(int refresh_reason) {
457 workspace_tree.refresh(refresh_reason);
458 }
459
460
461 /** When a user right-clicks within the workspace and collection trees they are presented with a small popup menu of context based options. This class provides such functionality.
462 */
463 private class RightClickMenu
464 extends JPopupMenu
465 implements ActionListener {
466
467 /** The tree over which the right click action occurred. */
468 private DragTree tree = null;
469 /** The tree nodes selected when the right click action occurred. */
470 private TreePath[] selection_paths = null;
471 /** The file record over which the right click action occurred. */
472 private FileNode node = null;
473
474 private JMenuItem create_shortcut = null;
475 private JMenuItem delete_shortcut = null;
476 private JMenuItem collapse_folder = null;
477 private JMenuItem expand_folder = null;
478 private JMenuItem explode_metadata_database = null;
479 private JMenuItem delete = null;
480 private JMenuItem metaaudit = null;
481 private JMenuItem new_folder = null;
482 private JMenuItem open_externally = null;
483
484
485 private RightClickMenu(DragTree tree, MouseEvent event)
486 {
487 super();
488 this.tree = tree;
489
490 // Get the paths currently selected in the tree
491 selection_paths = tree.getSelectionPaths();
492
493 // Create an appropriate context menu, based on what is selected
494 buildContextMenu(selection_paths);
495
496 // Show the popup menu on screen
497 show(tree, event.getX(), event.getY());
498 }
499
500
501 private void buildContextMenu(TreePath[] selection_paths)
502 {
503 // If nothing is selected, only the new folder option is available...
504 if (selection_paths == null) {
505 // ... but only in the collection tree!
506 if (tree == collection_tree) {
507 new_folder = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_Folder"), KeyEvent.VK_N);
508 new_folder.addActionListener(this);
509 add(new_folder);
510
511 node = (CollectionTreeNode) tree.getModel().getRoot();
512 }
513
514 return;
515 }
516
517 DebugStream.println("Number of files/folders selected: " + selection_paths.length);
518
519 // Collection tree: display meta-audit option and delete options
520 if (tree == collection_tree) {
521 String[] args = new String[1];
522 args[0] = collection_tree.getSelectionDetails();
523 metaaudit = new JMenuItem(Dictionary.get("Menu.Metadata_View", args), KeyEvent.VK_A);
524 metaaudit.addActionListener(this);
525 add(metaaudit);
526
527 delete = new JMenuItem(Dictionary.get("CollectionPopupMenu.Delete"), KeyEvent.VK_D);
528 delete.addActionListener(this);
529 add(delete);
530 }
531
532 // Only meta-audit and delete are available if multiple items are selected...
533 if (selection_paths.length > 1) {
534 return;
535 }
536
537 TreePath path = selection_paths[0];
538 node = (FileNode) path.getLastPathComponent();
539
540 // ---- Options for file nodes ----
541 if (node.isLeaf()) {
542 // Explode metadata databases, for collection tree only, and for explodable files only
543 if (tree == collection_tree && ((CollectionTreeNode) node).isExplodable()) {
544 explode_metadata_database = new JMenuItem(Dictionary.get("Menu.Explode_Metadata_Database"), KeyEvent.VK_E);
545 explode_metadata_database.addActionListener(this);
546 add(explode_metadata_database);
547 }
548
549 // Open the file in an external program
550 open_externally = new JMenuItem(Dictionary.get("Menu.Open_Externally"), KeyEvent.VK_O);
551 open_externally.addActionListener(this);
552 add(open_externally);
553
554 return;
555 }
556
557 // ---- Options for folder nodes ----
558 // Collapse or expand, depending on current status
559 if (tree.isExpanded(path)) {
560 collapse_folder = new JMenuItem(Dictionary.get("Menu.Collapse"), KeyEvent.VK_C);
561 collapse_folder.addActionListener(this);
562 add(collapse_folder);
563 }
564 else {
565 expand_folder = new JMenuItem(Dictionary.get("Menu.Expand"), KeyEvent.VK_O);
566 expand_folder.addActionListener(this);
567 add(expand_folder);
568 }
569
570 // New folder option, for collection tree only
571 if (tree == collection_tree && !node.isReadOnly()) {
572 new_folder = new JMenuItem(Dictionary.get("CollectionPopupMenu.New_Folder"), KeyEvent.VK_N);
573 new_folder.addActionListener(this);
574 add(new_folder);
575 }
576
577 // Create/remove shortcut option, for workspace tree only
578 if (tree == workspace_tree) {
579 // The "built-in" folders can't be modified
580 String node_name = node.toString();
581 if (node_name.equals(Dictionary.get("Tree.World")) || node_name.equals(Dictionary.get("Tree.Root")) || node_name.equals(Dictionary.get("Tree.DownloadedFiles"))) {
582 return;
583 }
584
585 // You can unmap 1st level nodes
586 WorkspaceTreeNode root = (WorkspaceTreeNode) tree.getModel().getRoot();
587 if (root.getIndex(node) != -1) {
588 delete_shortcut = new JMenuItem(Dictionary.get("MappingPrompt.Unmap"), KeyEvent.VK_R);
589 delete_shortcut.addActionListener(this);
590 add(delete_shortcut);
591 }
592 // Or map any other level directories
593 else {
594 create_shortcut = new JMenuItem(Dictionary.get("MappingPrompt.Map"), KeyEvent.VK_S);
595 create_shortcut.addActionListener(this);
596 add(create_shortcut);
597 }
598 }
599 }
600
601
602 /** Called whenever one of the menu items is clicked, this method then causes the appropriate effect. */
603 public void actionPerformed(ActionEvent event)
604 {
605 Object source = event.getSource();
606
607 // Create shortcut
608 if (source == create_shortcut) {
609 MappingPrompt mp = new MappingPrompt(node.getFile());
610 mp.destroy();
611 }
612
613 // Delete shortcut
614 else if (source == delete_shortcut) {
615 workspace_tree.removeDirectoryMapping((WorkspaceTreeNode) node);
616 }
617
618 // Collapse folder
619 else if (source == collapse_folder) {
620 tree.collapsePath(selection_paths[0]);
621 }
622
623 // Expand folder
624 else if (source == expand_folder) {
625 tree.expandPath(selection_paths[0]);
626 }
627
628 // Explode metadata database
629 else if (source == explode_metadata_database) {
630 // !! TO DO: Open dialog with options to explode_metadata_databases.pl, then run it
631 }
632
633 // Delete
634 else if (source == delete) {
635 FileNode[] source_nodes = new FileNode[selection_paths.length];
636 for (int i = 0; i < selection_paths.length; i++) {
637 source_nodes[i] = (FileNode) selection_paths[i].getLastPathComponent();
638 }
639
640 // Fire a delete action
641 Gatherer.f_man.action(tree, source_nodes, bin_button, null);
642 }
643
644 // Meta-audit
645 else if (source == metaaudit) {
646 Gatherer.g_man.showMetaAuditBox();
647 }
648
649 // New folder
650 else if (source == new_folder) {
651 Gatherer.f_man.newFolder(tree, (CollectionTreeNode) node);
652 }
653
654 // Open in external program
655 else if (source == open_externally) {
656 Gatherer.self.spawnApplication(node.getFile());
657 }
658 }
659 }
660
661
662 /** This class listens for certain key presses, such as [Enter] or [Delete], and responds appropriately. */
663 private class KeyListenerImpl
664 extends KeyAdapter {
665 private boolean vk_left_pressed = false;
666 /** 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). */
667 public void keyReleased(KeyEvent event) {
668 ///ystem.err.println("Key Release detected. " + event.getKeyCode());
669 if(event.getKeyCode() == KeyEvent.VK_DELETE) {
670 // Get the selected files from the tree and removal them using the default dnd removal method.
671 // Find the active tree (you've made selections in).
672 DragTree tree = (DragTree) group.getActive();
673 // Fudge things a bit
674 group.setSource(tree);
675 // Determine the selection.
676 TreePath paths[] = tree.getSelectionPaths();
677 if(paths != null) {
678 FileNode[] source_nodes = new FileNode[paths.length];
679 for(int i = 0; i < source_nodes.length; i++) {
680 source_nodes[i] = (FileNode) paths[i].getLastPathComponent();
681 }
682 Gatherer.f_man.action(tree, source_nodes, bin_button, null);
683 source_nodes = null;
684 }
685 }
686 else if(event.getKeyCode() == KeyEvent.VK_ENTER) {
687 // Get the first selected file.
688 DragTree tree = (DragTree)event.getSource();
689 TreePath path = tree.getSelectionPath();
690 if(path != null) {
691 File file = ((FileNode)path.getLastPathComponent()).getFile();
692 if(file != null && file.isFile()) {
693 Gatherer.self.spawnApplication(file);
694 }
695 else {
696 if(!tree.isExpanded(path)) {
697 tree.expandPath(path);
698 }
699 else {
700 tree.collapsePath(path);
701 }
702 }
703 }
704 } else if (event.getKeyCode() == KeyEvent.VK_UP || event.getKeyCode() == KeyEvent.VK_DOWN) {
705 DragTree tree = (DragTree)event.getSource();
706 // we need to manually do the up and down selections
707 boolean up = (event.getKeyCode() == KeyEvent.VK_UP);
708 int current_row = tree.getLeadSelectionRow();
709 tree.setImmediate(true);
710 if (up) {
711 if (current_row > 0) {
712 tree.clearSelection();
713 tree.setSelectionRow(current_row-1);
714 }
715 } else {
716 if (current_row < tree.getRowCount()-1){
717 tree.clearSelection();
718 tree.setSelectionRow(current_row+1);
719 }
720 }
721 tree.setImmediate(false);
722 } else if (event.getKeyCode() == KeyEvent.VK_LEFT) {
723 // left key on a file shifts the selection to the parent folder
724 DragTree tree = (DragTree)event.getSource();
725 TreePath path = tree.getLeadSelectionPath();
726 if(path != null) {
727 File file = ((FileNode)path.getLastPathComponent()).getFile();
728 if(file != null) {
729 if (file.isFile() || vk_left_pressed) {
730 vk_left_pressed = false;
731 TreePath parent_path = path.getParentPath();
732 if (parent_path != null && parent_path.getParentPath() != null) {
733 // if this file is in the top level folder, don't move the focus
734 tree.setImmediate(true);
735 tree.clearSelection();
736 tree.setSelectionPath(parent_path);
737 tree.setImmediate(false);
738 }
739 }
740 }
741 }
742 }
743 }
744
745 // we need to watch for left clicks on an unopened folder - should shift the focus to teh parent folder. But because there is some other mysterious key listener that does opening and closing folders on right and left clicks, we must detect the situation before the other handler has done its job, and process it after.
746 public void keyPressed(KeyEvent event) {
747 if (event.getKeyCode() == KeyEvent.VK_LEFT) {
748 // left key on closed directory shifts the selection to the parent folder
749 DragTree tree = (DragTree)event.getSource();
750 TreePath path = tree.getLeadSelectionPath();
751 if(path == null) return;
752 File file = ((FileNode)path.getLastPathComponent()).getFile();
753 if(file == null) return;
754
755 if (file.isDirectory() && tree.isCollapsed(path)) {
756 vk_left_pressed = true;
757 }
758 }
759 }
760 }
761
762 /** This provides a small prompt for gathering addition details about a special directory mapping such as its symbolic name.
763 * Used when creating a new shortcut to a folder */
764 private class MappingPrompt
765 extends JDialog
766 implements ActionListener, KeyListener {
767 private boolean cancelled = false;
768 private JButton cancel_button = null;
769 private JButton ok_button = null;
770 private JTextField name_field = null;
771 public MappingPrompt(File file) {
772 super(Gatherer.g_man);
773 setModal(true);
774 setSize(DIALOG_SIZE);
775 Dictionary.setText(this, "MappingPrompt.Title");
776
777 // Creation
778 JPanel content_pane = (JPanel) getContentPane();
779 JPanel center_pane = new JPanel();
780 JPanel labels_pane = new JPanel();
781 JPanel fields_pane = new JPanel();
782
783 JLabel file_label = new JLabel();
784 Dictionary.setText(file_label, "MappingPrompt.File");
785 JLabel file_field = new JLabel(file.getAbsolutePath());
786
787 JLabel name_label = new JLabel();
788 Dictionary.setText(name_label, "MappingPrompt.Name");
789 name_field = new JTextField(file.getName());
790
791 JPanel button_pane = new JPanel();
792 ok_button = new GLIButton();
793 ok_button.setEnabled(name_field.getText().length() > 0);
794 ok_button.setMnemonic(KeyEvent.VK_O);
795 Dictionary.setBoth(ok_button, "General.OK", "General.OK_Tooltip");
796 cancel_button = new GLIButton();
797 cancel_button.setMnemonic(KeyEvent.VK_C);
798 Dictionary.setBoth(cancel_button, "General.Cancel", "General.Cancel_Tooltip");
799
800 // Connection
801 cancel_button.addActionListener(this);
802 ok_button.addActionListener(this);
803 name_field.addKeyListener(this);
804
805 // Layout
806 labels_pane.setLayout(new GridLayout(2,1,5,0));
807 labels_pane.add(file_label);
808 labels_pane.add(name_label);
809
810 fields_pane.setLayout(new GridLayout(2,1,5,0));
811 fields_pane.add(file_field);
812 fields_pane.add(name_field);
813
814 center_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
815 center_pane.setLayout(new BorderLayout(5,0));
816 center_pane.add(labels_pane, BorderLayout.WEST);
817 center_pane.add(fields_pane, BorderLayout.CENTER);
818
819 button_pane.setLayout(new GridLayout(1,2,5,5));
820 button_pane.add(ok_button);
821 button_pane.add(cancel_button);
822
823 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
824 content_pane.setLayout(new BorderLayout());
825 content_pane.add(center_pane, BorderLayout.CENTER);
826 content_pane.add(button_pane, BorderLayout.SOUTH);
827 // Display
828 Dimension screen_size = Configuration.screen_size;
829 setLocation((screen_size.width - DIALOG_SIZE.width) / 2, (screen_size.height - DIALOG_SIZE.height) / 2);
830 setVisible(true);
831 // If not cancelled create mapping.
832 if (!cancelled) {
833 workspace_tree.addDirectoryMapping(name_field.getText(), file);
834 }
835 }
836 public void actionPerformed(ActionEvent event) {
837 if(event.getSource() == cancel_button) {
838 cancelled = true;
839 }
840 dispose();
841 }
842 public void destroy() {
843 cancel_button = null;
844 ok_button = null;
845 name_field = null;
846 }
847 public void keyPressed(KeyEvent event) {
848 }
849 public void keyReleased(KeyEvent event) {
850 ok_button.setEnabled(name_field.getText().length() > 0);
851
852 }
853 public void keyTyped(KeyEvent event) {
854 }
855 }
856
857 /** This class listens for mouse clicks and responds right mouse button clicks (popup menu). */
858 private class MouseListenerImpl
859 extends MouseAdapter {
860 /** 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. */
861 public void mouseClicked(MouseEvent event) {
862 if (SwingUtilities.isRightMouseButton(event)) {
863 new RightClickMenu((DragTree) event.getSource(), event);
864 }
865 }
866 }
867}
Note: See TracBrowser for help on using the repository browser.