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

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

Removed all occurrences of classes explicitly importing other classes in the same package.

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