source: trunk/gli/src/org/greenstone/gatherer/gui/CollectionPane.java@ 5773

Last change on this file since 5773 was 5773, checked in by mdewsnip, 21 years ago

Fixed bug 98: popup menu appears when right-clicking even when no file has been selected.

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