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