source: tags/gsdl-2_70u-distribution/gli/src/org/greenstone/gatherer/gui/GatherPane.java@ 11745

Last change on this file since 11745 was 11745, checked in by (none), 18 years ago

This commit was manufactured by cvs2svn to create tag
'gsdl-2_70u-distribution'.

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