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

Last change on this file since 6394 was 6318, checked in by jmt12, 21 years ago

Changed JButtons for GLIButtons, which know whether they should paint their background depending on what platform they are run on, and finished keyboard shortcuts

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