1 | /**
|
---|
2 | *############################################################################
|
---|
3 | * A component of the Greenstone Librarian Interface, part of the Greenstone
|
---|
4 | * digital library suite from the New Zealand Digital Library Project at the
|
---|
5 | * University of Waikato, New Zealand.
|
---|
6 | *
|
---|
7 | * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
|
---|
8 | * Based on code by John Thompson
|
---|
9 | *
|
---|
10 | * Copyright (C) 2005 New Zealand Digital Library Project
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or modify
|
---|
13 | * it under the terms of the GNU General Public License as published by
|
---|
14 | * the Free Software Foundation; either version 2 of the License, or
|
---|
15 | * (at your option) any later version.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful,
|
---|
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
20 | * GNU General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, write to the Free Software
|
---|
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
25 | *############################################################################
|
---|
26 | */
|
---|
27 |
|
---|
28 | package org.greenstone.gatherer.gui;
|
---|
29 |
|
---|
30 |
|
---|
31 | import java.awt.*;
|
---|
32 | import java.awt.event.*;
|
---|
33 | import java.io.*;
|
---|
34 | import java.util.*;
|
---|
35 | import javax.swing.*;
|
---|
36 | import javax.swing.event.*;
|
---|
37 | import javax.swing.text.*;
|
---|
38 | import javax.swing.tree.*;
|
---|
39 | import org.greenstone.gatherer.Configuration;
|
---|
40 | import org.greenstone.gatherer.DebugStream;
|
---|
41 | import org.greenstone.gatherer.Dictionary;
|
---|
42 | import org.greenstone.gatherer.Gatherer;
|
---|
43 | import org.greenstone.gatherer.collection.CollectionTree;
|
---|
44 | import org.greenstone.gatherer.collection.CollectionTreeNode;
|
---|
45 | import org.greenstone.gatherer.gui.tree.DragTree;
|
---|
46 | import org.greenstone.gatherer.metadata.MetadataElement;
|
---|
47 | import org.greenstone.gatherer.metadata.MetadataValue;
|
---|
48 | import org.greenstone.gatherer.metadata.MetadataValueTableEntry;
|
---|
49 | import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
|
---|
50 | import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
|
---|
51 | import org.greenstone.gatherer.util.DragGroup;
|
---|
52 | import org.greenstone.gatherer.util.TreeSynchronizer;
|
---|
53 |
|
---|
54 |
|
---|
55 | /** Provides a view of controls for the editing of metadata.
|
---|
56 | */
|
---|
57 | public class EnrichPane
|
---|
58 | extends JPanel
|
---|
59 | implements TreeSelectionListener
|
---|
60 | {
|
---|
61 | static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
|
---|
62 | static private Dimension COLLECTION_TREE_SIZE = new Dimension(250, 500);
|
---|
63 |
|
---|
64 | /** The collection tree. */
|
---|
65 | private CollectionTree collection_tree = null;
|
---|
66 | /** The currently reported selection. */
|
---|
67 | private CollectionTreeNode[] file_nodes = null;
|
---|
68 | /** Used to dynamically filter the collection tree. Synchronized with the same filter in the Gather view. */
|
---|
69 | private Filter collection_filter = null;
|
---|
70 | /** The label at the top of the collection tree, which shows the collection name. */
|
---|
71 | private JLabel collection_label;
|
---|
72 | /** The splitpane dividing the collection tree and the metadata editing controls. */
|
---|
73 | private JSplitPane external_split;
|
---|
74 | /** The metadata value table shows the metadata values that are currently assigned to a file. */
|
---|
75 | private MetadataValueTablePane metadata_value_table_pane = null;
|
---|
76 | /** The metadata value tree shows the metadata values that are currently assigned to a metadata element. */
|
---|
77 | private MetadataValueTreePane metadata_value_tree_pane = null;
|
---|
78 | /** Provide synchronization between the collection trees in this view and the collection pane view. */
|
---|
79 | private TreeSynchronizer collection_tree_sync = null;
|
---|
80 |
|
---|
81 |
|
---|
82 | /** Constructor.
|
---|
83 | * @param tree_sync The <strong>TreeSynchronizer</strong> to be used on the collection tree
|
---|
84 | */
|
---|
85 | public EnrichPane(TreeSynchronizer collection_tree_sync)
|
---|
86 | {
|
---|
87 | this.collection_tree_sync = collection_tree_sync;
|
---|
88 |
|
---|
89 | // Create the metadata value tree pane
|
---|
90 | metadata_value_tree_pane = new MetadataValueTreePane();
|
---|
91 | metadata_value_tree_pane.addMetadataValueTreeSelectionListener(new MetadataValueTreeSelectionListener());
|
---|
92 |
|
---|
93 | // Create metadata value table pane
|
---|
94 | metadata_value_table_pane = new MetadataValueTablePane();
|
---|
95 | metadata_value_table_pane.addMetadataValueTableListSelectionListener(new MetadataValueTableListSelectionListener());
|
---|
96 | metadata_value_table_pane.addMetadataValueTableMouseListener(new MetadataValueTableMouseListener());
|
---|
97 | metadata_value_table_pane.addMetadataValueTextFieldDocumentListener(new MetadataValueTextFieldDocumentListener());
|
---|
98 | metadata_value_table_pane.addMetadataValueTextFieldKeyListener(new MetadataValueTextFieldKeyListener());
|
---|
99 | }
|
---|
100 |
|
---|
101 |
|
---|
102 | /** Some actions can only occur after this panel has been displayed on-screen, so this method is provided to do exactly that. Such actions include the proportioning of the split panes and the setting of table column widths.
|
---|
103 | */
|
---|
104 | public void afterDisplay()
|
---|
105 | {
|
---|
106 | external_split.setDividerLocation(0.3);
|
---|
107 | }
|
---|
108 |
|
---|
109 |
|
---|
110 | /** Used to create, connect and layout the components to be shown on this control panel.
|
---|
111 | * @see org.greenstone.gatherer.Gatherer
|
---|
112 | * @see org.greenstone.gatherer.file.FileOpenActionListener
|
---|
113 | * @see org.greenstone.gatherer.gui.Filter
|
---|
114 | */
|
---|
115 | public void display()
|
---|
116 | {
|
---|
117 | // Creation
|
---|
118 | JPanel collection_pane = new JPanel(new BorderLayout());
|
---|
119 | collection_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
120 | collection_pane.setPreferredSize(COLLECTION_TREE_SIZE);
|
---|
121 |
|
---|
122 | external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
---|
123 |
|
---|
124 | collection_label = new JLabel();
|
---|
125 | Dictionary.registerText(collection_label, "Collection.No_Collection");
|
---|
126 | collection_label.setOpaque(true);
|
---|
127 |
|
---|
128 | DragGroup group = new DragGroup();
|
---|
129 | collection_tree = new CollectionTree("Enrich", Gatherer.c_man.getCollectionTreeModel(), false);
|
---|
130 | group.add(collection_tree);
|
---|
131 | collection_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
---|
132 | collection_tree.putClientProperty("JTree.lineStyle", "Angled");
|
---|
133 | collection_tree.addMouseListener(Gatherer.g_man.foa_listener);
|
---|
134 | collection_tree.addMouseListener(new RightButtonListener());
|
---|
135 | collection_tree.addTreeSelectionListener(this);
|
---|
136 | collection_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
|
---|
137 | collection_tree.setBackgroundNonSelectionColor(Configuration.getColor("coloring.collection_tree_background", false));
|
---|
138 | collection_tree.setTextNonSelectionColor(Configuration.getColor("coloring.collection_tree_foreground", false));
|
---|
139 | collection_tree.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
|
---|
140 | collection_tree.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
|
---|
141 | collection_tree.setRootVisible(false);
|
---|
142 |
|
---|
143 | KeyListenerImpl key_listener = new KeyListenerImpl();
|
---|
144 | collection_tree.addKeyListener(key_listener);
|
---|
145 | JScrollPane collection_scroll = new JScrollPane(collection_tree);
|
---|
146 |
|
---|
147 | collection_filter = Gatherer.g_man.getFilter(collection_tree);
|
---|
148 | collection_filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
|
---|
149 | collection_filter.setEditable(Configuration.getMode() > Configuration.LIBRARIAN_MODE);
|
---|
150 | Dictionary.registerTooltip(collection_filter.getComboBox(), "Collection.Filter_Tooltip");
|
---|
151 |
|
---|
152 | // Layout
|
---|
153 | collection_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createLoweredBevelBorder()));
|
---|
154 | collection_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
155 | collection_pane.setPreferredSize(new Dimension(Gatherer.g_man.getSize().width / 3, Gatherer.g_man.getSize().height));
|
---|
156 |
|
---|
157 | // Collection Pane
|
---|
158 | collection_pane.add(collection_label, BorderLayout.NORTH);
|
---|
159 | collection_pane.add(collection_scroll, BorderLayout.CENTER);
|
---|
160 | collection_pane.add(collection_filter, BorderLayout.SOUTH);
|
---|
161 |
|
---|
162 | JSplitPane metadata_editing_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
---|
163 | metadata_editing_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
|
---|
164 | metadata_editing_pane.setDividerSize(8);
|
---|
165 |
|
---|
166 | metadata_editing_pane.add(metadata_value_table_pane, JSplitPane.TOP);
|
---|
167 | metadata_editing_pane.add(metadata_value_tree_pane, JSplitPane.BOTTOM);
|
---|
168 | metadata_editing_pane.setDividerLocation(250);
|
---|
169 |
|
---|
170 | external_split.add(collection_pane, JSplitPane.LEFT);
|
---|
171 | external_split.add(metadata_editing_pane, JSplitPane.RIGHT);
|
---|
172 |
|
---|
173 | this.setLayout(new BorderLayout());
|
---|
174 | this.add(external_split, BorderLayout.CENTER);
|
---|
175 | }
|
---|
176 |
|
---|
177 |
|
---|
178 | /** Called to inform this pane that it has just gained focus as an effect of the user clicking on its tab
|
---|
179 | */
|
---|
180 | public void gainFocus()
|
---|
181 | {
|
---|
182 | // If there is nothing in the collection tree there is nothing to do
|
---|
183 | if (collection_tree == null || collection_tree.getRowCount() == 0) {
|
---|
184 | return;
|
---|
185 | }
|
---|
186 |
|
---|
187 | // Select the first node in the tree if nothing is already selected
|
---|
188 | if (collection_tree.getSelectionPaths() == null) {
|
---|
189 | collection_tree.setImmediate(true);
|
---|
190 | collection_tree.setSelectionRow(0);
|
---|
191 | collection_tree.setImmediate(false);
|
---|
192 | return;
|
---|
193 | }
|
---|
194 |
|
---|
195 | // Force all of the controls to be updated
|
---|
196 | valueChanged(null);
|
---|
197 | }
|
---|
198 |
|
---|
199 |
|
---|
200 | /** Called to inform this pane that it has just lost focus as an effect of the user clicking on another tab
|
---|
201 | */
|
---|
202 | public void loseFocus()
|
---|
203 | {
|
---|
204 | // Very important: make sure metadata value is saved before leaving the pane
|
---|
205 | metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
|
---|
206 |
|
---|
207 | // Upload the modified metadata.xml files to the server now, if we're using one
|
---|
208 | MetadataXMLFileManager.uploadModifiedMetadataXMLFiles();
|
---|
209 | }
|
---|
210 |
|
---|
211 |
|
---|
212 | /** Called whenever the detail mode changes to ensure the filters are at an appropriate level (ie only editable by those that understand regular expression matching)
|
---|
213 | * @param mode the mode level as an int
|
---|
214 | */
|
---|
215 | public void modeChanged(int mode)
|
---|
216 | {
|
---|
217 | collection_filter.setEditable(mode > Configuration.LIBRARIAN_MODE);
|
---|
218 | }
|
---|
219 |
|
---|
220 |
|
---|
221 | /** Refresh this pane, depending on what has just happened (refresh_reason). */
|
---|
222 | public void refresh(int refresh_reason, boolean collection_loaded)
|
---|
223 | {
|
---|
224 | if (collection_loaded) {
|
---|
225 | // Update collection label
|
---|
226 | Dictionary.registerText(collection_label, "Collection.Collection");
|
---|
227 | collection_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
|
---|
228 | collection_label.setForeground(Configuration.getColor("coloring.collection_heading_foreground", false));
|
---|
229 |
|
---|
230 | // Update collection tree
|
---|
231 | if (refresh_reason == Gatherer.COLLECTION_OPENED) {
|
---|
232 | collection_tree.setModel(Gatherer.c_man.getCollectionTreeModel());
|
---|
233 | }
|
---|
234 |
|
---|
235 | // Update collection filter
|
---|
236 | collection_filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
|
---|
237 | }
|
---|
238 | else {
|
---|
239 | // Update collection label
|
---|
240 | Dictionary.registerText(collection_label, "Collection.No_Collection");
|
---|
241 | collection_label.setBackground(Color.lightGray);
|
---|
242 | collection_label.setForeground(Color.black);
|
---|
243 |
|
---|
244 | // Update collection tree
|
---|
245 | collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Error")));
|
---|
246 |
|
---|
247 | // Update collection filter
|
---|
248 | collection_filter.setBackground(Color.lightGray);
|
---|
249 | }
|
---|
250 |
|
---|
251 | // Enable or disable the controls
|
---|
252 | collection_tree.setEnabled(collection_loaded);
|
---|
253 | collection_filter.setEnabled(collection_loaded);
|
---|
254 |
|
---|
255 | // Ensure that this collection tree view is synchronized with all others
|
---|
256 | collection_tree_sync.add(collection_tree);
|
---|
257 |
|
---|
258 | // Force the metadata table to be rebuilt (for switching extracted metadata on or off)
|
---|
259 | if (refresh_reason == Gatherer.PREFERENCES_CHANGED) {
|
---|
260 | metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
|
---|
261 | }
|
---|
262 | }
|
---|
263 |
|
---|
264 |
|
---|
265 | /** Called whenever the collection tree selection changes. This causes the metadata value table to be rebuilt. */
|
---|
266 | public void valueChanged(TreeSelectionEvent event)
|
---|
267 | {
|
---|
268 | // Nothing selected in the collection tree
|
---|
269 | if (collection_tree.getSelectionCount() == 0) {
|
---|
270 | file_nodes = null;
|
---|
271 | }
|
---|
272 |
|
---|
273 | // Some files selected in the collection tree
|
---|
274 | else {
|
---|
275 | TreePath paths[] = collection_tree.getSelectionPaths();
|
---|
276 | file_nodes = new CollectionTreeNode[paths.length];
|
---|
277 | for (int i = 0; i < paths.length; i++) {
|
---|
278 | file_nodes[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
|
---|
279 | }
|
---|
280 | }
|
---|
281 |
|
---|
282 | // Update the metadata value table (and consequently, the metadata value tree)
|
---|
283 | metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
|
---|
284 | }
|
---|
285 |
|
---|
286 |
|
---|
287 | private class MetadataValueTableListSelectionListener
|
---|
288 | implements ListSelectionListener
|
---|
289 | {
|
---|
290 | public void valueChanged(ListSelectionEvent list_selection_event)
|
---|
291 | {
|
---|
292 | // We only want to handle one event per selection, so wait for the value to stabilise
|
---|
293 | if (list_selection_event.getValueIsAdjusting()) {
|
---|
294 | return;
|
---|
295 | }
|
---|
296 |
|
---|
297 | // Update the metadata value tree for the current table selection
|
---|
298 | metadata_value_tree_pane.rebuild(metadata_value_table_pane.getSelectedMetadataValueTableEntry());
|
---|
299 | }
|
---|
300 | }
|
---|
301 |
|
---|
302 |
|
---|
303 | private class MetadataValueTableMouseListener
|
---|
304 | extends MouseAdapter
|
---|
305 | {
|
---|
306 | public void mouseClicked(MouseEvent mouse_event)
|
---|
307 | {
|
---|
308 | // We're only interested in clicks on the inherited column
|
---|
309 | if (metadata_value_table_pane.isMouseEventForInheritedMetadataValueTableColumn(mouse_event) == false) {
|
---|
310 | return;
|
---|
311 | }
|
---|
312 |
|
---|
313 | // If the selected metadata is inherited, switch to the folder it came from
|
---|
314 | MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table_pane.getSelectedMetadataValueTableEntry();
|
---|
315 | if (selected_metadata_value_table_entry.isInheritedMetadata()) {
|
---|
316 | collection_tree.setSelection(selected_metadata_value_table_entry.getFolderMetadataInheritedFrom());
|
---|
317 | }
|
---|
318 | }
|
---|
319 | }
|
---|
320 |
|
---|
321 |
|
---|
322 | private class MetadataValueTextFieldDocumentListener
|
---|
323 | implements DocumentListener
|
---|
324 | {
|
---|
325 | /** Gives notification that an attribute or set of attributes changed */
|
---|
326 | public void changedUpdate(DocumentEvent document_event) {
|
---|
327 | validate(document_event);
|
---|
328 | }
|
---|
329 |
|
---|
330 | /** Gives notification that there was an insert into the document */
|
---|
331 | public void insertUpdate(DocumentEvent document_event) {
|
---|
332 | validate(document_event);
|
---|
333 | }
|
---|
334 |
|
---|
335 | /** Gives notification that a portion of the document has been removed */
|
---|
336 | public void removeUpdate(DocumentEvent document_event) {
|
---|
337 | validate(document_event);
|
---|
338 | }
|
---|
339 |
|
---|
340 |
|
---|
341 | /** Ensures that the value tree is updated in response to changes in the value text field */
|
---|
342 | private void validate(DocumentEvent document_event)
|
---|
343 | {
|
---|
344 | try {
|
---|
345 | Document document = document_event.getDocument();
|
---|
346 | String metadata_value_string = document.getText(0, document.getLength());
|
---|
347 | metadata_value_tree_pane.selectBestPathForMetadataValue(metadata_value_string);
|
---|
348 | }
|
---|
349 | catch (Exception exception) {
|
---|
350 | DebugStream.printStackTrace(exception);
|
---|
351 | }
|
---|
352 | }
|
---|
353 | }
|
---|
354 |
|
---|
355 |
|
---|
356 | private class MetadataValueTextFieldKeyListener
|
---|
357 | extends KeyAdapter
|
---|
358 | {
|
---|
359 | /** Gives notification of key events on the text field */
|
---|
360 | public void keyPressed(KeyEvent key_event)
|
---|
361 | {
|
---|
362 | // Tab: Auto-complete what is selected in the metadata value tree
|
---|
363 | if (key_event.getKeyCode() == KeyEvent.VK_TAB) {
|
---|
364 | MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
|
---|
365 | if (selected_metadata_value_tree_node != null) {
|
---|
366 | metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
|
---|
367 | }
|
---|
368 |
|
---|
369 | // We do not want this event to be processed by the table also
|
---|
370 | key_event.consume();
|
---|
371 | }
|
---|
372 |
|
---|
373 | // Enter: save the current value then add a blank row for the selected metadata element
|
---|
374 | if (key_event.getKeyCode() == KeyEvent.VK_ENTER) {
|
---|
375 | metadata_value_table_pane.stopEditingAndAddBlankRowForSelectedMetadataElement();
|
---|
376 | }
|
---|
377 | }
|
---|
378 | }
|
---|
379 |
|
---|
380 |
|
---|
381 | private class MetadataValueTreeSelectionListener
|
---|
382 | implements TreeSelectionListener
|
---|
383 | {
|
---|
384 | public void valueChanged(TreeSelectionEvent tree_selection_event)
|
---|
385 | {
|
---|
386 | // When a node is selected in the tree, fill the metadata value text field with the selected node's value
|
---|
387 | MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
|
---|
388 | if (selected_metadata_value_tree_node != null) {
|
---|
389 | metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
|
---|
390 | }
|
---|
391 | }
|
---|
392 | }
|
---|
393 |
|
---|
394 |
|
---|
395 | /** This class listens for certain key presses, such as [Up] or [Down], -copied from Gather Pane */
|
---|
396 | private class KeyListenerImpl
|
---|
397 | extends KeyAdapter {
|
---|
398 | private boolean vk_left_pressed = false;
|
---|
399 | /** 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). */
|
---|
400 | public void keyReleased(KeyEvent event) {
|
---|
401 | ///ystem.err.println("Key Release detected. " + event.getKeyCode());
|
---|
402 | if (event.getKeyCode() == KeyEvent.VK_UP || event.getKeyCode() == KeyEvent.VK_DOWN) {
|
---|
403 | DragTree tree = (DragTree)event.getSource();
|
---|
404 | // we need to manually do the up and down selections
|
---|
405 | boolean up = (event.getKeyCode() == KeyEvent.VK_UP);
|
---|
406 | int current_row = tree.getLeadSelectionRow();
|
---|
407 | tree.setImmediate(true);
|
---|
408 | if (up) {
|
---|
409 | if (current_row > 0) {
|
---|
410 | tree.clearSelection();
|
---|
411 | tree.setSelectionRow(current_row-1);
|
---|
412 | }
|
---|
413 | } else {
|
---|
414 | if (current_row < tree.getRowCount()-1){
|
---|
415 | tree.clearSelection();
|
---|
416 | tree.setSelectionRow(current_row+1);
|
---|
417 | }
|
---|
418 | }
|
---|
419 | tree.setImmediate(false);
|
---|
420 | }
|
---|
421 | else if (event.getKeyCode() == KeyEvent.VK_LEFT) {
|
---|
422 | // left key on a file shifts the selection to the parent folder
|
---|
423 | DragTree tree = (DragTree)event.getSource();
|
---|
424 | TreePath path = tree.getLeadSelectionPath();
|
---|
425 | if(path != null) {
|
---|
426 | File file = ((CollectionTreeNode) path.getLastPathComponent()).getFile();
|
---|
427 | if(file != null) {
|
---|
428 | if (file.isFile() || vk_left_pressed) {
|
---|
429 | vk_left_pressed = false;
|
---|
430 | TreePath parent_path = path.getParentPath();
|
---|
431 | if (parent_path != null && parent_path.getParentPath() != null) {
|
---|
432 | // if this file is in the top level folder, don't move the focus
|
---|
433 | tree.setImmediate(true);
|
---|
434 | tree.clearSelection();
|
---|
435 | tree.setSelectionPath(parent_path);
|
---|
436 | tree.setImmediate(false);
|
---|
437 | }
|
---|
438 | }
|
---|
439 | }
|
---|
440 | }
|
---|
441 | }
|
---|
442 | }
|
---|
443 |
|
---|
444 | // we need to watch for left clicks on an unopened folder - should shift the focus to teh parent folder. But because there is some other mysterious key listener that does opening and closing folders on right and left clicks, we must detect the situation before the other handler has done its job, and process it after.
|
---|
445 | public void keyPressed(KeyEvent event) {
|
---|
446 | if (event.getKeyCode() == KeyEvent.VK_LEFT) {
|
---|
447 | // left key on closed directory shifts the selection to the parent folder
|
---|
448 | DragTree tree = (DragTree)event.getSource();
|
---|
449 | TreePath path = tree.getLeadSelectionPath();
|
---|
450 | if(path == null) return;
|
---|
451 | File file = ((CollectionTreeNode) path.getLastPathComponent()).getFile();
|
---|
452 | if(file == null) return;
|
---|
453 |
|
---|
454 | if (file.isDirectory() && tree.isCollapsed(path)) {
|
---|
455 | vk_left_pressed = true;
|
---|
456 | }
|
---|
457 | }
|
---|
458 | }
|
---|
459 | }
|
---|
460 |
|
---|
461 |
|
---|
462 | /** A listener for right mouse button clicks over the collection tree. */
|
---|
463 | private class RightButtonListener
|
---|
464 | extends MouseAdapter {
|
---|
465 | /** Called whenever a mouse click occurs (right or left) over a target component.
|
---|
466 | * @param event A <strong>MouseEvent</strong> containing further information about the mouse click action.
|
---|
467 | * @see org.greenstone.gatherer.gui.EnrichPane.RightButtonMenu
|
---|
468 | */
|
---|
469 | public void mouseClicked(MouseEvent event) {
|
---|
470 | if (SwingUtilities.isRightMouseButton(event)) {
|
---|
471 | new RightButtonMenu(event);
|
---|
472 | }
|
---|
473 | }
|
---|
474 | }
|
---|
475 |
|
---|
476 |
|
---|
477 | /** When a user right-clicks within the collection tree they are presented with a small popup menu of context based options. This class provides such functionality.
|
---|
478 | */
|
---|
479 | private class RightButtonMenu
|
---|
480 | extends JPopupMenu
|
---|
481 | implements ActionListener {
|
---|
482 |
|
---|
483 | /** The tree over which the right click action occurred. */
|
---|
484 | private DragTree tree = null;
|
---|
485 | /** The tree nodes selected when the right click action occurred. */
|
---|
486 | private TreePath[] selection_paths = null;
|
---|
487 | /** The file record over which the right click action occurred. */
|
---|
488 | private CollectionTreeNode node = null;
|
---|
489 |
|
---|
490 | private JMenuItem collapse_folder = null;
|
---|
491 | private JMenuItem expand_folder = null;
|
---|
492 | private JMenuItem metaaudit = null;
|
---|
493 | private JMenuItem open_externally = null;
|
---|
494 |
|
---|
495 |
|
---|
496 | private RightButtonMenu(MouseEvent event)
|
---|
497 | {
|
---|
498 | super();
|
---|
499 | this.tree = collection_tree;
|
---|
500 |
|
---|
501 | // Note we have to use setImmediate() with the set selction paths
|
---|
502 | // otherwise the selection doesn't get updated until after the
|
---|
503 | // popup comes up.
|
---|
504 |
|
---|
505 | // the right click position
|
---|
506 | TreePath right_click_path = tree.getPathForLocation(event.getX(), event.getY());
|
---|
507 | if (right_click_path == null) {
|
---|
508 | // user has clicked outside of the tree, clear the selection
|
---|
509 | selection_paths = null;
|
---|
510 | tree.setImmediate(true);
|
---|
511 | tree.clearSelection();
|
---|
512 | tree.setImmediate(false);
|
---|
513 | } else {
|
---|
514 | // Get the paths currently selected in the tree
|
---|
515 | selection_paths = tree.getSelectionPaths();
|
---|
516 | if (selection_paths == null) {
|
---|
517 | // nothing currently selected - we shift the selection to
|
---|
518 | // the node that was right clicked on
|
---|
519 | selection_paths = new TreePath[1];
|
---|
520 | selection_paths[0] = right_click_path;
|
---|
521 | tree.setImmediate(true);
|
---|
522 | tree.setSelectionPath(right_click_path);
|
---|
523 | tree.setImmediate(false);
|
---|
524 | } else if (selection_paths.length == 1 && ! selection_paths[0].equals( right_click_path)) {
|
---|
525 | tree.setImmediate(true);
|
---|
526 | tree.clearSelection();
|
---|
527 | tree.setSelectionPath(right_click_path);
|
---|
528 | tree.setImmediate(false);
|
---|
529 | selection_paths[0] = right_click_path;
|
---|
530 | } else {
|
---|
531 | // we had multiply selected paths in the tree.
|
---|
532 | // if we clicked on one of those paths, then use all the
|
---|
533 | // current selection, otherwise clear the selection and
|
---|
534 | // select the one we right clicked on
|
---|
535 | boolean clicked_in_selection = false;
|
---|
536 | for (int i=0; i<selection_paths.length; i++) {
|
---|
537 | if (selection_paths[i].equals(right_click_path)) {
|
---|
538 | clicked_in_selection = true;
|
---|
539 | break;
|
---|
540 | }
|
---|
541 | }
|
---|
542 | if (!clicked_in_selection) {
|
---|
543 | // want the tree to update right away
|
---|
544 | tree.setImmediate(true);
|
---|
545 | tree.clearSelection();
|
---|
546 | tree.setSelectionPath(right_click_path);
|
---|
547 | tree.setImmediate(false);
|
---|
548 | selection_paths = new TreePath[1];
|
---|
549 | selection_paths[0] = right_click_path;
|
---|
550 | }
|
---|
551 | }
|
---|
552 | }
|
---|
553 | // finally we have the correct selection paths!
|
---|
554 |
|
---|
555 | // Create an appropriate context menu, based on what is selected
|
---|
556 | buildContextMenu(selection_paths);
|
---|
557 |
|
---|
558 | // Show the popup menu on screen
|
---|
559 | show(tree, event.getX(), event.getY());
|
---|
560 | }
|
---|
561 |
|
---|
562 |
|
---|
563 | private void buildContextMenu(TreePath[] selection_paths)
|
---|
564 | {
|
---|
565 | // If nothing is selected, no options are available
|
---|
566 | if (selection_paths == null) {
|
---|
567 | return;
|
---|
568 | }
|
---|
569 |
|
---|
570 | DebugStream.println("Number of files/folders selected: " + selection_paths.length);
|
---|
571 |
|
---|
572 | // Always have meta-audit option
|
---|
573 | metaaudit = new JMenuItem(Dictionary.get("Menu.Metadata_View", collection_tree.getSelectionDetails()), KeyEvent.VK_A);
|
---|
574 | metaaudit.addActionListener(this);
|
---|
575 | add(metaaudit);
|
---|
576 |
|
---|
577 | // Only meta-audit is available if multiple items are selected...
|
---|
578 | if (selection_paths.length > 1) {
|
---|
579 | return;
|
---|
580 | }
|
---|
581 |
|
---|
582 | TreePath path = selection_paths[0];
|
---|
583 | node = (CollectionTreeNode) path.getLastPathComponent();
|
---|
584 |
|
---|
585 | // ---- Options for file nodes ----
|
---|
586 | if (node.isLeaf()) {
|
---|
587 | // Open the file in an external program
|
---|
588 | open_externally = new JMenuItem(Dictionary.get("Menu.Open_Externally"), KeyEvent.VK_O);
|
---|
589 | open_externally.addActionListener(this);
|
---|
590 | add(open_externally);
|
---|
591 | return;
|
---|
592 | }
|
---|
593 |
|
---|
594 | // ---- Options for folder nodes ----
|
---|
595 | // Collapse or expand, depending on current status
|
---|
596 | if (tree.isExpanded(path)) {
|
---|
597 | collapse_folder = new JMenuItem(Dictionary.get("Menu.Collapse"), KeyEvent.VK_C);
|
---|
598 | collapse_folder.addActionListener(this);
|
---|
599 | add(collapse_folder);
|
---|
600 | }
|
---|
601 | else {
|
---|
602 | expand_folder = new JMenuItem(Dictionary.get("Menu.Expand"), KeyEvent.VK_O);
|
---|
603 | expand_folder.addActionListener(this);
|
---|
604 | add(expand_folder);
|
---|
605 | }
|
---|
606 | }
|
---|
607 |
|
---|
608 |
|
---|
609 | /** Called whenever one of the menu items is clicked, this method then causes the appropriate effect. */
|
---|
610 | public void actionPerformed(ActionEvent event)
|
---|
611 | {
|
---|
612 | Object source = event.getSource();
|
---|
613 |
|
---|
614 | // Collapse folder
|
---|
615 | if (source == collapse_folder) {
|
---|
616 | tree.collapsePath(selection_paths[0]);
|
---|
617 | }
|
---|
618 |
|
---|
619 | // Expand folder
|
---|
620 | else if (source == expand_folder) {
|
---|
621 | tree.expandPath(selection_paths[0]);
|
---|
622 | }
|
---|
623 |
|
---|
624 | // Meta-audit
|
---|
625 | else if (source == metaaudit) {
|
---|
626 | Gatherer.g_man.showMetaAuditBox();
|
---|
627 | }
|
---|
628 |
|
---|
629 | // Open in external program
|
---|
630 | else if (source == open_externally) {
|
---|
631 | Gatherer.f_man.openFileInExternalApplication(node.getFile());
|
---|
632 | }
|
---|
633 | }
|
---|
634 | }
|
---|
635 | }
|
---|