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

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

Many more tooltips and improvements to the Dictionary. Still more to come.

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