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 |
|
---|
40 | import java.awt.*;
|
---|
41 | import java.awt.event.*;
|
---|
42 | import java.io.*;
|
---|
43 | import java.util.*;
|
---|
44 | import javax.swing.*;
|
---|
45 | import javax.swing.event.*;
|
---|
46 | import javax.swing.table.*;
|
---|
47 | import javax.swing.tree.*;
|
---|
48 | import org.greenstone.gatherer.Configuration;
|
---|
49 | import org.greenstone.gatherer.DebugStream;
|
---|
50 | import org.greenstone.gatherer.Dictionary;
|
---|
51 | import org.greenstone.gatherer.Gatherer;
|
---|
52 | import org.greenstone.gatherer.collection.CollectionManager;
|
---|
53 | import org.greenstone.gatherer.file.FileNode;
|
---|
54 | import org.greenstone.gatherer.gui.tree.DragTree;
|
---|
55 | import org.greenstone.gatherer.metadata.MetadataElement;
|
---|
56 | import org.greenstone.gatherer.metadata.MetadataValue;
|
---|
57 | import org.greenstone.gatherer.metadata.MetadataValueTableEntry;
|
---|
58 | import org.greenstone.gatherer.metadata.MetadataValueTableModel;
|
---|
59 | import org.greenstone.gatherer.metadata.MetadataValueTreeModel;
|
---|
60 | import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
|
---|
61 | import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
|
---|
62 | import org.greenstone.gatherer.util.DragGroup;
|
---|
63 | import org.greenstone.gatherer.util.PatternTokenizer;
|
---|
64 | import org.greenstone.gatherer.util.TreeSynchronizer;
|
---|
65 | import org.greenstone.gatherer.util.Utility;
|
---|
66 |
|
---|
67 |
|
---|
68 | /** Provides a view of controls for the editing of metadata. It makes use of several other important components such as a GTree, GTable and MetadataValueTree. While much of the gui content is left to these components, the EnrichPane is resposible for actioning metadata edit requests, listing for mouse clicks within its scope and other data functionality (such as providing a list of the selected files).
|
---|
69 | */
|
---|
70 | public class EnrichPane
|
---|
71 | extends JPanel
|
---|
72 | implements ActionListener, TreeSelectionListener {
|
---|
73 |
|
---|
74 | static private Dimension BUTTON_SIZE = new Dimension(190, 25);
|
---|
75 | static private Dimension CONTROL_SIZE = new Dimension(560, 240);
|
---|
76 | static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
|
---|
77 | static private Dimension TABLE_SIZE = new Dimension(560, 25);
|
---|
78 | static private Dimension TREE_SIZE = new Dimension(250, 500);
|
---|
79 | static private int MINIMUM_TABLE_HEADER_SIZE = 15;
|
---|
80 |
|
---|
81 | private MetadataValueTable metadata_value_table = null;
|
---|
82 | /** The layout manager used to display either the MetadataValueTable (when files are selected) or a placeholder panel */
|
---|
83 | private CardLayout metadata_value_table_card_layout = null;
|
---|
84 | /** The name of the panel containing the metadata table */
|
---|
85 | static final private String TABLE_CARD = "";
|
---|
86 | /** The name of the panel containing the "no files selected" placeholder for the metadata table */
|
---|
87 | static final private String TABLE_CARD_NO_FILES_SELECTED = "No files selected";
|
---|
88 | /** The name of the panel containing the "no metadata available" placeholder for the metadata table */
|
---|
89 | static final private String TABLE_CARD_NO_METADATA_AVAILABLE = "No metadata available";
|
---|
90 |
|
---|
91 | /** The MetadataValueTree graphically shows the available metadata that has been previously assigned, and provides controls for adding, updating and removing metadata, depending on the user's selections in the other important components. */
|
---|
92 | private MetadataValueTree metadata_value_tree = null;
|
---|
93 | /** The layout manager used to display either the MetadataValueTree (when metadata is selected) or a placeholder panel */
|
---|
94 | private CardLayout metadata_value_tree_card_layout = null;
|
---|
95 | /** The name of the panel containing the metadata value tree */
|
---|
96 | static final private String TREE_CARD = "";
|
---|
97 | /** The name of the panel containing the "no metadata element selected" placeholder for the metadata table */
|
---|
98 | static final private String TREE_CARD_NO_METADATA_ELEMENT_SELECTED = "No metadata element selected";
|
---|
99 |
|
---|
100 | /** Used to dynamically filter the collection tree at user request. Note that this is synchronized with the collection tree filter in the Gather view. */
|
---|
101 | private Filter filter = null;
|
---|
102 | /** The currently reported selection. */
|
---|
103 | private FileNode[] file_nodes = null;
|
---|
104 | /** The button, which when clicked, adds metadata to the selected records. */
|
---|
105 | private JButton add;
|
---|
106 | /** The button, which when clicked, removes the selected metadata from the selected records. */
|
---|
107 | private JButton remove;
|
---|
108 | /** The button, which when clicked, updates the selected metadata from the selected records. */
|
---|
109 | private JButton update;
|
---|
110 | /** This button creates an editor dialog to allow for an expanded area to type in text. */
|
---|
111 | private JButton expand;
|
---|
112 | private JButton expand_for_extracted;
|
---|
113 | /** The label at the top of the collection tree, which shows the collection name. */
|
---|
114 | private JLabel collection_label;
|
---|
115 | /** The panel in which the metadata value card layout resides. */
|
---|
116 | private JPanel control_pane;
|
---|
117 | /** The panel in which the metadata table card layout resides. */
|
---|
118 | private JPanel table_card_pane;
|
---|
119 | /** The splitpane dividing the collection tree and the metadata based controls. */
|
---|
120 | private JSplitPane external_split;
|
---|
121 | /** The label shown at the top of the metadata table detailing the current selection statistics. */
|
---|
122 | private JTextField table_label;
|
---|
123 | /** A reference to the collection tree. */
|
---|
124 | private DragTree collection_tree;
|
---|
125 | /** Provide synchronization between the collection trees in this view and the collection pane view. */
|
---|
126 | private TreeSynchronizer tree_sync = null;
|
---|
127 | private boolean metadata_edit_event = false;
|
---|
128 |
|
---|
129 |
|
---|
130 | /** Constructor.
|
---|
131 | * @param tree_sync The <strong>TreeSynchronizer</strong> to be used on the collection tree
|
---|
132 | */
|
---|
133 | public EnrichPane(TreeSynchronizer tree_sync)
|
---|
134 | {
|
---|
135 | this.tree_sync = tree_sync;
|
---|
136 |
|
---|
137 | add = new GLIButton();
|
---|
138 | add.addActionListener(this);
|
---|
139 | add.setEnabled(false);
|
---|
140 | add.setMnemonic(KeyEvent.VK_A);
|
---|
141 | add.setPreferredSize(BUTTON_SIZE);
|
---|
142 | Dictionary.registerBoth(add, "MetaEdit.Accumulate", "MetaEdit.Accumulate_Tooltip");
|
---|
143 |
|
---|
144 | update = new GLIButton();
|
---|
145 | update.addActionListener(this);
|
---|
146 | update.setEnabled(false);
|
---|
147 | update.setMnemonic(KeyEvent.VK_P);
|
---|
148 | update.setPreferredSize(BUTTON_SIZE);
|
---|
149 | Dictionary.registerBoth(update, "MetaEdit.Overwrite", "MetaEdit.Overwrite_Tooltip");
|
---|
150 |
|
---|
151 | remove = new GLIButton();
|
---|
152 | remove.addActionListener(this);
|
---|
153 | remove.setEnabled(false);
|
---|
154 | remove.setMnemonic(KeyEvent.VK_R);
|
---|
155 | remove.setPreferredSize(BUTTON_SIZE);
|
---|
156 | Dictionary.registerBoth(remove, "MetaEdit.Remove", "MetaEdit.Remove_Tooltip");
|
---|
157 |
|
---|
158 | expand = new GLIButton();
|
---|
159 | expand.addActionListener(this);
|
---|
160 | expand.setEnabled(true);
|
---|
161 | expand.setMnemonic(KeyEvent.VK_E);
|
---|
162 | expand.setPreferredSize(new Dimension(25, 25));
|
---|
163 | Dictionary.registerBoth(expand, "MetaEdit.Expand", "MetaEdit.Expand_Tooltip");
|
---|
164 |
|
---|
165 | expand_for_extracted = new GLIButton();
|
---|
166 | expand_for_extracted.addActionListener(this);
|
---|
167 | expand_for_extracted.setEnabled(true);
|
---|
168 | expand_for_extracted.setMnemonic(KeyEvent.VK_E);
|
---|
169 | expand_for_extracted.setPreferredSize(new Dimension(25, 25));
|
---|
170 | Dictionary.registerBoth(expand_for_extracted, "MetaEdit.Expand", "MetaEdit.Expand_Tooltip");
|
---|
171 |
|
---|
172 | metadata_value_table = new MetadataValueTable();
|
---|
173 | metadata_value_tree = new MetadataValueTree(CONTROL_SIZE.width, CONTROL_SIZE.height);
|
---|
174 | }
|
---|
175 |
|
---|
176 |
|
---|
177 | /** Called whenever an action occurs on one of our registered buttons.
|
---|
178 | * @param event An <strong>ActionEvent</strong> containing information about the event.
|
---|
179 | */
|
---|
180 | public void actionPerformed(ActionEvent event)
|
---|
181 | {
|
---|
182 | Object esrc = event.getSource();
|
---|
183 |
|
---|
184 | // Add button pressed
|
---|
185 | if (esrc == add) {
|
---|
186 | MetadataElement metadata_element = metadata_value_table.getSelectedMetadataElement();
|
---|
187 | MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue(metadata_value_tree.getSelectedValue());
|
---|
188 | MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
|
---|
189 | metadata_value.setIsAccumulatingMetadata(true);
|
---|
190 | (new AppendMetadataTask(metadata_value)).start();
|
---|
191 | }
|
---|
192 |
|
---|
193 | // Replace button pressed
|
---|
194 | else if (esrc == update) {
|
---|
195 | MetadataElement metadata_element = metadata_value_table.getSelectedMetadataElement();
|
---|
196 | MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue(metadata_value_tree.getSelectedValue());
|
---|
197 | MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
|
---|
198 | metadata_value.setIsAccumulatingMetadata(!metadata_value_table.getSelectedMetadataValueTableEntry().isInheritedMetadata());
|
---|
199 | (new UpdateMetadataTask(metadata_value)).start();
|
---|
200 | }
|
---|
201 |
|
---|
202 | // Remove button pressed
|
---|
203 | else if (esrc == remove) {
|
---|
204 | (new RemoveMetadataTask()).start();
|
---|
205 | }
|
---|
206 |
|
---|
207 | // Expand button pressed
|
---|
208 | else if (esrc == expand) {
|
---|
209 | EditorDialog ed = new EditorDialog();
|
---|
210 | String new_metadata_value = ed.display(metadata_value_tree.getSelectedValue());
|
---|
211 | if (new_metadata_value != null) {
|
---|
212 | metadata_value_tree.setSelectedValue(new_metadata_value);
|
---|
213 | }
|
---|
214 | }
|
---|
215 |
|
---|
216 | // Expand button for extracted metadata pressed
|
---|
217 | else if (esrc == expand_for_extracted) {
|
---|
218 | EditorDialog ed = new EditorDialog();
|
---|
219 | ed.setEditable(false);
|
---|
220 | ed.display(metadata_value_table.getSelectedMetadataValueTableEntry().getFullValue());
|
---|
221 | }
|
---|
222 | }
|
---|
223 |
|
---|
224 |
|
---|
225 | /** 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.
|
---|
226 | */
|
---|
227 | public void afterDisplay()
|
---|
228 | {
|
---|
229 | external_split.setDividerLocation(0.3);
|
---|
230 | }
|
---|
231 |
|
---|
232 |
|
---|
233 | /** Used to create, connect and layout the components to be shown on this control panel.
|
---|
234 | * @see org.greenstone.gatherer.Gatherer
|
---|
235 | * @see org.greenstone.gatherer.collection.CollectionManager
|
---|
236 | * @see org.greenstone.gatherer.file.FileOpenActionListener
|
---|
237 | * @see org.greenstone.gatherer.gui.Filter
|
---|
238 | */
|
---|
239 | public void display()
|
---|
240 | {
|
---|
241 | // Creation
|
---|
242 | JPanel collection_pane = new JPanel(new BorderLayout());
|
---|
243 | collection_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
244 | collection_pane.setPreferredSize(TREE_SIZE);
|
---|
245 |
|
---|
246 | external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
---|
247 |
|
---|
248 | collection_label = new JLabel();
|
---|
249 | Dictionary.registerText(collection_label, "Collection.No_Collection");
|
---|
250 | collection_label.setOpaque(true);
|
---|
251 |
|
---|
252 | DragGroup group = new DragGroup();
|
---|
253 | TreeModel collection_model = Gatherer.c_man.getRecordSet();
|
---|
254 | if (collection_model != null) {
|
---|
255 | collection_tree = new DragTree("Enrich", collection_model, null, false);
|
---|
256 | }
|
---|
257 | else {
|
---|
258 | collection_tree = new DragTree("Enrich", null, false);
|
---|
259 | }
|
---|
260 | group.add(collection_tree);
|
---|
261 | collection_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
---|
262 | collection_tree.putClientProperty("JTree.lineStyle", "Angled");
|
---|
263 | collection_tree.addMouseListener(Gatherer.g_man.foa_listener);
|
---|
264 | collection_tree.addMouseListener(new RightButtonListener());
|
---|
265 | collection_tree.addTreeSelectionListener(this);
|
---|
266 | collection_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
|
---|
267 | collection_tree.setLargeModel(true);
|
---|
268 | collection_tree.setRootVisible(false);
|
---|
269 |
|
---|
270 | KeyListenerImpl key_listener = new KeyListenerImpl();
|
---|
271 | collection_tree.addKeyListener(key_listener);
|
---|
272 | JScrollPane collection_scroll = new JScrollPane(collection_tree);
|
---|
273 |
|
---|
274 | filter = Gatherer.g_man.getFilter(collection_tree);
|
---|
275 | filter.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
|
---|
276 | filter.setEditable(Configuration.getMode() > Configuration.LIBRARIAN_MODE);
|
---|
277 | Dictionary.registerTooltip(filter.getComboBox(), "Collection.Filter_Tooltip");
|
---|
278 |
|
---|
279 | // Layout
|
---|
280 | collection_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createLoweredBevelBorder()));
|
---|
281 | collection_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
282 | collection_pane.setPreferredSize(new Dimension(Gatherer.g_man.getSize().width / 3, Gatherer.g_man.getSize().height));
|
---|
283 |
|
---|
284 | // Collection Pane
|
---|
285 | collection_pane.add(collection_label, BorderLayout.NORTH);
|
---|
286 | collection_pane.add(collection_scroll, BorderLayout.CENTER);
|
---|
287 | collection_pane.add(filter, BorderLayout.SOUTH);
|
---|
288 |
|
---|
289 | JSplitPane main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
---|
290 | main_split_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
|
---|
291 | main_split_pane.setDividerSize(8);
|
---|
292 |
|
---|
293 | // Metadata Table. In order to achieve what Ian envisions I'm going
|
---|
294 | // to have to do a tricky. The table itself will sit in a stacked
|
---|
295 | // CardLayout. Card zero will be a see-through JPanel with a message
|
---|
296 | // smack bang in the center, something like "No Metadata". If any
|
---|
297 | // page of the table would appear to have no metadata this card will
|
---|
298 | // be swapped to the front.
|
---|
299 |
|
---|
300 | JPanel table_pane = new JPanel();
|
---|
301 | table_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
|
---|
302 |
|
---|
303 | JPanel table_title_pane = new JPanel();
|
---|
304 |
|
---|
305 | table_label = new JTextField();
|
---|
306 | table_label.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
|
---|
307 | table_label.setEditable(false);
|
---|
308 | Dictionary.registerText(table_label, "MetaEdit.No_File");
|
---|
309 |
|
---|
310 | table_card_pane = new JPanel();
|
---|
311 |
|
---|
312 | JPanel table_pane_zero = new JPanel();
|
---|
313 | table_pane_zero.setOpaque(false);
|
---|
314 |
|
---|
315 | JPanel table_pane_two = new JPanel();
|
---|
316 | table_pane_two.setOpaque(false);
|
---|
317 |
|
---|
318 | JLabel no_file_message = new JLabel();
|
---|
319 | no_file_message.setHorizontalAlignment(JLabel.CENTER);
|
---|
320 | no_file_message.setOpaque(false);
|
---|
321 | no_file_message.setVerticalAlignment(JLabel.CENTER);
|
---|
322 | Dictionary.registerText(no_file_message, "MetaEdit.No_File");
|
---|
323 |
|
---|
324 | JLabel no_metadata_message = new JLabel();
|
---|
325 | no_metadata_message.setHorizontalAlignment(JLabel.CENTER);
|
---|
326 | no_metadata_message.setOpaque(false);
|
---|
327 | no_metadata_message.setVerticalAlignment(JLabel.CENTER);
|
---|
328 | Dictionary.registerText(no_metadata_message, "MetaEdit.No_Metadata");
|
---|
329 |
|
---|
330 | JPanel table_pane_one = new JPanel();
|
---|
331 |
|
---|
332 | JScrollPane table_scroll = new JScrollPane(metadata_value_table);
|
---|
333 | table_scroll.getViewport().setBackground(Configuration.getColor("coloring.collection_tree_background", false));
|
---|
334 | table_scroll.setOpaque(true);
|
---|
335 |
|
---|
336 | // Create the cards for the metadata value table
|
---|
337 | metadata_value_table_card_layout = new CardLayout();
|
---|
338 |
|
---|
339 | table_pane_zero.setLayout(new BorderLayout());
|
---|
340 | table_pane_zero.add(no_file_message, BorderLayout.CENTER);
|
---|
341 |
|
---|
342 | table_pane_one.setLayout(new BorderLayout());
|
---|
343 | table_pane_one.add(table_scroll, BorderLayout.CENTER);
|
---|
344 |
|
---|
345 | table_pane_two.setLayout(new BorderLayout());
|
---|
346 | table_pane_two.add(no_metadata_message, BorderLayout.CENTER);
|
---|
347 |
|
---|
348 | table_card_pane.setLayout(metadata_value_table_card_layout);
|
---|
349 | table_card_pane.add(table_pane_zero, TABLE_CARD_NO_FILES_SELECTED);
|
---|
350 | table_card_pane.add(table_pane_one, TABLE_CARD);
|
---|
351 | table_card_pane.add(table_pane_two, TABLE_CARD_NO_METADATA_AVAILABLE);
|
---|
352 |
|
---|
353 | // Create the cards for the metadata value tree
|
---|
354 | metadata_value_tree_card_layout = new CardLayout();
|
---|
355 |
|
---|
356 | // Control pane
|
---|
357 | control_pane = new JPanel();
|
---|
358 | control_pane.setBorder(BorderFactory.createLoweredBevelBorder());
|
---|
359 | control_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
360 | control_pane.setSize(CONTROL_SIZE);
|
---|
361 | control_pane.setPreferredSize(CONTROL_SIZE);
|
---|
362 |
|
---|
363 | JPanel tools_off_pane = new JPanel();
|
---|
364 |
|
---|
365 | JLabel tools_off_label = new JLabel();
|
---|
366 | tools_off_label.setHorizontalAlignment(JLabel.CENTER);
|
---|
367 | tools_off_label.setOpaque(false);
|
---|
368 | tools_off_label.setVerticalAlignment(JLabel.CENTER);
|
---|
369 | Dictionary.registerText(tools_off_label, "MetaEdit.No_Metadata_Element");
|
---|
370 |
|
---|
371 | JPanel tools_on_pane = new JPanel();
|
---|
372 | tools_on_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
373 | tools_on_pane.setSize(TABLE_SIZE);
|
---|
374 | tools_on_pane.setPreferredSize(TABLE_SIZE);
|
---|
375 |
|
---|
376 | // Layout.
|
---|
377 |
|
---|
378 | // Metaedit controls
|
---|
379 | tools_off_pane.setLayout(new BorderLayout());
|
---|
380 | tools_off_pane.add(tools_off_label, BorderLayout.CENTER);
|
---|
381 |
|
---|
382 | tools_on_pane.setLayout(new BorderLayout());
|
---|
383 | tools_on_pane.add(metadata_value_tree, BorderLayout.CENTER);
|
---|
384 |
|
---|
385 | control_pane.setLayout(metadata_value_tree_card_layout);
|
---|
386 | control_pane.add(tools_off_pane, TREE_CARD_NO_METADATA_ELEMENT_SELECTED);
|
---|
387 | control_pane.add(tools_on_pane, TREE_CARD);
|
---|
388 |
|
---|
389 | // Table components
|
---|
390 | table_title_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
|
---|
391 | table_title_pane.setLayout(new BorderLayout());
|
---|
392 | table_title_pane.add(table_label, BorderLayout.CENTER);
|
---|
393 |
|
---|
394 | table_pane.setLayout(new BorderLayout());
|
---|
395 | table_pane.add(table_title_pane, BorderLayout.NORTH);
|
---|
396 | table_pane.add(table_card_pane, BorderLayout.CENTER);
|
---|
397 |
|
---|
398 | main_split_pane.add(table_pane, JSplitPane.TOP);
|
---|
399 | main_split_pane.add(control_pane, JSplitPane.BOTTOM);
|
---|
400 | main_split_pane.setDividerLocation(250);
|
---|
401 |
|
---|
402 | external_split.add(collection_pane, JSplitPane.LEFT);
|
---|
403 | external_split.add(main_split_pane, JSplitPane.RIGHT);
|
---|
404 |
|
---|
405 | this.setLayout(new BorderLayout());
|
---|
406 | this.add(external_split, BorderLayout.CENTER);
|
---|
407 | }
|
---|
408 |
|
---|
409 |
|
---|
410 | /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
|
---|
411 | */
|
---|
412 | public void gainFocus()
|
---|
413 | {
|
---|
414 | // If there is nothing in the collection tree there is nothing to do
|
---|
415 | if (collection_tree == null || collection_tree.getRowCount() == 0) {
|
---|
416 | System.err.println("No/empty collection tree... nothing to do.");
|
---|
417 | return;
|
---|
418 | }
|
---|
419 |
|
---|
420 | // Select the first node in the tree if nothing is already selected
|
---|
421 | if (collection_tree.getSelectionPaths() == null) {
|
---|
422 | collection_tree.setImmediate(true);
|
---|
423 | collection_tree.setSelectionRow(0);
|
---|
424 | collection_tree.setImmediate(false);
|
---|
425 | return;
|
---|
426 | }
|
---|
427 |
|
---|
428 | // Force all of the controls to be updated
|
---|
429 | valueChanged(new TreeSelectionEvent(this, null, false, null, null));
|
---|
430 | }
|
---|
431 |
|
---|
432 |
|
---|
433 | /** 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)
|
---|
434 | * @param mode the mode level as an int
|
---|
435 | */
|
---|
436 | public void modeChanged(int mode)
|
---|
437 | {
|
---|
438 | filter.setEditable(mode > Configuration.LIBRARIAN_MODE);
|
---|
439 | }
|
---|
440 |
|
---|
441 |
|
---|
442 | public void refresh(int refresh_reason, boolean ready)
|
---|
443 | {
|
---|
444 | if (ready) {
|
---|
445 | // Update label
|
---|
446 | Dictionary.registerText(collection_label, "Collection.Collection");
|
---|
447 | collection_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
|
---|
448 | collection_label.setForeground(Configuration.getColor("coloring.collection_heading_foreground", false));
|
---|
449 |
|
---|
450 | // Update tree
|
---|
451 | collection_tree.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
|
---|
452 | collection_tree.setForeground(Configuration.getColor("coloring.collection_tree_foreground", false));
|
---|
453 | if (refresh_reason != CollectionManager.COLLECTION_REBUILT) {
|
---|
454 | collection_tree.setModel(Gatherer.c_man.getRecordSet());
|
---|
455 | }
|
---|
456 | }
|
---|
457 | else {
|
---|
458 | // Update label
|
---|
459 | Dictionary.registerText(collection_label, "Collection.No_Collection");
|
---|
460 | collection_label.setBackground(Color.lightGray);
|
---|
461 | collection_label.setForeground(Color.black);
|
---|
462 |
|
---|
463 | // Update tree
|
---|
464 | collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode(Dictionary.get("Collection.No_Collection"))));
|
---|
465 | }
|
---|
466 |
|
---|
467 | filter.setEnabled(ready);
|
---|
468 | // Ensure that this collection tree view is synchronized with any others.
|
---|
469 | tree_sync.add(collection_tree);
|
---|
470 | }
|
---|
471 |
|
---|
472 |
|
---|
473 | /** This method is called whenever the selection within the collection
|
---|
474 | * tree changes. This causes the table to be rebuilt with a new model.
|
---|
475 | */
|
---|
476 | public void valueChanged(TreeSelectionEvent event)
|
---|
477 | {
|
---|
478 | // Nothing selected in the collection tree
|
---|
479 | if (collection_tree.getSelectionCount() == 0) {
|
---|
480 | file_nodes = null;
|
---|
481 |
|
---|
482 | // Update the label to show nothing is selected in the collection tree
|
---|
483 | Dictionary.registerText(table_label, "MetaEdit.No_File");
|
---|
484 |
|
---|
485 | // Display the "no file selected" card
|
---|
486 | metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD_NO_FILES_SELECTED);
|
---|
487 | }
|
---|
488 |
|
---|
489 | // Some files selected in the collection tree
|
---|
490 | else {
|
---|
491 | TreePath paths[] = collection_tree.getSelectionPaths();
|
---|
492 | file_nodes = new FileNode[paths.length];
|
---|
493 | for (int i = 0; i < paths.length; i++) {
|
---|
494 | file_nodes[i] = (FileNode) paths[i].getLastPathComponent();
|
---|
495 | }
|
---|
496 |
|
---|
497 | // Update the label to show what is selected in the collection tree
|
---|
498 | table_label.setText(collection_tree.getSelectionDetails());
|
---|
499 | }
|
---|
500 |
|
---|
501 | // Update the metadata value table (and consequently, the metadata value tree)
|
---|
502 | MetadataValue previously_selected_metadata_value = metadata_value_table.getSelectedMetadataValueTableEntry();
|
---|
503 | metadata_value_table.rebuildAndSelectRowWithValueClosestTo(previously_selected_metadata_value);
|
---|
504 |
|
---|
505 | // // Update the meta-audit view to show the current selection, if any.
|
---|
506 | // Gatherer.g_man.meta_audit.setRecords(file_nodes);
|
---|
507 | }
|
---|
508 |
|
---|
509 |
|
---|
510 | private class AppendMetadataTask
|
---|
511 | extends Thread
|
---|
512 | {
|
---|
513 | private MetadataValue metadata_value = null;
|
---|
514 |
|
---|
515 | private AppendMetadataTask(MetadataValue metadata_value)
|
---|
516 | {
|
---|
517 | this.metadata_value = metadata_value;
|
---|
518 | }
|
---|
519 |
|
---|
520 | public void run()
|
---|
521 | {
|
---|
522 | if (file_nodes == null || metadata_value == null) {
|
---|
523 | return;
|
---|
524 | }
|
---|
525 |
|
---|
526 | // If we're adding metadata to folders display the warning
|
---|
527 | if (!file_nodes[0].isLeaf()) {
|
---|
528 | WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", true);
|
---|
529 | int dialog_result = dialog.display();
|
---|
530 | dialog.dispose();
|
---|
531 |
|
---|
532 | if (dialog_result != JOptionPane.OK_OPTION) {
|
---|
533 | return;
|
---|
534 | }
|
---|
535 | }
|
---|
536 |
|
---|
537 | // Edit metadata.xml files to add the metadata
|
---|
538 | MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
|
---|
539 |
|
---|
540 | // Update the metadata value table and tree
|
---|
541 | metadata_edit_event = true;
|
---|
542 | metadata_value_table.rebuildAndSelectRowWithValueClosestTo(metadata_value);
|
---|
543 | metadata_edit_event = false;
|
---|
544 | }
|
---|
545 | }
|
---|
546 |
|
---|
547 |
|
---|
548 | private class UpdateMetadataTask
|
---|
549 | extends Thread
|
---|
550 | {
|
---|
551 | private MetadataValue metadata_value = null;
|
---|
552 |
|
---|
553 | private UpdateMetadataTask(MetadataValue metadata_value)
|
---|
554 | {
|
---|
555 | this.metadata_value = metadata_value;
|
---|
556 | }
|
---|
557 |
|
---|
558 | public void run()
|
---|
559 | {
|
---|
560 | MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
|
---|
561 | if (selected_metadata_value_table_entry == null || file_nodes == null || metadata_value == null) {
|
---|
562 | return;
|
---|
563 | }
|
---|
564 |
|
---|
565 | // Edit metadata.xml files to replace the metadata
|
---|
566 | MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
|
---|
567 | MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
|
---|
568 |
|
---|
569 | // Update the metadata value table and tree
|
---|
570 | metadata_edit_event = true;
|
---|
571 | metadata_value_table.rebuildAndSelectRowWithValueClosestTo(metadata_value);
|
---|
572 | metadata_edit_event = false;
|
---|
573 | }
|
---|
574 | }
|
---|
575 |
|
---|
576 |
|
---|
577 | private class RemoveMetadataTask
|
---|
578 | extends Thread
|
---|
579 | {
|
---|
580 | public void run()
|
---|
581 | {
|
---|
582 | MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
|
---|
583 | if (selected_metadata_value_table_entry == null || file_nodes == null) {
|
---|
584 | return;
|
---|
585 | }
|
---|
586 |
|
---|
587 | // Edit metadata.xml files to remove the metadata
|
---|
588 | MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
|
---|
589 |
|
---|
590 | // Update the metadata value table and tree
|
---|
591 | metadata_edit_event = true;
|
---|
592 | metadata_value_table.rebuildAndSelectRowWithValueClosestTo(selected_metadata_value_table_entry);
|
---|
593 | metadata_edit_event = false;
|
---|
594 | }
|
---|
595 | }
|
---|
596 |
|
---|
597 |
|
---|
598 | private class MetadataValueTable
|
---|
599 | extends JTable
|
---|
600 | {
|
---|
601 | private MetadataValueTableModel metadata_value_table_model = null;
|
---|
602 | /** The currently selected metadata value table entry */
|
---|
603 | private MetadataValueTableEntry selected_metadata_value_table_entry = null;
|
---|
604 |
|
---|
605 |
|
---|
606 | public MetadataValueTable()
|
---|
607 | {
|
---|
608 | metadata_value_table_model = new MetadataValueTableModel();
|
---|
609 | setModel(metadata_value_table_model);
|
---|
610 |
|
---|
611 | // We allow only one row to be selected at a time
|
---|
612 | setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
---|
613 | getSelectionModel().addListSelectionListener(new MetadataValueTableListSelectionListener());
|
---|
614 |
|
---|
615 | // The default JTable doesn't quite have the desired properties - when a
|
---|
616 | // table cell is clicked on, the table is scrolled so the cell is as
|
---|
617 | // visible as possible. This means that the left-most cells can become
|
---|
618 | // hidden. To fix this, the MouseInputHandler in BasicTableUI is removed
|
---|
619 | // as a MouseListener on the table, and a very slightly altered copy is
|
---|
620 | // added in its place (TableMouseInputHandler).
|
---|
621 | MouseListener[] mls = getMouseListeners();
|
---|
622 | for (int i = 0; i < mls.length; i++) {
|
---|
623 | // Remove the instances of MouseInputHandler
|
---|
624 | if (mls[i].toString().startsWith("javax.swing.plaf.basic.BasicTableUI$MouseInputHandler@")) {
|
---|
625 | removeMouseListener(mls[i]);
|
---|
626 | }
|
---|
627 | }
|
---|
628 | // Add the slightly-altered mouse listener
|
---|
629 | addMouseListener(new TableMouseInputHandler());
|
---|
630 |
|
---|
631 | // We also have to ensure that the table column header hasn't gone on a severe Jenny Craig binge and has somehow lost 7/8th of its component size
|
---|
632 | JTableHeader table_header = getTableHeader();
|
---|
633 | Dimension table_header_preferred_size = table_header.getPreferredSize();
|
---|
634 | if (table_header_preferred_size.height < MINIMUM_TABLE_HEADER_SIZE) {
|
---|
635 | table_header_preferred_size.setSize(table_header_preferred_size.width, MINIMUM_TABLE_HEADER_SIZE);
|
---|
636 | table_header.setPreferredSize(table_header_preferred_size);
|
---|
637 | }
|
---|
638 |
|
---|
639 | // Set the table columns as we want them
|
---|
640 | setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
---|
641 | Dimension table_size = getPreferredSize();
|
---|
642 | TableColumnModel column_model = getColumnModel();
|
---|
643 |
|
---|
644 | TableColumn inherited_column = column_model.getColumn(0);
|
---|
645 | inherited_column.setPreferredWidth(20);
|
---|
646 | inherited_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
|
---|
647 |
|
---|
648 | TableColumn element_column = column_model.getColumn(1);
|
---|
649 | element_column.setPreferredWidth((TABLE_SIZE.width / 4) - 15);
|
---|
650 | element_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
|
---|
651 |
|
---|
652 | TableColumn value_column = column_model.getColumn(2);
|
---|
653 | value_column.setPreferredWidth(((3 * TABLE_SIZE.width) / 4) - 30);
|
---|
654 | value_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
|
---|
655 | }
|
---|
656 |
|
---|
657 |
|
---|
658 | public MetadataElement getSelectedMetadataElement()
|
---|
659 | {
|
---|
660 | return selected_metadata_value_table_entry.getMetadataElement();
|
---|
661 | }
|
---|
662 |
|
---|
663 |
|
---|
664 | public MetadataValueTableEntry getSelectedMetadataValueTableEntry()
|
---|
665 | {
|
---|
666 | return selected_metadata_value_table_entry;
|
---|
667 | }
|
---|
668 |
|
---|
669 |
|
---|
670 | public boolean isSelectedMetadataValueTableEntryCommon()
|
---|
671 | {
|
---|
672 | return metadata_value_table_model.isCommon(selected_metadata_value_table_entry);
|
---|
673 | }
|
---|
674 |
|
---|
675 |
|
---|
676 | public void moveSelectionDown()
|
---|
677 | {
|
---|
678 | int new_row_to_select = getSelectedRow() + 1;
|
---|
679 | if (new_row_to_select < metadata_value_table_model.getRowCount()) {
|
---|
680 | changeSelection(new_row_to_select, 1, false, false);
|
---|
681 | }
|
---|
682 | }
|
---|
683 |
|
---|
684 |
|
---|
685 | public void moveSelectionUp()
|
---|
686 | {
|
---|
687 | int new_row_to_select = getSelectedRow() - 1;
|
---|
688 | if (new_row_to_select >= 0) {
|
---|
689 | changeSelection(new_row_to_select, 1, false, false);
|
---|
690 | }
|
---|
691 | }
|
---|
692 |
|
---|
693 |
|
---|
694 | public void rebuildAndSelectRowWithValueClosestTo(MetadataValue optimal_metadata_value_to_select_when_building_complete)
|
---|
695 | {
|
---|
696 | // We don't want a lot of ListSelectionEvents while the table is rebuilding
|
---|
697 | clearSelection();
|
---|
698 |
|
---|
699 | // Rebuild the metadata value table model
|
---|
700 | metadata_value_table_model.rebuild(file_nodes);
|
---|
701 |
|
---|
702 | // If the metadata value table is empty there isn't much to do
|
---|
703 | if (metadata_value_table_model.getRowCount() == 0) {
|
---|
704 | // ...except display the "no metadata available" card, unless no files are selected
|
---|
705 | if (file_nodes != null) {
|
---|
706 | metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD_NO_METADATA_AVAILABLE);
|
---|
707 | }
|
---|
708 | return;
|
---|
709 | }
|
---|
710 |
|
---|
711 | // Display the card with the metadata value table
|
---|
712 | metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD);
|
---|
713 |
|
---|
714 | // If we don't need to select a row in the table after rebuilding, we're done
|
---|
715 | if (optimal_metadata_value_to_select_when_building_complete == null) {
|
---|
716 | return;
|
---|
717 | }
|
---|
718 |
|
---|
719 | MetadataElement optimal_metadata_element = optimal_metadata_value_to_select_when_building_complete.getMetadataElement();
|
---|
720 |
|
---|
721 | // Find the row containing the closest value to the optimal value
|
---|
722 | int optimal_row_to_select = 0;
|
---|
723 | for (int i = 0; i < metadata_value_table_model.getRowCount(); i++) {
|
---|
724 | MetadataValueTableEntry metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(i);
|
---|
725 | MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
|
---|
726 | if (metadata_element.equals(optimal_metadata_element)) {
|
---|
727 | // This row will be the optimal row, unless the next row matches better
|
---|
728 | optimal_row_to_select = i;
|
---|
729 | }
|
---|
730 |
|
---|
731 | int c = metadata_value_table_entry.compareTo(optimal_metadata_value_to_select_when_building_complete);
|
---|
732 | if (c >= 0) {
|
---|
733 | // We've either found an exact match, or there is no exact match, so stop here
|
---|
734 | break;
|
---|
735 | }
|
---|
736 | }
|
---|
737 |
|
---|
738 | changeSelection(optimal_row_to_select, 1, false, false);
|
---|
739 | }
|
---|
740 |
|
---|
741 |
|
---|
742 | private class MetadataValueTableListSelectionListener
|
---|
743 | implements ListSelectionListener
|
---|
744 | {
|
---|
745 | public void valueChanged(ListSelectionEvent event)
|
---|
746 | {
|
---|
747 | // We only want to handle one event per selection, so wait for the value to stabilise
|
---|
748 | if (getSelectionModel().getValueIsAdjusting() == true) {
|
---|
749 | return;
|
---|
750 | }
|
---|
751 |
|
---|
752 | // System.err.println("In MetadataValueTableListSelectionListener.valueChanged()...");
|
---|
753 |
|
---|
754 | selected_metadata_value_table_entry = null;
|
---|
755 | MetadataElement selected_metadata_element = null;
|
---|
756 | String selected_metadata_value = "";
|
---|
757 |
|
---|
758 | // We have a SINGLE_SELECTION model set so there is at most one selection
|
---|
759 | int selected_row = getSelectedRow();
|
---|
760 | if (selected_row >= 0) {
|
---|
761 | selected_metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(selected_row);
|
---|
762 | selected_metadata_element = selected_metadata_value_table_entry.getMetadataElement();
|
---|
763 | MetadataValueTreeNode metadata_value_tree_node = selected_metadata_value_table_entry.getMetadataValueTreeNode();
|
---|
764 | if (metadata_value_tree_node != null) {
|
---|
765 | selected_metadata_value = metadata_value_tree_node.getFullValue();
|
---|
766 | }
|
---|
767 | }
|
---|
768 |
|
---|
769 | // Update the metadata value tree
|
---|
770 | metadata_value_tree.rebuild(selected_metadata_element, selected_metadata_value);
|
---|
771 | }
|
---|
772 | }
|
---|
773 |
|
---|
774 |
|
---|
775 | /** A direct copy of javax.swing.plaf.basic.BasicTableUI$MouseInputHandler, except for one change in adjustFocusAndSelection(). The purpose of this change is to always keep the left-most cells of the table row selected visible. */
|
---|
776 | private class TableMouseInputHandler implements MouseInputListener
|
---|
777 | {
|
---|
778 | // Component receiving mouse events during editing.
|
---|
779 | // May not be editorComponent.
|
---|
780 | private Component dispatchComponent;
|
---|
781 | private boolean selectedOnPress;
|
---|
782 |
|
---|
783 | // The Table's mouse listener methods.
|
---|
784 |
|
---|
785 | public void mouseClicked(MouseEvent e) {}
|
---|
786 |
|
---|
787 | private void setDispatchComponent(MouseEvent e) {
|
---|
788 | Component editorComponent = getEditorComponent();
|
---|
789 | Point p = e.getPoint();
|
---|
790 | Point p2 = SwingUtilities.convertPoint(metadata_value_table, p, editorComponent);
|
---|
791 | dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
|
---|
792 | p2.x, p2.y);
|
---|
793 | }
|
---|
794 |
|
---|
795 | private boolean repostEvent(MouseEvent e) {
|
---|
796 | // Check for isEditing() in case another event has
|
---|
797 | // caused the editor to be removed. See bug #4306499.
|
---|
798 | if (dispatchComponent == null || !isEditing()) {
|
---|
799 | return false;
|
---|
800 | }
|
---|
801 | MouseEvent e2 = SwingUtilities.convertMouseEvent(metadata_value_table, e, dispatchComponent);
|
---|
802 | dispatchComponent.dispatchEvent(e2);
|
---|
803 | return true;
|
---|
804 | }
|
---|
805 |
|
---|
806 | private void setValueIsAdjusting(boolean flag) {
|
---|
807 | getSelectionModel().setValueIsAdjusting(flag);
|
---|
808 | getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
|
---|
809 | }
|
---|
810 |
|
---|
811 | private boolean shouldIgnore(MouseEvent e) {
|
---|
812 | return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && isEnabled()));
|
---|
813 | }
|
---|
814 |
|
---|
815 | public void mousePressed(MouseEvent e) {
|
---|
816 | if (e.isConsumed()) {
|
---|
817 | selectedOnPress = false;
|
---|
818 | return;
|
---|
819 | }
|
---|
820 | selectedOnPress = true;
|
---|
821 | adjustFocusAndSelection(e);
|
---|
822 | }
|
---|
823 |
|
---|
824 | void adjustFocusAndSelection(MouseEvent e) {
|
---|
825 | if (shouldIgnore(e)) {
|
---|
826 | return;
|
---|
827 | }
|
---|
828 |
|
---|
829 | Point p = e.getPoint();
|
---|
830 | int row = rowAtPoint(p);
|
---|
831 | int column = columnAtPoint(p);
|
---|
832 | // The autoscroller can generate drag events outside the Table's range.
|
---|
833 | if ((column == -1) || (row == -1)) {
|
---|
834 | return;
|
---|
835 | }
|
---|
836 |
|
---|
837 | if (editCellAt(row, column, e)) {
|
---|
838 | setDispatchComponent(e);
|
---|
839 | repostEvent(e);
|
---|
840 | }
|
---|
841 | else if (isRequestFocusEnabled()) {
|
---|
842 | requestFocus();
|
---|
843 | }
|
---|
844 |
|
---|
845 | CellEditor editor = getCellEditor();
|
---|
846 | if (editor == null || editor.shouldSelectCell(e)) {
|
---|
847 | boolean adjusting = (e.getID() == MouseEvent.MOUSE_PRESSED) ? true : false;
|
---|
848 | setValueIsAdjusting(adjusting);
|
---|
849 |
|
---|
850 | // Special code for clicking the first column (folder-level metadata)
|
---|
851 | if (column == 0) {
|
---|
852 | selected_metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(row);
|
---|
853 |
|
---|
854 | // If this metadata is inherited, switch to the folder it came from
|
---|
855 | if (selected_metadata_value_table_entry.isInheritedMetadata()) {
|
---|
856 | File folder_metadata_inherited_from = selected_metadata_value_table_entry.getFolderMetadataInheritedFrom();
|
---|
857 | if (folder_metadata_inherited_from != null) {
|
---|
858 | collection_tree.setSelection(folder_metadata_inherited_from);
|
---|
859 | }
|
---|
860 | }
|
---|
861 |
|
---|
862 | changeSelection(row, 0, e.isControlDown(), e.isShiftDown());
|
---|
863 | }
|
---|
864 | else {
|
---|
865 | changeSelection(row, 1, e.isControlDown(), e.isShiftDown());
|
---|
866 | }
|
---|
867 | }
|
---|
868 | }
|
---|
869 |
|
---|
870 | public void mouseReleased(MouseEvent e) {
|
---|
871 | if (selectedOnPress) {
|
---|
872 | if (shouldIgnore(e)) {
|
---|
873 | return;
|
---|
874 | }
|
---|
875 |
|
---|
876 | repostEvent(e);
|
---|
877 | dispatchComponent = null;
|
---|
878 | setValueIsAdjusting(false);
|
---|
879 | } else {
|
---|
880 | adjustFocusAndSelection(e);
|
---|
881 | }
|
---|
882 | }
|
---|
883 |
|
---|
884 |
|
---|
885 | public void mouseEntered(MouseEvent e) {}
|
---|
886 |
|
---|
887 | public void mouseExited(MouseEvent e) {}
|
---|
888 |
|
---|
889 | // The Table's mouse motion listener methods.
|
---|
890 |
|
---|
891 | public void mouseMoved(MouseEvent e) {}
|
---|
892 |
|
---|
893 | public void mouseDragged(MouseEvent e) {
|
---|
894 | if (shouldIgnore(e)) {
|
---|
895 | return;
|
---|
896 | }
|
---|
897 |
|
---|
898 | repostEvent(e);
|
---|
899 |
|
---|
900 | CellEditor editor = getCellEditor();
|
---|
901 | if (editor == null || editor.shouldSelectCell(e)) {
|
---|
902 | Point p = e.getPoint();
|
---|
903 | int row = rowAtPoint(p);
|
---|
904 | int column = columnAtPoint(p);
|
---|
905 | // The autoscroller can generate drag events outside the Table's range.
|
---|
906 | if ((column == -1) || (row == -1)) {
|
---|
907 | return;
|
---|
908 | }
|
---|
909 | changeSelection(row, column, false, true);
|
---|
910 | }
|
---|
911 | }
|
---|
912 | }
|
---|
913 | }
|
---|
914 |
|
---|
915 |
|
---|
916 | /**
|
---|
917 | * This class is a little bit complex, a little bit subtle, and a little bit odd.
|
---|
918 | * The strange interaction model is due to the fact that it is very tightly
|
---|
919 | * coupled to the EnrichPane.
|
---|
920 | *
|
---|
921 | * The interaction is complex because there are three separate controls that the
|
---|
922 | * user may interact with, each of which can affect the other two. The three
|
---|
923 | * controls are:
|
---|
924 | * - The "assigned metadata" table, at the top right of the meta edit pane.
|
---|
925 | * - The "metadata value" text field, where users can type in new values.
|
---|
926 | * - The "metadata value tree", which shows other values that have been
|
---|
927 | * assigned to the selected metadata element.
|
---|
928 | *
|
---|
929 | * The interactions are:
|
---|
930 | * - The "assigned metadata" table
|
---|
931 | * Users may select one (and only one) row in this table. Selecting a row
|
---|
932 | * chooses one metadata element. The text field will be set to the current
|
---|
933 | * value of the metadata element. This value will also be selected in the
|
---|
934 | * metadata value tree.
|
---|
935 | *
|
---|
936 | * - The "metadata value" text field
|
---|
937 | * If the value the user has typed in this is a prefix of an entry in the
|
---|
938 | * value tree, this value will be selected in the value tree. In this case,
|
---|
939 | * pressing "Tab" will complete the value (ie. make the value in the text
|
---|
940 | * field the same as the value in the tree). This is to allow users to
|
---|
941 | * quickly and easily assign existing metadata values to new documents.
|
---|
942 | *
|
---|
943 | * - The "metadata value tree"
|
---|
944 | * Selecting a value in the tree will set the text field to this value.
|
---|
945 | */
|
---|
946 | private class MetadataValueTree
|
---|
947 | extends JPanel
|
---|
948 | implements TreeSelectionListener
|
---|
949 | {
|
---|
950 | private boolean ignore_tree_selection_event = false;
|
---|
951 | private boolean manual_text_edit_event = false;
|
---|
952 | private CardLayout card_layout;
|
---|
953 | private String card_showing = null;
|
---|
954 | /** The metadata element that is currently selected. */
|
---|
955 | private MetadataElement selected_metadata_element = null;
|
---|
956 | private JTextArea extracted_message;
|
---|
957 | private JTextField value;
|
---|
958 | private JTree tree;
|
---|
959 |
|
---|
960 | static final private String NONE = "None";
|
---|
961 | static final private String TREE = "Tree";
|
---|
962 |
|
---|
963 |
|
---|
964 | public MetadataValueTree(int width, int height)
|
---|
965 | {
|
---|
966 | super();
|
---|
967 |
|
---|
968 | setFont(Configuration.getFont("general.font", false));
|
---|
969 | setPreferredSize(new Dimension(width, height));
|
---|
970 |
|
---|
971 | JPanel metadata_pane = new JPanel();
|
---|
972 |
|
---|
973 | JPanel value_pane = new JPanel();
|
---|
974 | JLabel value_label = new JLabel();
|
---|
975 | Dictionary.registerText(value_label, "MetaEdit.Value");
|
---|
976 |
|
---|
977 | JPanel value_field_pane = new JPanel();
|
---|
978 | value = new JTextField();
|
---|
979 | value.setBackground(Configuration.getColor("coloring.editable_background", false));
|
---|
980 | value.setForeground(Configuration.getColor("coloring.editable_foreground", false));
|
---|
981 | value.setPreferredSize(new Dimension(413, 24));
|
---|
982 | value.getDocument().addDocumentListener(new DocumentListenerImpl());
|
---|
983 | value.addKeyListener(new MetadataFieldListener());
|
---|
984 | value.setFocusTraversalKeysEnabled(false);
|
---|
985 | Dictionary.setTooltip(value, "MetaEdit.Value_Field_Tooltip");
|
---|
986 |
|
---|
987 | JPanel button_pane = new JPanel();
|
---|
988 |
|
---|
989 | JPanel tree_pane = new JPanel();
|
---|
990 | JLabel tree_label = new JLabel();
|
---|
991 | Dictionary.registerText(tree_label, "MetaEdit.Tree");
|
---|
992 |
|
---|
993 | tree = new JTree();
|
---|
994 | tree.addTreeSelectionListener(this);
|
---|
995 | tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
---|
996 | tree.setRootVisible(false);
|
---|
997 | tree.putClientProperty("JTree.lineStyle", "Angled");
|
---|
998 |
|
---|
999 | JPanel extracted_pane = new JPanel();
|
---|
1000 | JPanel extracted_header_pane = new JPanel();
|
---|
1001 | extracted_message = new JTextArea("");
|
---|
1002 | extracted_message.setEditable(false);
|
---|
1003 | extracted_message.setLineWrap(true);
|
---|
1004 | extracted_message.setOpaque(false);
|
---|
1005 | extracted_message.setWrapStyleWord(true);
|
---|
1006 |
|
---|
1007 | card_layout = new CardLayout();
|
---|
1008 |
|
---|
1009 | // Layout
|
---|
1010 | value_field_pane.setBorder(BorderFactory.createEmptyBorder(0,5,0,5));
|
---|
1011 | value_field_pane.setLayout(new BorderLayout(0, 0));
|
---|
1012 | value_field_pane.add(value, BorderLayout.CENTER);
|
---|
1013 |
|
---|
1014 | button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
|
---|
1015 | button_pane.setLayout(new GridLayout());
|
---|
1016 | button_pane.add(add);
|
---|
1017 | button_pane.add(update);
|
---|
1018 | button_pane.add(remove);
|
---|
1019 |
|
---|
1020 | value_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
|
---|
1021 | value_pane.setLayout(new BorderLayout());
|
---|
1022 | value_pane.add(value_label, BorderLayout.WEST);
|
---|
1023 | value_pane.add(value_field_pane, BorderLayout.CENTER);
|
---|
1024 | value_pane.add(expand, BorderLayout.EAST);
|
---|
1025 | value_pane.add(button_pane, BorderLayout.SOUTH);
|
---|
1026 |
|
---|
1027 | tree_pane.setLayout(new BorderLayout());
|
---|
1028 | tree_pane.add(tree_label, BorderLayout.NORTH);
|
---|
1029 | tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
|
---|
1030 |
|
---|
1031 | metadata_pane.setLayout(new BorderLayout());
|
---|
1032 | metadata_pane.add(value_pane, BorderLayout.NORTH);
|
---|
1033 | metadata_pane.add(tree_pane, BorderLayout.CENTER);
|
---|
1034 |
|
---|
1035 | extracted_header_pane.setLayout(new BorderLayout());
|
---|
1036 | extracted_header_pane.add(expand_for_extracted, BorderLayout.EAST);
|
---|
1037 |
|
---|
1038 | extracted_pane.setBorder(BorderFactory.createEmptyBorder(0,10,25,0));
|
---|
1039 | extracted_pane.setLayout(new BorderLayout());
|
---|
1040 | extracted_pane.add(extracted_header_pane, BorderLayout.NORTH);
|
---|
1041 | extracted_pane.add(extracted_message, BorderLayout.CENTER);
|
---|
1042 |
|
---|
1043 | this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
|
---|
1044 | this.setLayout(card_layout);
|
---|
1045 | this.add(metadata_pane, TREE);
|
---|
1046 | this.add(extracted_pane, NONE);
|
---|
1047 | card_showing = TREE;
|
---|
1048 | }
|
---|
1049 |
|
---|
1050 |
|
---|
1051 | /** Returns a TreePath for the node most closely matching the metadata value. */
|
---|
1052 | private TreePath getClosestPath(String val)
|
---|
1053 | {
|
---|
1054 | // Start at the root of the tree
|
---|
1055 | MetadataValueTreeNode tree_node = (MetadataValueTreeNode) tree.getModel().getRoot();
|
---|
1056 |
|
---|
1057 | // Separate hierarchical values
|
---|
1058 | PatternTokenizer tokenizer = new PatternTokenizer(val, MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
|
---|
1059 | while (tokenizer.hasMoreTokens()) {
|
---|
1060 | String token = tokenizer.nextToken();
|
---|
1061 |
|
---|
1062 | // All components except the last must match exactly
|
---|
1063 | if (tokenizer.hasMoreTokens()) {
|
---|
1064 | for (int i = 0; i < tree_node.getChildCount(); i++) {
|
---|
1065 | MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
|
---|
1066 | if (child_node.getValue().equals(token)) {
|
---|
1067 | // The matching node has been found, so move onto the next token
|
---|
1068 | tree_node = child_node;
|
---|
1069 | break;
|
---|
1070 | }
|
---|
1071 | }
|
---|
1072 | }
|
---|
1073 |
|
---|
1074 | // The last component may match partially
|
---|
1075 | else {
|
---|
1076 | for (int i = 0; i < tree_node.getChildCount(); i++) {
|
---|
1077 | MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
|
---|
1078 | if (child_node.getValue().startsWith(token)) {
|
---|
1079 | // The closest node has been found, so return its path
|
---|
1080 | return new TreePath(child_node.getPath());
|
---|
1081 | }
|
---|
1082 | }
|
---|
1083 |
|
---|
1084 | // Not even a partial match
|
---|
1085 | return null;
|
---|
1086 | }
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | // If nothing else, return the path of the root node
|
---|
1090 | return new TreePath(tree_node.getPath());
|
---|
1091 | }
|
---|
1092 |
|
---|
1093 |
|
---|
1094 | public String getSelectedValue()
|
---|
1095 | {
|
---|
1096 | return value.getText().replaceAll("\\\\", MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
|
---|
1097 | }
|
---|
1098 |
|
---|
1099 |
|
---|
1100 | public void rebuild(MetadataElement metadata_element, String metadata_value)
|
---|
1101 | {
|
---|
1102 | // System.err.println("In MetadataValueTree.rebuild()...");
|
---|
1103 |
|
---|
1104 | // Clear selection
|
---|
1105 | if (metadata_element == null) {
|
---|
1106 | if (metadata_edit_event == false) {
|
---|
1107 | // System.err.println("Clearing selection...");
|
---|
1108 | selected_metadata_element = null;
|
---|
1109 | validateDisplay();
|
---|
1110 | }
|
---|
1111 | return;
|
---|
1112 | }
|
---|
1113 |
|
---|
1114 | // Reload the value tree model if the selected metadata element has changed
|
---|
1115 | if (selected_metadata_element != metadata_element) {
|
---|
1116 | selected_metadata_element = metadata_element;
|
---|
1117 | tree.setModel(selected_metadata_element.getMetadataValueTreeModel());
|
---|
1118 | }
|
---|
1119 |
|
---|
1120 | validateDisplay();
|
---|
1121 |
|
---|
1122 | setSelectedValue(metadata_value);
|
---|
1123 | }
|
---|
1124 |
|
---|
1125 |
|
---|
1126 | /** This function is copied from JTree::setPathToVisible(), and modified so tree rows
|
---|
1127 | are always shown flushed left. Note: doesn't do accessibleContext stuff. */
|
---|
1128 | private void scrollTreePathToVisible(TreePath path)
|
---|
1129 | {
|
---|
1130 | if (path != null) {
|
---|
1131 | tree.makeVisible(path);
|
---|
1132 |
|
---|
1133 | Rectangle bounds = tree.getPathBounds(path);
|
---|
1134 | if (bounds != null) {
|
---|
1135 | bounds.width += bounds.x;
|
---|
1136 | bounds.x = 0;
|
---|
1137 | tree.scrollRectToVisible(bounds);
|
---|
1138 | }
|
---|
1139 | }
|
---|
1140 | }
|
---|
1141 |
|
---|
1142 |
|
---|
1143 | /** Sets the value in the text field. */
|
---|
1144 | private void setSelectedValue(String metadata_value)
|
---|
1145 | {
|
---|
1146 | // Setting the text of the field causes the DocumentListener to be notified, and
|
---|
1147 | // updating the tree is handled there (DocumentListenerImpl::validate())
|
---|
1148 | if (!card_showing.equals(NONE)) {
|
---|
1149 | manual_text_edit_event = metadata_value.equals(""); // Set to false unless val == ""
|
---|
1150 | value.setText(metadata_value);
|
---|
1151 | manual_text_edit_event = true;
|
---|
1152 | }
|
---|
1153 | }
|
---|
1154 |
|
---|
1155 |
|
---|
1156 | private void validateDisplay()
|
---|
1157 | {
|
---|
1158 | // If no metadata is selected in the metadata value table, display "no metadata element selected" card
|
---|
1159 | if (file_nodes == null || selected_metadata_element == null) {
|
---|
1160 | metadata_value_tree_card_layout.show(control_pane, TREE_CARD_NO_METADATA_ELEMENT_SELECTED);
|
---|
1161 | }
|
---|
1162 |
|
---|
1163 | // Otherwise, display the card with the metadata value tree
|
---|
1164 | else {
|
---|
1165 | metadata_value_tree_card_layout.show(control_pane, TREE_CARD);
|
---|
1166 |
|
---|
1167 | // Special case the extracted metadata set
|
---|
1168 | if (selected_metadata_element.getNamespace().equals(Utility.EXTRACTED_METADATA_NAMESPACE)) {
|
---|
1169 | // Display the panel showing the "you cannot edit this metadata" message
|
---|
1170 | String[] args = new String[1];
|
---|
1171 | args[0] = selected_metadata_element.getFullName();
|
---|
1172 | Dictionary.registerText(extracted_message, "MetaEdit.AutoMessage", args);
|
---|
1173 | card_layout.show(this, NONE);
|
---|
1174 | card_showing = NONE;
|
---|
1175 | }
|
---|
1176 | else {
|
---|
1177 | // Display the panel for viewing and assigning metadata
|
---|
1178 | card_layout.show(this, TREE);
|
---|
1179 | card_showing = TREE;
|
---|
1180 | }
|
---|
1181 |
|
---|
1182 | // Validate the buttons above the value tree
|
---|
1183 | MetadataValueTableEntry metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
|
---|
1184 | if (metadata_value_table_entry.getValue().equals("")) {
|
---|
1185 | // Can only append if something has been entered
|
---|
1186 | add.setEnabled((getSelectedValue().length() > 0));
|
---|
1187 |
|
---|
1188 | // Nothing to replace or remove
|
---|
1189 | update.setEnabled(false);
|
---|
1190 | remove.setEnabled(false);
|
---|
1191 | return;
|
---|
1192 | }
|
---|
1193 |
|
---|
1194 | // Check if the text in the value field is the same as the metadata value
|
---|
1195 | if (getSelectedValue().equals(metadata_value_table_entry.getFullValue())) {
|
---|
1196 | // Can't replace
|
---|
1197 | update.setEnabled(false);
|
---|
1198 |
|
---|
1199 | // Adding, however, is dependant on whether the selected metadata is common or uncommon. If the later then you can append so as to make it common.
|
---|
1200 | add.setEnabled(!metadata_value_table.isSelectedMetadataValueTableEntryCommon());
|
---|
1201 | }
|
---|
1202 | else {
|
---|
1203 | // Can append or replace, if something has been entered
|
---|
1204 | add.setEnabled((getSelectedValue().length() > 0));
|
---|
1205 | update.setEnabled((getSelectedValue().length() > 0));
|
---|
1206 | }
|
---|
1207 |
|
---|
1208 | // Can only remove if the metadata is file level
|
---|
1209 | if (metadata_value_table_entry != null) { // Shouldn't be necessary, but is
|
---|
1210 | remove.setEnabled((metadata_value_table_entry.isInheritedMetadata() == false));
|
---|
1211 | }
|
---|
1212 | }
|
---|
1213 | }
|
---|
1214 |
|
---|
1215 |
|
---|
1216 | /** Handles TreeSelectionEvents. */
|
---|
1217 | public void valueChanged(TreeSelectionEvent event)
|
---|
1218 | {
|
---|
1219 | if (tree.getSelectionCount() != 0 && !ignore_tree_selection_event) {
|
---|
1220 | TreePath path = tree.getSelectionPath();
|
---|
1221 | MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
|
---|
1222 | setSelectedValue(node.getFullValue());
|
---|
1223 | }
|
---|
1224 | }
|
---|
1225 |
|
---|
1226 |
|
---|
1227 | private class MetadataFieldListener
|
---|
1228 | implements KeyListener {
|
---|
1229 |
|
---|
1230 | /** Gives notification of key events on the text field */
|
---|
1231 | public void keyPressed(KeyEvent e)
|
---|
1232 | {
|
---|
1233 | // Tab: Auto-complete
|
---|
1234 | if (e.getKeyCode() == KeyEvent.VK_TAB) {
|
---|
1235 | if (tree.getSelectionCount() != 0 && !getSelectedValue().equals("")) {
|
---|
1236 | TreePath path = tree.getSelectionPath();
|
---|
1237 | MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
|
---|
1238 | setSelectedValue(node.getFullValue());
|
---|
1239 | }
|
---|
1240 | }
|
---|
1241 |
|
---|
1242 | // Enter: Append the metadata value, if we're allowed to
|
---|
1243 | if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
---|
1244 | if (add.isEnabled()) {
|
---|
1245 | add.doClick();
|
---|
1246 | }
|
---|
1247 | }
|
---|
1248 |
|
---|
1249 | // Down: Change the metadata value table selection
|
---|
1250 | if (e.getKeyCode() == KeyEvent.VK_DOWN) {
|
---|
1251 | metadata_value_table.moveSelectionDown();
|
---|
1252 | }
|
---|
1253 |
|
---|
1254 | // Up: Change the metadata value table selection
|
---|
1255 | if (e.getKeyCode() == KeyEvent.VK_UP) {
|
---|
1256 | metadata_value_table.moveSelectionUp();
|
---|
1257 | }
|
---|
1258 | }
|
---|
1259 |
|
---|
1260 | public void keyReleased(KeyEvent e) { }
|
---|
1261 |
|
---|
1262 | public void keyTyped(KeyEvent e) { }
|
---|
1263 | }
|
---|
1264 |
|
---|
1265 |
|
---|
1266 | private class DocumentListenerImpl
|
---|
1267 | implements DocumentListener
|
---|
1268 | {
|
---|
1269 | /** Gives notification that an attribute or set of attributes changed */
|
---|
1270 | public void changedUpdate(DocumentEvent e) {
|
---|
1271 | validate();
|
---|
1272 | }
|
---|
1273 |
|
---|
1274 | /** Gives notification that there was an insert into the document */
|
---|
1275 | public void insertUpdate(DocumentEvent e) {
|
---|
1276 | validate();
|
---|
1277 | }
|
---|
1278 |
|
---|
1279 | /** Gives notification that a portion of the document has been removed */
|
---|
1280 | public void removeUpdate(DocumentEvent e) {
|
---|
1281 | validate();
|
---|
1282 | }
|
---|
1283 |
|
---|
1284 |
|
---|
1285 | /** Ensures that the value text field and value tree are synchronized */
|
---|
1286 | private void validate()
|
---|
1287 | {
|
---|
1288 | String value_text = getSelectedValue();
|
---|
1289 |
|
---|
1290 | // Ignore the validate() with empty text that occurs when value.setText() is used
|
---|
1291 | if (!value_text.equals("") || manual_text_edit_event == true) {
|
---|
1292 | TreePath closest_path = getClosestPath(value_text);
|
---|
1293 |
|
---|
1294 | // Select the new path in the tree
|
---|
1295 | // The tree selection event this causes must be ignored, since it alters value
|
---|
1296 | ignore_tree_selection_event = true;
|
---|
1297 | tree.setSelectionPath(closest_path);
|
---|
1298 | ignore_tree_selection_event = false;
|
---|
1299 |
|
---|
1300 | // If a folder has been specified, make sure it is expanded
|
---|
1301 | if (value_text.endsWith(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN) && !tree.isExpanded(closest_path)) {
|
---|
1302 | tree.expandPath(closest_path);
|
---|
1303 | }
|
---|
1304 |
|
---|
1305 | // Make sure the path is visible on the screen
|
---|
1306 | scrollTreePathToVisible(closest_path);
|
---|
1307 | // One call should be enough, but it isn't in some cases (for some reason)
|
---|
1308 | scrollTreePathToVisible(closest_path);
|
---|
1309 |
|
---|
1310 | // Update the status of the buttons
|
---|
1311 | validateDisplay();
|
---|
1312 | }
|
---|
1313 | }
|
---|
1314 | }
|
---|
1315 | }
|
---|
1316 |
|
---|
1317 |
|
---|
1318 | /** This class listens for certain key presses, such as [Up] or [Down], -copied from Gather Pane */
|
---|
1319 | private class KeyListenerImpl
|
---|
1320 | extends KeyAdapter {
|
---|
1321 | private boolean vk_left_pressed = false;
|
---|
1322 | /** 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). */
|
---|
1323 | public void keyReleased(KeyEvent event) {
|
---|
1324 | ///ystem.err.println("Key Release detected. " + event.getKeyCode());
|
---|
1325 | if (event.getKeyCode() == KeyEvent.VK_UP || event.getKeyCode() == KeyEvent.VK_DOWN) {
|
---|
1326 | DragTree tree = (DragTree)event.getSource();
|
---|
1327 | // we need to manually do the up and down selections
|
---|
1328 | boolean up = (event.getKeyCode() == KeyEvent.VK_UP);
|
---|
1329 | int current_row = tree.getLeadSelectionRow();
|
---|
1330 | tree.setImmediate(true);
|
---|
1331 | if (up) {
|
---|
1332 | if (current_row > 0) {
|
---|
1333 | tree.clearSelection();
|
---|
1334 | tree.setSelectionRow(current_row-1);
|
---|
1335 | }
|
---|
1336 | } else {
|
---|
1337 | if (current_row < tree.getRowCount()-1){
|
---|
1338 | tree.clearSelection();
|
---|
1339 | tree.setSelectionRow(current_row+1);
|
---|
1340 | }
|
---|
1341 | }
|
---|
1342 | tree.setImmediate(false);
|
---|
1343 | } else if (event.getKeyCode() == KeyEvent.VK_LEFT) {
|
---|
1344 | // left key on a file shifts the selection to the parent folder
|
---|
1345 | DragTree tree = (DragTree)event.getSource();
|
---|
1346 | TreePath path = tree.getLeadSelectionPath();
|
---|
1347 | if(path != null) {
|
---|
1348 | File file = ((FileNode)path.getLastPathComponent()).getFile();
|
---|
1349 | if(file != null) {
|
---|
1350 | if (file.isFile() || vk_left_pressed) {
|
---|
1351 | vk_left_pressed = false;
|
---|
1352 | TreePath parent_path = path.getParentPath();
|
---|
1353 | if (parent_path != null && parent_path.getParentPath() != null) {
|
---|
1354 | // if this file is in the top level folder, don't move the focus
|
---|
1355 | tree.setImmediate(true);
|
---|
1356 | tree.clearSelection();
|
---|
1357 | tree.setSelectionPath(parent_path);
|
---|
1358 | tree.setImmediate(false);
|
---|
1359 | }
|
---|
1360 | }
|
---|
1361 | }
|
---|
1362 | }
|
---|
1363 | }
|
---|
1364 | }
|
---|
1365 |
|
---|
1366 | // 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.
|
---|
1367 | public void keyPressed(KeyEvent event) {
|
---|
1368 | if (event.getKeyCode() == KeyEvent.VK_LEFT) {
|
---|
1369 | // left key on closed directory shifts the selection to the parent folder
|
---|
1370 | DragTree tree = (DragTree)event.getSource();
|
---|
1371 | TreePath path = tree.getLeadSelectionPath();
|
---|
1372 | if(path == null) return;
|
---|
1373 | File file = ((FileNode)path.getLastPathComponent()).getFile();
|
---|
1374 | if(file == null) return;
|
---|
1375 |
|
---|
1376 | if (file.isDirectory() && tree.isCollapsed(path)) {
|
---|
1377 | vk_left_pressed = true;
|
---|
1378 | }
|
---|
1379 | }
|
---|
1380 | }
|
---|
1381 | }
|
---|
1382 |
|
---|
1383 |
|
---|
1384 | /** A listener for right mouse button clicks over the collection tree. */
|
---|
1385 | private class RightButtonListener
|
---|
1386 | extends MouseAdapter {
|
---|
1387 | /** Called whenever a mouse click occurs (right or left) over a target component.
|
---|
1388 | * @param event A <strong>MouseEvent</strong> containing further information about the mouse click action.
|
---|
1389 | * @see org.greenstone.gatherer.gui.EnrichPane.RightButtonMenu
|
---|
1390 | */
|
---|
1391 | public void mouseClicked(MouseEvent event) {
|
---|
1392 | if (SwingUtilities.isRightMouseButton(event)) {
|
---|
1393 | new RightButtonMenu(event);
|
---|
1394 | }
|
---|
1395 | }
|
---|
1396 | }
|
---|
1397 |
|
---|
1398 |
|
---|
1399 | /** 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.
|
---|
1400 | */
|
---|
1401 | private class RightButtonMenu
|
---|
1402 | extends JPopupMenu
|
---|
1403 | implements ActionListener {
|
---|
1404 |
|
---|
1405 | /** The tree over which the right click action occurred. */
|
---|
1406 | private DragTree tree = null;
|
---|
1407 | /** The tree nodes selected when the right click action occurred. */
|
---|
1408 | private TreePath[] selection_paths = null;
|
---|
1409 | /** The file record over which the right click action occurred. */
|
---|
1410 | private FileNode node = null;
|
---|
1411 |
|
---|
1412 | private JMenuItem collapse_folder = null;
|
---|
1413 | private JMenuItem expand_folder = null;
|
---|
1414 | private JMenuItem metaaudit = null;
|
---|
1415 | private JMenuItem open_externally = null;
|
---|
1416 |
|
---|
1417 |
|
---|
1418 | private RightButtonMenu(MouseEvent event)
|
---|
1419 | {
|
---|
1420 | super();
|
---|
1421 | this.tree = collection_tree;
|
---|
1422 |
|
---|
1423 | // Get the paths currently selected in the tree
|
---|
1424 | selection_paths = tree.getSelectionPaths();
|
---|
1425 |
|
---|
1426 | // Create an appropriate context menu, based on what is selected
|
---|
1427 | buildContextMenu(selection_paths);
|
---|
1428 |
|
---|
1429 | // Show the popup menu on screen
|
---|
1430 | show(tree, event.getX(), event.getY());
|
---|
1431 | }
|
---|
1432 |
|
---|
1433 |
|
---|
1434 | private void buildContextMenu(TreePath[] selection_paths)
|
---|
1435 | {
|
---|
1436 | // If nothing is selected, no options are available
|
---|
1437 | if (selection_paths == null) {
|
---|
1438 | return;
|
---|
1439 | }
|
---|
1440 |
|
---|
1441 | DebugStream.println("Number of files/folders selected: " + selection_paths.length);
|
---|
1442 |
|
---|
1443 | // Always have meta-audit option
|
---|
1444 | metaaudit = new JMenuItem(Dictionary.get("Menu.Metadata_View", collection_tree.getSelectionDetails()), KeyEvent.VK_A);
|
---|
1445 | metaaudit.addActionListener(this);
|
---|
1446 | add(metaaudit);
|
---|
1447 |
|
---|
1448 | // Only meta-audit is available if multiple items are selected...
|
---|
1449 | if (selection_paths.length > 1) {
|
---|
1450 | return;
|
---|
1451 | }
|
---|
1452 |
|
---|
1453 | TreePath path = selection_paths[0];
|
---|
1454 | node = (FileNode) path.getLastPathComponent();
|
---|
1455 |
|
---|
1456 | // ---- Options for file nodes ----
|
---|
1457 | if (node.isLeaf()) {
|
---|
1458 | // Open the file in an external program
|
---|
1459 | open_externally = new JMenuItem(Dictionary.get("Menu.Open_Externally"), KeyEvent.VK_O);
|
---|
1460 | open_externally.addActionListener(this);
|
---|
1461 | add(open_externally);
|
---|
1462 | return;
|
---|
1463 | }
|
---|
1464 |
|
---|
1465 | // ---- Options for folder nodes ----
|
---|
1466 | // Collapse or expand, depending on current status
|
---|
1467 | if (tree.isExpanded(path)) {
|
---|
1468 | collapse_folder = new JMenuItem(Dictionary.get("Menu.Collapse"), KeyEvent.VK_C);
|
---|
1469 | collapse_folder.addActionListener(this);
|
---|
1470 | add(collapse_folder);
|
---|
1471 | }
|
---|
1472 | else {
|
---|
1473 | expand_folder = new JMenuItem(Dictionary.get("Menu.Expand"), KeyEvent.VK_O);
|
---|
1474 | expand_folder.addActionListener(this);
|
---|
1475 | add(expand_folder);
|
---|
1476 | }
|
---|
1477 | }
|
---|
1478 |
|
---|
1479 |
|
---|
1480 | /** Called whenever one of the menu items is clicked, this method then causes the appropriate effect. */
|
---|
1481 | public void actionPerformed(ActionEvent event)
|
---|
1482 | {
|
---|
1483 | Object source = event.getSource();
|
---|
1484 |
|
---|
1485 | // Collapse folder
|
---|
1486 | if (source == collapse_folder) {
|
---|
1487 | tree.collapsePath(selection_paths[0]);
|
---|
1488 | }
|
---|
1489 |
|
---|
1490 | // Expand folder
|
---|
1491 | else if (source == expand_folder) {
|
---|
1492 | tree.expandPath(selection_paths[0]);
|
---|
1493 | }
|
---|
1494 |
|
---|
1495 | // Meta-audit
|
---|
1496 | else if (source == metaaudit) {
|
---|
1497 | Gatherer.g_man.showMetaAuditBox();
|
---|
1498 | }
|
---|
1499 |
|
---|
1500 | // Open in external program
|
---|
1501 | else if (source == open_externally) {
|
---|
1502 | Gatherer.self.spawnApplication(node.getFile());
|
---|
1503 | }
|
---|
1504 | }
|
---|
1505 | }
|
---|
1506 | }
|
---|