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

Last change on this file since 7183 was 7163, checked in by kjdon, 20 years ago

changed some shortcut keys for the right mouse click menu

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