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

Last change on this file since 6822 was 6590, checked in by jmt12, 20 years ago

Started effecting the changes requested by Ian and David. So far I've removed the complex arguments, and have ensured that the path is correct for downloads without page requisites. I've also tried to get the workspace tree to update properly, and it is much closer than before but it is now temporarily displaying the same node twice.

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