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

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

Replaced all "Gatherer.config" with "Configuration".

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