1 | /**
|
---|
2 | *#########################################################################
|
---|
3 | *
|
---|
4 | * A component of the Gatherer application, part of the Greenstone digital
|
---|
5 | * library suite from the New Zealand Digital Library Project at the
|
---|
6 | * University of Waikato, New Zealand.
|
---|
7 | *
|
---|
8 | * <BR><BR>
|
---|
9 | *
|
---|
10 | * Author: John Thompson, Greenstone Digital Library, University of Waikato
|
---|
11 | *
|
---|
12 | * <BR><BR>
|
---|
13 | *
|
---|
14 | * Copyright (C) 1999 New Zealand Digital Library Project
|
---|
15 | *
|
---|
16 | * <BR><BR>
|
---|
17 | *
|
---|
18 | * This program is free software; you can redistribute it and/or modify
|
---|
19 | * it under the terms of the GNU General Public License as published by
|
---|
20 | * the Free Software Foundation; either version 2 of the License, or
|
---|
21 | * (at your option) any later version.
|
---|
22 | *
|
---|
23 | * <BR><BR>
|
---|
24 | *
|
---|
25 | * This program is distributed in the hope that it will be useful,
|
---|
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
28 | * GNU General Public License for more details.
|
---|
29 | *
|
---|
30 | * <BR><BR>
|
---|
31 | *
|
---|
32 | * You should have received a copy of the GNU General Public License
|
---|
33 | * along with this program; if not, write to the Free Software
|
---|
34 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
35 | *########################################################################
|
---|
36 | */
|
---|
37 | package org.greenstone.gatherer.gui;
|
---|
38 |
|
---|
39 | import java.awt.*;
|
---|
40 | import java.awt.event.*;
|
---|
41 | import java.io.*;
|
---|
42 | import java.util.*;
|
---|
43 | import javax.swing.*;
|
---|
44 | import javax.swing.border.*;
|
---|
45 | import javax.swing.event.*;
|
---|
46 | import javax.swing.table.*;
|
---|
47 | import javax.swing.tree.*;
|
---|
48 | import org.greenstone.gatherer.Dictionary;
|
---|
49 | import org.greenstone.gatherer.Gatherer;
|
---|
50 | import org.greenstone.gatherer.file.FileNode;
|
---|
51 | import org.greenstone.gatherer.gui.Filter;
|
---|
52 | import org.greenstone.gatherer.gui.GComboBox;
|
---|
53 | import org.greenstone.gatherer.gui.MetaEditPrompt;
|
---|
54 | import org.greenstone.gatherer.gui.TransformCharacterTextField;
|
---|
55 | import org.greenstone.gatherer.gui.WarningDialog;
|
---|
56 | import org.greenstone.gatherer.gui.table.GTableModel;
|
---|
57 | import org.greenstone.gatherer.gui.table.TableCellRenderer;
|
---|
58 | import org.greenstone.gatherer.gui.tree.DragTree;
|
---|
59 | import org.greenstone.gatherer.msm.ElementWrapper;
|
---|
60 | import org.greenstone.gatherer.msm.Metadata;
|
---|
61 | import org.greenstone.gatherer.msm.MSMEvent;
|
---|
62 | import org.greenstone.gatherer.msm.MSMListener;
|
---|
63 | import org.greenstone.gatherer.util.ArrayTools;
|
---|
64 | import org.greenstone.gatherer.util.Codec;
|
---|
65 | import org.greenstone.gatherer.util.DragGroup;
|
---|
66 | import org.greenstone.gatherer.util.PatternTokenizer;
|
---|
67 | import org.greenstone.gatherer.util.StaticStrings;
|
---|
68 | import org.greenstone.gatherer.util.TreeSynchronizer;
|
---|
69 | import org.greenstone.gatherer.util.Utility;
|
---|
70 | import org.greenstone.gatherer.valuetree.GValueModel;
|
---|
71 | import org.greenstone.gatherer.valuetree.GValueNode;
|
---|
72 | /** Provides a view of controls for the editing of metadata. It makes use of several other important components such as a GTree, GTable and GValueTree. While much of the gui content is left to these components, the MetaEditPane 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).
|
---|
73 | * @author John Thompson, Greenstone Digital Libraries, University of Waikato
|
---|
74 | * @version 2.3b
|
---|
75 | */
|
---|
76 | public class MetaEditPane
|
---|
77 | extends JPanel
|
---|
78 | implements ActionListener, ListSelectionListener, TreeSelectionListener, MSMListener {
|
---|
79 | /** The GValueTree graphically shows the available metadata that has been previously assigned, and provides controls for adding, updating and removing metadata, depending on the users selections in the other important components. */
|
---|
80 | private GValueTree tree = null;
|
---|
81 | /** The layout manager used to display either the GTable (when records are selected) or a placeholder panel with the immortal words 'No Record Selected'. */
|
---|
82 | private CardLayout card_layout = null;
|
---|
83 | /** The layout manager used to display either the GValueTree (when records are selected) or a placeholder panel with the immortal words 'No Record Selected'. */
|
---|
84 | private CardLayout card_layout2 = null;
|
---|
85 | /** Used to dynamically filter the collection tree at user request. Note that this is synchronized with the collection tree filter in the Gather view. */
|
---|
86 | private Filter filter = null;
|
---|
87 | /** The data model behind the GTable, to which we hold a reference for convenient access. */
|
---|
88 | private GTableModel model = null;
|
---|
89 | /** The currently reported selection. Note that this may actually be incorrect, but if so the changed flag will be set, and the selection will be updated the next time it is needed. */
|
---|
90 | private FileNode records[] = null;
|
---|
91 | /** The button, which when clicked, adds metadata to the selected records. */
|
---|
92 | private JButton add;
|
---|
93 | /** The button, which when clicked, removes the selected metadata from the selected records. */
|
---|
94 | private JButton remove;
|
---|
95 | /** The button, which when clicked, updates the selected metadata from the selected records. */
|
---|
96 | private JButton update;
|
---|
97 | /** This button creates an editor dialog to allow for an expanded area to type in text. */
|
---|
98 | private JButton expand;
|
---|
99 | private JButton expand_for_extracted;
|
---|
100 | /** The label at the top of the collection tree, which shows the collection name. */
|
---|
101 | private JLabel collection_label;
|
---|
102 | /** The panel in which the metadata value card layout resides. */
|
---|
103 | private JPanel control_pane;
|
---|
104 | /** The panel in which the metadata table card layout resides. */
|
---|
105 | private JPanel table_card_pane;
|
---|
106 | /** The splitpane dividing the collection tree and the metadata based controls. */
|
---|
107 | private JSplitPane external_split;
|
---|
108 | /** A reference to the metadata table, via its superclass. */
|
---|
109 | private JTable table;
|
---|
110 | /** The label shown at the top of the metadata table detailing the current selection statistics. */
|
---|
111 | private JTextField table_label;
|
---|
112 | /** A reference to the collection tree. */
|
---|
113 | private DragTree collection_tree;
|
---|
114 | /** The currently selected metadata determined by listening to every second list selection event from the metadata table. */
|
---|
115 | private Metadata selected_metadata;
|
---|
116 | /** Provide synchronization between the collection trees in this view and the collection pane view. */
|
---|
117 | private TreeSynchronizer tree_sync = null;
|
---|
118 | static private Dimension BUTTON_SIZE = new Dimension(190, 25);
|
---|
119 | static private Dimension CONTROL_SIZE = new Dimension(560, 240);
|
---|
120 | static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
|
---|
121 | static private Dimension TABLE_SIZE = new Dimension(560, 25);
|
---|
122 | static private Dimension TREE_SIZE = new Dimension(250, 500);
|
---|
123 | /** The name of the panel containing the metadata table. */
|
---|
124 | static final private String CARD_ONE = "Card One";
|
---|
125 | /** The name of the panel containing the 'no file' placeholder for the metadata table. */
|
---|
126 | static final private String CARD_TWO = "Card Two";
|
---|
127 | /** The name of the panel containing the 'no metadata' placeholder for the metadata table. */
|
---|
128 | static final private String CARD_ZERO = "Card Zero";
|
---|
129 | /** The name of the panel containing the placeholder for the value tree. */
|
---|
130 | static final private String TOOLS_OFF = "Tools Off";
|
---|
131 | /** The name of the panel containing the value tree. */
|
---|
132 | static final private String TOOLS_ON = "Tools On";
|
---|
133 |
|
---|
134 | /** Constructor.
|
---|
135 | * @param tree_sync The <strong>TreeSynchronizer</strong> to be used on the collection tree
|
---|
136 | * @see org.greenstone.gatherer.Configuration
|
---|
137 | * @see org.greenstone.gatherer.gui.table.GTable
|
---|
138 | * @see org.greenstone.gatherer.valuetree.GValueTree
|
---|
139 | */
|
---|
140 | public MetaEditPane(TreeSynchronizer tree_sync)
|
---|
141 | {
|
---|
142 | this.tree = null;
|
---|
143 | this.tree_sync = tree_sync;
|
---|
144 |
|
---|
145 | add = new JButton();
|
---|
146 | add.addActionListener(this);
|
---|
147 | add.setEnabled(false);
|
---|
148 | add.setMnemonic(KeyEvent.VK_A);
|
---|
149 | add.setPreferredSize(BUTTON_SIZE);
|
---|
150 | Dictionary.registerBoth(add, "MetaEdit.Accumulate", "MetaEdit.Accumulate_Tooltip");
|
---|
151 |
|
---|
152 | update = new JButton();
|
---|
153 | update.addActionListener(this);
|
---|
154 | update.setEnabled(false);
|
---|
155 | update.setMnemonic(KeyEvent.VK_P);
|
---|
156 | update.setPreferredSize(BUTTON_SIZE);
|
---|
157 | Dictionary.registerBoth(update, "MetaEdit.Overwrite", "MetaEdit.Overwrite_Tooltip");
|
---|
158 |
|
---|
159 | remove = new JButton();
|
---|
160 | remove.addActionListener(this);
|
---|
161 | remove.setEnabled(false);
|
---|
162 | remove.setMnemonic(KeyEvent.VK_R);
|
---|
163 | remove.setPreferredSize(BUTTON_SIZE);
|
---|
164 | Dictionary.registerBoth(remove, "MetaEdit.Remove", "MetaEdit.Remove_Tooltip");
|
---|
165 |
|
---|
166 | expand = new JButton();
|
---|
167 | expand.addActionListener(this);
|
---|
168 | expand.setEnabled(true);
|
---|
169 | expand.setMnemonic(KeyEvent.VK_E);
|
---|
170 | expand.setPreferredSize(new Dimension(25, 25));
|
---|
171 | Dictionary.registerBoth(expand, "MetaEdit.Expand", "MetaEdit.Expand_Tooltip");
|
---|
172 |
|
---|
173 | expand_for_extracted = new JButton();
|
---|
174 | expand_for_extracted.addActionListener(this);
|
---|
175 | expand_for_extracted.setEnabled(true);
|
---|
176 | expand_for_extracted.setMnemonic(KeyEvent.VK_E);
|
---|
177 | expand_for_extracted.setPreferredSize(new Dimension(25, 25));
|
---|
178 | Dictionary.registerBoth(expand_for_extracted, "MetaEdit.Expand", "MetaEdit.Expand_Tooltip");
|
---|
179 |
|
---|
180 | tree = new GValueTree(CONTROL_SIZE.width, CONTROL_SIZE.height);
|
---|
181 | }
|
---|
182 |
|
---|
183 |
|
---|
184 | /** Called whenever an action occurs on one of our registered buttons.
|
---|
185 | * @param event An <strong>ActionEvent</strong> containing information about the event.
|
---|
186 | * @see org.greenstone.gatherer.collection.CollectionManager
|
---|
187 | * @see org.greenstone.gatherer.gui.table.GTableModel
|
---|
188 | * @see org.greenstone.gatherer.msm.ElementWrapper
|
---|
189 | * @see org.greenstone.gatherer.msm.MetadataSetManager
|
---|
190 | * @see org.greenstone.gatherer.msm.MSMEvent
|
---|
191 | * @see org.greenstone.gatherer.valuetree.GValueTree
|
---|
192 | */
|
---|
193 | public void actionPerformed(ActionEvent event) {
|
---|
194 | Object esrc = event.getSource();
|
---|
195 | if (esrc == add) {
|
---|
196 | ElementWrapper element = tree.getSelectedMetadataElement();
|
---|
197 | String value = tree.getSelectedValue();
|
---|
198 | (new AppendMetadataTask(element, value)).start();
|
---|
199 | value = null;
|
---|
200 | element = null;
|
---|
201 | }
|
---|
202 | else if (esrc == update) {
|
---|
203 | ElementWrapper element = tree.getSelectedMetadataElement();
|
---|
204 | String value = tree.getSelectedValue();
|
---|
205 | (new UpdateMetadataTask(element, value)).start();
|
---|
206 | value = null;
|
---|
207 | element = null;
|
---|
208 | }
|
---|
209 | else if (esrc == remove) {
|
---|
210 | (new RemoveMetadataTask()).start();
|
---|
211 | }
|
---|
212 | else if (esrc == expand) {
|
---|
213 | EditorDialog ed = new EditorDialog();
|
---|
214 | String temp = ed.display(tree.getSelectedValue());
|
---|
215 | if (temp != null) {
|
---|
216 | tree.setSelectedValue(temp);
|
---|
217 | }
|
---|
218 | }
|
---|
219 | else if(esrc == expand_for_extracted) {
|
---|
220 | EditorDialog ed = new EditorDialog();
|
---|
221 | ed.setEditable(false);
|
---|
222 | String temp = ed.display(selected_metadata.getValue());
|
---|
223 | }
|
---|
224 | validateControls();
|
---|
225 | }
|
---|
226 |
|
---|
227 | private class AppendMetadataTask
|
---|
228 | extends Thread {
|
---|
229 |
|
---|
230 | private ElementWrapper element = null;
|
---|
231 | private String value = null;
|
---|
232 |
|
---|
233 | private AppendMetadataTask(ElementWrapper element, String raw_value) {
|
---|
234 | this.element = element;
|
---|
235 | this.value = raw_value;
|
---|
236 | // Transform the raw text to be GREENSTONE and DOM compatible - as that will be its next destination immediately after being added to the value model
|
---|
237 | //this.value = /odec.transform(value, /odec.ENCODE_PATH);
|
---|
238 | //this.value = /odec.transform(this.value, /odec.REMOVE_SQUARE_BRACKET);
|
---|
239 | //this.value = /odec.transform(this.value, /odec.TEXT_TO_DOM);
|
---|
240 | }
|
---|
241 |
|
---|
242 | public void run() {
|
---|
243 | System.err.println("Add metadata - '" + value + "'");
|
---|
244 | // Check the new metadata is valid
|
---|
245 | if(records != null && element != null && value != null) {
|
---|
246 | // Check the records, and if they are folders then display the warning.
|
---|
247 | Metadata added_metadata = null;
|
---|
248 | if(!records[0].isLeaf()) {
|
---|
249 | WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", true);
|
---|
250 | if(dialog.display() == JOptionPane.OK_OPTION) {
|
---|
251 | added_metadata = Gatherer.c_man.getCollection().msm.addMetadata(System.currentTimeMillis(), records, element, value);
|
---|
252 | }
|
---|
253 | dialog.dispose();
|
---|
254 | dialog = null;
|
---|
255 | }
|
---|
256 | else {
|
---|
257 | added_metadata = Gatherer.c_man.getCollection().msm.addMetadata(System.currentTimeMillis(), records, element, value);
|
---|
258 | }
|
---|
259 | model.selectMetadataWhenBuildingComplete(added_metadata);
|
---|
260 | }
|
---|
261 | }
|
---|
262 | }
|
---|
263 |
|
---|
264 | private class UpdateMetadataTask
|
---|
265 | extends Thread {
|
---|
266 |
|
---|
267 | private ElementWrapper element;
|
---|
268 | private String value;
|
---|
269 |
|
---|
270 | private UpdateMetadataTask(ElementWrapper element, String raw_value) {
|
---|
271 | this.element = element;
|
---|
272 | this.value = raw_value;
|
---|
273 | // Transform the raw text to be DOM compatible - as that will be its next destination immediately after being added to the value model
|
---|
274 | //this.value = /odec.transform(value, /odec.REMOVE_SQUARE_BRACKET);
|
---|
275 | //this.value = /odec.transform(this.value, /odec.TEXT_TO_DOM);
|
---|
276 | }
|
---|
277 |
|
---|
278 | public void run() {
|
---|
279 | // You can only update if there is a selected_metadata and
|
---|
280 | // you have valid values in all fields.
|
---|
281 | Gatherer.println("Replacing value:");
|
---|
282 | if(selected_metadata != null && records != null && element != null && value != null) {
|
---|
283 | selected_metadata = Gatherer.c_man.getCollection().msm.updateMetadata(System.currentTimeMillis(), selected_metadata, records, value, MetaEditPrompt.CONFIRM, selected_metadata.isFileLevel());
|
---|
284 | }
|
---|
285 | model.selectMetadataWhenBuildingComplete(selected_metadata);
|
---|
286 | }
|
---|
287 | }
|
---|
288 |
|
---|
289 | private class RemoveMetadataTask
|
---|
290 | extends Thread {
|
---|
291 |
|
---|
292 | private RemoveMetadataTask() {}
|
---|
293 |
|
---|
294 | public void run() {
|
---|
295 | Gatherer.println("Removing value:");
|
---|
296 | if(selected_metadata != null && records != null) {
|
---|
297 | Gatherer.c_man.getCollection().msm.removeMetadata(System.currentTimeMillis(), selected_metadata, records);
|
---|
298 | }
|
---|
299 |
|
---|
300 | // Select the closest piece of metadata with the same element name
|
---|
301 | model.selectClosestMetadataWhenBuildingComplete(selected_metadata);
|
---|
302 | }
|
---|
303 | }
|
---|
304 |
|
---|
305 | /** 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.
|
---|
306 | * @see org.greenstone.gatherer.gui.table.GTableModel
|
---|
307 | */
|
---|
308 | public void afterDisplay() {
|
---|
309 | external_split.setDividerLocation(0.3);
|
---|
310 | if(table != null) {
|
---|
311 | Dimension table_size = table.getPreferredSize();
|
---|
312 | TableColumnModel column_model = table.getColumnModel();
|
---|
313 |
|
---|
314 | TableColumn inherited_column = column_model.getColumn(0);
|
---|
315 | inherited_column.setPreferredWidth(20);
|
---|
316 | inherited_column.setCellRenderer(new TableCellRenderer(model));
|
---|
317 |
|
---|
318 | TableColumn element_column = column_model.getColumn(1);
|
---|
319 | element_column.setPreferredWidth(table_size.width / 4);
|
---|
320 | element_column.setCellRenderer(new TableCellRenderer(model));
|
---|
321 |
|
---|
322 | TableColumn value_column = column_model.getColumn(2);
|
---|
323 | value_column.setPreferredWidth(((3 * table_size.width) / 4) - 15);
|
---|
324 | value_column.setCellRenderer(new TableCellRenderer(model));
|
---|
325 | }
|
---|
326 | table.doLayout();
|
---|
327 | model.fireTableDataChanged();
|
---|
328 | }
|
---|
329 | /** Called whenever a significant change has occured in the state of the currently loaded collection.
|
---|
330 | * @param ready <i>true</i> if there is a collection currently ready to be editing, <i>false</i> otherwise.
|
---|
331 | * @see org.greenstone.gatherer.collection.CollectionManager
|
---|
332 | * @see org.greenstone.gatherer.collection.CollectionModel
|
---|
333 | * @see org.greenstone.gatherer.tree.GTree
|
---|
334 | * @see org.greenstone.gatherer.util.TreeSynchronizer
|
---|
335 | */
|
---|
336 | public void collectionChanged(boolean ready) {
|
---|
337 | if(ready) {
|
---|
338 | TreeModel collection_model = Gatherer.c_man.getRecordSet();
|
---|
339 | //String[] args = new String[1];
|
---|
340 | //args[0] = Gatherer.c_man.getCollection().getName();
|
---|
341 | Dictionary.registerText(collection_label, "Collection.Collection");
|
---|
342 | // Update label coloring.
|
---|
343 | collection_label.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
|
---|
344 | collection_label.setForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
|
---|
345 | collection_tree.setModel(collection_model);
|
---|
346 | // Update tree coloring.
|
---|
347 | collection_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
|
---|
348 | collection_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
|
---|
349 | // Register our msm dependant components (or correctly their models) with msm.
|
---|
350 | if(model != null) {
|
---|
351 | // remove the listener first - in case its already present
|
---|
352 | Gatherer.c_man.getCollection().msm.removeMSMListener(model);
|
---|
353 | Gatherer.c_man.getCollection().msm.addMSMListener(model);
|
---|
354 | }
|
---|
355 | // register the pane itself as an MSMListener, so if we add a new metadata set, it will immediately show up. - remove itself first just in case
|
---|
356 | Gatherer.c_man.getCollection().msm.removeMSMListener(this);
|
---|
357 | Gatherer.c_man.getCollection().msm.addMSMListener(this);
|
---|
358 | }
|
---|
359 | else {
|
---|
360 | Dictionary.registerText(collection_label, "Collection.No_Collection");
|
---|
361 | collection_label.setBackground(Color.lightGray);
|
---|
362 | collection_label.setForeground(Color.black);
|
---|
363 | collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode(Dictionary.get("Collection.No_Collection"))));
|
---|
364 | }
|
---|
365 |
|
---|
366 | filter.setEnabled(ready);
|
---|
367 | // Ensure that this collection tree view is synchronized with any others.
|
---|
368 | tree_sync.add(collection_tree);
|
---|
369 | }
|
---|
370 | /** Used to create, connect and layout the components to be shown on this control panel.
|
---|
371 | * @see org.greenstone.gatherer.Gatherer
|
---|
372 | * @see org.greenstone.gatherer.collection.CollectionManager
|
---|
373 | * @see org.greenstone.gatherer.collection.CollectionModel
|
---|
374 | * @see org.greenstone.gatherer.file.FileOpenActionListener
|
---|
375 | * @see org.greenstone.gatherer.gui.Filter
|
---|
376 | * @see org.greenstone.gatherer.gui.GComboBox
|
---|
377 | * @see org.greenstone.gatherer.gui.table.GTableModel
|
---|
378 | * @see org.greenstone.gatherer.tree.GTree
|
---|
379 | * @see org.greenstone.gatherer.tree.GTreeModel
|
---|
380 | * @see org.greenstone.gatherer.valuetree.GValueTree
|
---|
381 | */
|
---|
382 | public void display() {
|
---|
383 | RightButtonListener right_button_listener = new RightButtonListener();
|
---|
384 | // Creation
|
---|
385 | JPanel collection_pane = new JPanel(new BorderLayout());
|
---|
386 | collection_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
387 | collection_pane.setPreferredSize(TREE_SIZE);
|
---|
388 |
|
---|
389 | external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
---|
390 |
|
---|
391 | ///atherer.println("\tCreating collection_label");
|
---|
392 | collection_label = new JLabel();
|
---|
393 | Dictionary.registerText(collection_label, "Collection.No_Collection");
|
---|
394 | collection_label.setOpaque(true);
|
---|
395 |
|
---|
396 | DragGroup group = new DragGroup();
|
---|
397 | TreeModel collection_model = Gatherer.c_man.getRecordSet();
|
---|
398 | if(collection_model != null) {
|
---|
399 | collection_tree = new DragTree("MetaEdit", collection_model, null, false);
|
---|
400 | }
|
---|
401 | else {
|
---|
402 | collection_tree = new DragTree("MetaEdit", null, false);
|
---|
403 | }
|
---|
404 | group.add(collection_tree);
|
---|
405 | collection_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
---|
406 | collection_tree.putClientProperty("JTree.lineStyle", "Angled");
|
---|
407 | collection_tree.addMouseListener(Gatherer.g_man.foa_listener);
|
---|
408 | collection_tree.addMouseListener(right_button_listener);
|
---|
409 | collection_tree.addTreeSelectionListener(this);
|
---|
410 | collection_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
|
---|
411 | collection_tree.setLargeModel(true);
|
---|
412 | collection_tree.setRootVisible(false);
|
---|
413 |
|
---|
414 | JScrollPane collection_scroll = new JScrollPane(collection_tree);
|
---|
415 |
|
---|
416 | filter = Gatherer.g_man.getFilter(collection_tree);
|
---|
417 | filter.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
|
---|
418 | // Change the default colours of this filters combobox.
|
---|
419 | GComboBox fcb = filter.getComboBox();
|
---|
420 | fcb.setBackgroundNonSelectionColor(Color.white);
|
---|
421 | fcb.setTextNonSelectionColor(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
|
---|
422 | fcb.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
|
---|
423 | fcb.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
|
---|
424 | Dictionary.registerTooltip(fcb, "Collection.Filter_Tooltip");
|
---|
425 | fcb = null;
|
---|
426 |
|
---|
427 | // Connection
|
---|
428 |
|
---|
429 | // Layout
|
---|
430 | collection_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createLoweredBevelBorder()));
|
---|
431 | collection_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
432 | collection_pane.setPreferredSize(new Dimension(Gatherer.g_man.getSize().width / 3, Gatherer.g_man.getSize().height));
|
---|
433 |
|
---|
434 | // Collection Pane
|
---|
435 |
|
---|
436 | collection_pane.add(collection_label, BorderLayout.NORTH);
|
---|
437 | collection_pane.add(collection_scroll, BorderLayout.CENTER);
|
---|
438 | collection_pane.add(filter, BorderLayout.SOUTH);
|
---|
439 |
|
---|
440 | JSplitPane main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
---|
441 | main_split_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
|
---|
442 | main_split_pane.setDividerSize(8);
|
---|
443 |
|
---|
444 | // Metadata Table. In order to achieve what Ian envisions I'm going
|
---|
445 | // to have to do a tricky. The table itself will sit in a stacked
|
---|
446 | // CardLayout. Card zero will be a see-through JPanel with a message
|
---|
447 | // smack bang in the center, something like "No Metadata". If any
|
---|
448 | // page of the table would appear to have no metadata this card will
|
---|
449 | // be swapped to the front.
|
---|
450 |
|
---|
451 | JPanel table_pane = new JPanel();
|
---|
452 | table_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
|
---|
453 |
|
---|
454 | JPanel table_title_pane = new JPanel();
|
---|
455 |
|
---|
456 | table_label = new JTextField();
|
---|
457 | table_label.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
|
---|
458 | table_label.setEditable(false);
|
---|
459 | Dictionary.registerText(table_label, "MetaEdit.No_File");
|
---|
460 |
|
---|
461 | card_layout = new CardLayout();
|
---|
462 |
|
---|
463 | table_card_pane = new JPanel();
|
---|
464 |
|
---|
465 | JPanel table_pane_zero = new JPanel();
|
---|
466 | table_pane_zero.setOpaque(false);
|
---|
467 |
|
---|
468 | JPanel table_pane_two = new JPanel();
|
---|
469 | table_pane_two.setOpaque(false);
|
---|
470 |
|
---|
471 | JLabel no_file_message = new JLabel();
|
---|
472 | no_file_message.setHorizontalAlignment(JLabel.CENTER);
|
---|
473 | no_file_message.setOpaque(false);
|
---|
474 | no_file_message.setVerticalAlignment(JLabel.CENTER);
|
---|
475 | Dictionary.registerText(no_file_message, "MetaEdit.No_File");
|
---|
476 |
|
---|
477 | JLabel no_metadata_message = new JLabel();
|
---|
478 | no_metadata_message.setHorizontalAlignment(JLabel.CENTER);
|
---|
479 | no_metadata_message.setOpaque(false);
|
---|
480 | no_metadata_message.setVerticalAlignment(JLabel.CENTER);
|
---|
481 | Dictionary.registerText(no_metadata_message, "MetaEdit.No_Metadata");
|
---|
482 |
|
---|
483 | JPanel table_pane_one = new JPanel();
|
---|
484 |
|
---|
485 | ///atherer.println("\tCreating metadata_table");
|
---|
486 | table = new JTable();
|
---|
487 | model = new GTableModel(table);
|
---|
488 | table.setModel(model);
|
---|
489 | table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
---|
490 | table.getSelectionModel().addListSelectionListener(this);
|
---|
491 | table.setColumnSelectionAllowed(false);
|
---|
492 |
|
---|
493 | // The default JTable doesn't quite have the desired properties - when a
|
---|
494 | // table cell is clicked on, the table is scrolled so the cell is as
|
---|
495 | // visible as possible. This means that the left-most cells can become
|
---|
496 | // hidden. To fix this, the MouseInputHandler in BasicTableUI is removed
|
---|
497 | // as a MouseListener on the table, and a very slightly altered copy is
|
---|
498 | // added in its place (TableMouseInputHandler).
|
---|
499 | MouseListener[] mls = table.getMouseListeners();
|
---|
500 | for (int i = 0; i < mls.length; i++) {
|
---|
501 | // Remove the instances of MouseInputHandler
|
---|
502 | if (mls[i].toString().startsWith("javax.swing.plaf.basic.BasicTableUI$MouseInputHandler@")) {
|
---|
503 | table.removeMouseListener(mls[i]);
|
---|
504 | }
|
---|
505 | }
|
---|
506 | // Add the slightly-altered mouse listener
|
---|
507 | table.addMouseListener(new TableMouseInputHandler());
|
---|
508 |
|
---|
509 | JScrollPane table_scroll = new JScrollPane(table);
|
---|
510 | table_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
|
---|
511 | table_scroll.setOpaque(true);
|
---|
512 |
|
---|
513 | // Control pane
|
---|
514 | ///atherer.println("\tCreating Metadata Controls");
|
---|
515 |
|
---|
516 | control_pane = new JPanel();
|
---|
517 | control_pane.setBorder(BorderFactory.createLoweredBevelBorder());
|
---|
518 | control_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
519 | control_pane.setSize(CONTROL_SIZE);
|
---|
520 | control_pane.setPreferredSize(CONTROL_SIZE);
|
---|
521 |
|
---|
522 | card_layout2 = new CardLayout();
|
---|
523 |
|
---|
524 | JPanel tools_off_pane = new JPanel();
|
---|
525 |
|
---|
526 | JLabel tools_off_label = new JLabel();
|
---|
527 | tools_off_label.setHorizontalAlignment(JLabel.CENTER);
|
---|
528 | tools_off_label.setOpaque(false);
|
---|
529 | tools_off_label.setVerticalAlignment(JLabel.CENTER);
|
---|
530 | Dictionary.registerText(tools_off_label, "MetaEdit.No_Metadata_Element");
|
---|
531 |
|
---|
532 | JPanel tools_on_pane = new JPanel();
|
---|
533 | tools_on_pane.setMinimumSize(MINIMUM_SIZE);
|
---|
534 | tools_on_pane.setSize(TABLE_SIZE);
|
---|
535 | tools_on_pane.setPreferredSize(TABLE_SIZE);
|
---|
536 |
|
---|
537 | // Layout.
|
---|
538 |
|
---|
539 | // Metaedit controls
|
---|
540 | tools_off_pane.setLayout(new BorderLayout());
|
---|
541 | tools_off_pane.add(tools_off_label, BorderLayout.CENTER);
|
---|
542 |
|
---|
543 | tools_on_pane.setLayout(new BorderLayout());
|
---|
544 | tools_on_pane.add(tree, BorderLayout.CENTER);
|
---|
545 |
|
---|
546 | control_pane.setLayout(card_layout2);
|
---|
547 | control_pane.add(tools_off_pane, TOOLS_OFF);
|
---|
548 | control_pane.add(tools_on_pane, TOOLS_ON);
|
---|
549 |
|
---|
550 | // Table components
|
---|
551 | table_title_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
|
---|
552 | table_title_pane.setLayout(new BorderLayout());
|
---|
553 | table_title_pane.add(table_label, BorderLayout.CENTER);
|
---|
554 |
|
---|
555 | table_pane_zero.setLayout(new BorderLayout());
|
---|
556 | table_pane_zero.add(no_file_message, BorderLayout.CENTER);
|
---|
557 |
|
---|
558 | table_pane_one.setLayout(new BorderLayout());
|
---|
559 | table_pane_one.add(table_scroll, BorderLayout.CENTER);
|
---|
560 |
|
---|
561 | table_pane_two.setLayout(new BorderLayout());
|
---|
562 | table_pane_two.add(no_metadata_message, BorderLayout.CENTER);
|
---|
563 |
|
---|
564 | table_card_pane.setLayout(card_layout);
|
---|
565 | table_card_pane.add(table_pane_zero, CARD_ZERO);
|
---|
566 | table_card_pane.add(table_pane_one, CARD_ONE);
|
---|
567 | table_card_pane.add(table_pane_two, CARD_TWO);
|
---|
568 |
|
---|
569 | table_pane.setLayout(new BorderLayout());
|
---|
570 | table_pane.add(table_title_pane, BorderLayout.NORTH);
|
---|
571 | table_pane.add(table_card_pane, BorderLayout.CENTER);
|
---|
572 |
|
---|
573 | main_split_pane.add(table_pane, JSplitPane.TOP);
|
---|
574 | main_split_pane.add(control_pane, JSplitPane.BOTTOM);
|
---|
575 | main_split_pane.setDividerLocation(250);
|
---|
576 |
|
---|
577 | external_split.add(collection_pane, JSplitPane.LEFT);
|
---|
578 | external_split.add(main_split_pane, JSplitPane.RIGHT);
|
---|
579 |
|
---|
580 | this.setLayout(new BorderLayout());
|
---|
581 | this.add(external_split, BorderLayout.CENTER);
|
---|
582 | }
|
---|
583 |
|
---|
584 | /** Method that is called whenever an element within a set is changed or modified. - needed for MSMListener
|
---|
585 | * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
|
---|
586 | */
|
---|
587 | public void elementChanged(MSMEvent event) {}
|
---|
588 |
|
---|
589 | /** Ensures a certain tree path is expanded, visible and selected within the collection tree.
|
---|
590 | * @param path The <strong>TreePath</strong> to make visible.
|
---|
591 | * @see org.greenstone.gatherer.tree.GTree
|
---|
592 | */
|
---|
593 | private void expandPath(TreePath path) {
|
---|
594 | collection_tree.expandPath(path);
|
---|
595 | collection_tree.setSelectionPath(path);
|
---|
596 | }
|
---|
597 | /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
|
---|
598 | * @see org.greenstone.gatherer.tree.GTree
|
---|
599 | */
|
---|
600 | public void gainFocus() {
|
---|
601 | // Use this opportunity to update the table model etc.
|
---|
602 | // Select the first file (if any)
|
---|
603 | if(collection_tree != null && collection_tree.getRowCount() > 0) {
|
---|
604 | collection_tree.setImmediate(true);
|
---|
605 | collection_tree.setSelectionRow(0);
|
---|
606 | collection_tree.setImmediate(false);
|
---|
607 | }
|
---|
608 | else {
|
---|
609 | // No selection. Reset other components
|
---|
610 | valueChanged(new TreeSelectionEvent(this, null, false, null, null));
|
---|
611 | }
|
---|
612 |
|
---|
613 | // Update the menubars idea of whats been selected
|
---|
614 | if(collection_tree != null) {
|
---|
615 | if(collection_tree.isSelectionEmpty()) {
|
---|
616 | Gatherer.g_man.menu_bar.setMetaAuditSuffix(null);
|
---|
617 | }
|
---|
618 | else {
|
---|
619 | Gatherer.g_man.menu_bar.setMetaAuditSuffix(collection_tree.getSelectionDetails());
|
---|
620 | }
|
---|
621 | }
|
---|
622 | // Update the meta-audit view to show the current selection, if any.
|
---|
623 | Gatherer.g_man.meta_audit.setRecords(records);
|
---|
624 | }
|
---|
625 |
|
---|
626 | /** Retrieve the currently selected records.
|
---|
627 | * @return A <strong>FileNode[]</strong> containing the current, and up to date, selection.
|
---|
628 | */
|
---|
629 | public FileNode[] getSelectedNode() {
|
---|
630 | return records;
|
---|
631 | }
|
---|
632 |
|
---|
633 | /** Called whenever the metadata value changes in some way, such as the addition of a new value. - needed for MSMListener
|
---|
634 | * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
|
---|
635 | */
|
---|
636 | public void metadataChanged(MSMEvent event) {}
|
---|
637 |
|
---|
638 | public void refreshTrees() {
|
---|
639 | collection_tree.refresh(null); // Refresh entire tree.
|
---|
640 | }
|
---|
641 |
|
---|
642 | /** Method that is called whenever the metadata set collection changes in some way, such as the addition of a new set or the merging of two sets. - needed for MSMListener
|
---|
643 | * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
|
---|
644 | */
|
---|
645 | public void setChanged(MSMEvent event){
|
---|
646 | // call the collection tree value changed - we need to do the same stuff here
|
---|
647 | valueChanged((TreeSelectionEvent) null);
|
---|
648 | }
|
---|
649 |
|
---|
650 | /** This method enables or diables the various controls based upon the state of the system. This method should be called after every change which could affect the GUI.
|
---|
651 | */
|
---|
652 | private void validateControls() {
|
---|
653 | validateMetadataTable();
|
---|
654 | // Validate card_layout_2
|
---|
655 | if (selected_metadata != null) {
|
---|
656 | card_layout2.show(control_pane, TOOLS_ON);
|
---|
657 | }
|
---|
658 | else {
|
---|
659 | card_layout2.show(control_pane, TOOLS_OFF);
|
---|
660 | }
|
---|
661 |
|
---|
662 | // Validate the buttons in the lower pane
|
---|
663 | if (selected_metadata == null) {
|
---|
664 | // Nothing selected
|
---|
665 | return;
|
---|
666 | }
|
---|
667 |
|
---|
668 | // Does the metadata element have no current value?
|
---|
669 | GValueNode value_node = selected_metadata.getValueNode();
|
---|
670 | if (value_node == null) {
|
---|
671 | // Can only append if something has been entered
|
---|
672 | add.setEnabled((tree.getSelectedValue().length() > 0));
|
---|
673 | // Nothing to replace or remove
|
---|
674 | update.setEnabled(false);
|
---|
675 | remove.setEnabled(false);
|
---|
676 | }
|
---|
677 |
|
---|
678 | else {
|
---|
679 | // Check if the text in the value field is the same as the metadata value
|
---|
680 | if (tree.getSelectedValue().equals(value_node.getFullPath(false))) {
|
---|
681 | // Can't replace
|
---|
682 | update.setEnabled(false);
|
---|
683 | // Adding, however, is dependant on whether the metadata is common or uncommon. If the later then you can append so as to make it common.
|
---|
684 | add.setEnabled(!model.isCommon(selected_metadata));
|
---|
685 | }
|
---|
686 | else {
|
---|
687 | // Can append or replace, if something has been entered
|
---|
688 | add.setEnabled((tree.getSelectedValue().length() > 0));
|
---|
689 | update.setEnabled((tree.getSelectedValue().length() > 0));
|
---|
690 | }
|
---|
691 |
|
---|
692 | // Can only remove if the metadata is file level
|
---|
693 | if (selected_metadata != null) { // Shouldn't be necessary, but is
|
---|
694 | remove.setEnabled(selected_metadata.isFileLevel());
|
---|
695 | }
|
---|
696 | }
|
---|
697 | }
|
---|
698 |
|
---|
699 | /** Validate the metadata table area, and if no metadata is available display the no metadata panel. */
|
---|
700 | public void validateMetadataTable() {
|
---|
701 | // Validate card_layout
|
---|
702 | if (records == null) {
|
---|
703 | card_layout.show(table_card_pane, CARD_ZERO);
|
---|
704 | } else if (model.getRowCount() == 0) {
|
---|
705 | card_layout.show(table_card_pane, CARD_TWO);
|
---|
706 | } else {
|
---|
707 | card_layout.show(table_card_pane, CARD_ONE);
|
---|
708 | }
|
---|
709 | }
|
---|
710 |
|
---|
711 | /** Another in a long list of listeners, this one is called whenever the
|
---|
712 | * selection within the JTable changes. When it does we load the new data
|
---|
713 | * and set the selected_metadata if applicable (ie the metadata is non-
|
---|
714 | * empty).
|
---|
715 | * @param event The <strong>ListSelectionEvent</strong> which contains all the information about the event that has been fired.
|
---|
716 | * @see org.greenstone.gatherer.gui.table.GTableModel
|
---|
717 | * @see org.greenstone.gatherer.valuetree.GValueTree
|
---|
718 | */
|
---|
719 | public void valueChanged(ListSelectionEvent event) {
|
---|
720 | // We only want to handle one event per selection, so wait for the value to stabilise
|
---|
721 | ListSelectionModel lsm = table.getSelectionModel();
|
---|
722 | if (lsm.getValueIsAdjusting() == false) {
|
---|
723 | // We have a SINGLE_SELECTION model set so there is at most one
|
---|
724 | // selection. Get the offending row.
|
---|
725 | int selected = table.getSelectedRow();
|
---|
726 | selected_metadata = model.getMetadataAtRow(selected);
|
---|
727 | if (selected_metadata != null) { // Check something is selected.
|
---|
728 | tree.setSelectedMetadataElement(selected_metadata.getElement());
|
---|
729 | GValueNode value_node = selected_metadata.getValueNode();
|
---|
730 | if(value_node != null) {
|
---|
731 | tree.setSelectedValue(value_node.getFullPath(true));
|
---|
732 | }
|
---|
733 | else {
|
---|
734 | tree.setSelectedValue("");
|
---|
735 | }
|
---|
736 | }
|
---|
737 | validateControls();
|
---|
738 | }
|
---|
739 | }
|
---|
740 |
|
---|
741 | /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. - needed for MSMListener
|
---|
742 | * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
|
---|
743 | */
|
---|
744 | public void valueChanged(MSMEvent event) {}
|
---|
745 |
|
---|
746 | /** This method is called whenever the selection within the collection
|
---|
747 | * tree changes. This causes the table to be rebuilt with a new model
|
---|
748 | * @param event A <strong>TreeSelectionEvent</strong> containing information about the event.
|
---|
749 | * @see org.greenstone.gatherer.Gatherer
|
---|
750 | * @see org.greenstone.gatherer.collection.FileNode
|
---|
751 | * @see org.greenstone.gatherer.gui.GUIManager
|
---|
752 | * @see org.greenstone.gatherer.gui.MenuBar
|
---|
753 | * @see org.greenstone.gatherer.gui.table.GTableModel
|
---|
754 | * @see org.greenstone.gatherer.msm.MetadataSetManager
|
---|
755 | * @see org.greenstone.gatherer.tree.GTree
|
---|
756 | * @see org.greenstone.gatherer.util.ArrayTools
|
---|
757 | */
|
---|
758 | public void valueChanged(TreeSelectionEvent event) {
|
---|
759 | ///ystem.err.println("\n(MEP) valueChanged(TreeSelectionEvent)...");
|
---|
760 |
|
---|
761 | // Don't bother if this isn't the selected view
|
---|
762 | if (Gatherer.g_man.getSelectedView() != this) {
|
---|
763 | return;
|
---|
764 | }
|
---|
765 |
|
---|
766 | if(collection_tree.getSelectionCount() > 0) {
|
---|
767 | records = null;
|
---|
768 | TreePath paths[] = collection_tree.getSelectionPaths();
|
---|
769 | for(int i = 0; i < paths.length; i++) {
|
---|
770 | FileNode record = (FileNode) paths[i].getLastPathComponent();
|
---|
771 | records = ArrayTools.add(records, record);
|
---|
772 | }
|
---|
773 |
|
---|
774 | // Remember the previous selection so we can select it again later
|
---|
775 | Metadata previous_selection = selected_metadata;
|
---|
776 |
|
---|
777 | table_label.setText(collection_tree.getSelectionDetails());
|
---|
778 |
|
---|
779 | // Remove old model from msm
|
---|
780 | Gatherer.c_man.getCollection().msm.removeMSMListener(model);
|
---|
781 | // Create new model
|
---|
782 | model = new GTableModel(table, records);
|
---|
783 | table.setModel(model);
|
---|
784 |
|
---|
785 | // Select the closest piece of metadata in the new file
|
---|
786 | model.selectClosestMetadataWhenBuildingComplete(previous_selection);
|
---|
787 | }
|
---|
788 | else {
|
---|
789 | records = null;
|
---|
790 | Dictionary.registerText(table_label, "MetaEdit.No_File");
|
---|
791 |
|
---|
792 | // Remove old model from msm
|
---|
793 | if(Gatherer.c_man.ready()) {
|
---|
794 | Gatherer.c_man.getCollection().msm.removeMSMListener(model);
|
---|
795 | }
|
---|
796 | // Create new model
|
---|
797 | model = new GTableModel(table);
|
---|
798 | table.setModel(model);
|
---|
799 | }
|
---|
800 |
|
---|
801 | table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
---|
802 | if(table != null) {
|
---|
803 | Dimension table_size = table.getPreferredSize();
|
---|
804 | TableColumnModel column_model = table.getColumnModel();
|
---|
805 |
|
---|
806 | TableColumn inherited_column = column_model.getColumn(0);
|
---|
807 | inherited_column.setPreferredWidth(20);
|
---|
808 | inherited_column.setCellRenderer(new TableCellRenderer(model));
|
---|
809 |
|
---|
810 | TableColumn element_column = column_model.getColumn(1);
|
---|
811 | element_column.setPreferredWidth(TABLE_SIZE.width / 4);
|
---|
812 | element_column.setCellRenderer(new TableCellRenderer(model));
|
---|
813 |
|
---|
814 | TableColumn value_column = column_model.getColumn(2);
|
---|
815 | value_column.setPreferredWidth(((3 * TABLE_SIZE.width) / 4) - 15);
|
---|
816 | value_column.setCellRenderer(new TableCellRenderer(model));
|
---|
817 | }
|
---|
818 | table.doLayout();
|
---|
819 | validateControls();
|
---|
820 | }
|
---|
821 |
|
---|
822 |
|
---|
823 | /** 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. */
|
---|
824 | private class TableMouseInputHandler implements MouseInputListener
|
---|
825 | {
|
---|
826 | // Component receiving mouse events during editing.
|
---|
827 | // May not be editorComponent.
|
---|
828 | private Component dispatchComponent;
|
---|
829 | private boolean selectedOnPress;
|
---|
830 |
|
---|
831 | // The Table's mouse listener methods.
|
---|
832 |
|
---|
833 | public void mouseClicked(MouseEvent e) {}
|
---|
834 |
|
---|
835 | private void setDispatchComponent(MouseEvent e) {
|
---|
836 | Component editorComponent = table.getEditorComponent();
|
---|
837 | Point p = e.getPoint();
|
---|
838 | Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
|
---|
839 | dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
|
---|
840 | p2.x, p2.y);
|
---|
841 | }
|
---|
842 |
|
---|
843 | private boolean repostEvent(MouseEvent e) {
|
---|
844 | // Check for isEditing() in case another event has
|
---|
845 | // caused the editor to be removed. See bug #4306499.
|
---|
846 | if (dispatchComponent == null || !table.isEditing()) {
|
---|
847 | return false;
|
---|
848 | }
|
---|
849 | MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e, dispatchComponent);
|
---|
850 | dispatchComponent.dispatchEvent(e2);
|
---|
851 | return true;
|
---|
852 | }
|
---|
853 |
|
---|
854 | private void setValueIsAdjusting(boolean flag) {
|
---|
855 | table.getSelectionModel().setValueIsAdjusting(flag);
|
---|
856 | table.getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
|
---|
857 | }
|
---|
858 |
|
---|
859 | private boolean shouldIgnore(MouseEvent e) {
|
---|
860 | return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && table.isEnabled()));
|
---|
861 | }
|
---|
862 |
|
---|
863 | public void mousePressed(MouseEvent e) {
|
---|
864 | if (e.isConsumed()) {
|
---|
865 | selectedOnPress = false;
|
---|
866 | return;
|
---|
867 | }
|
---|
868 | selectedOnPress = true;
|
---|
869 | adjustFocusAndSelection(e);
|
---|
870 | }
|
---|
871 |
|
---|
872 | void adjustFocusAndSelection(MouseEvent e) {
|
---|
873 | if (shouldIgnore(e)) {
|
---|
874 | return;
|
---|
875 | }
|
---|
876 |
|
---|
877 | Point p = e.getPoint();
|
---|
878 | int row = table.rowAtPoint(p);
|
---|
879 | int column = table.columnAtPoint(p);
|
---|
880 | // The autoscroller can generate drag events outside the Table's range.
|
---|
881 | if ((column == -1) || (row == -1)) {
|
---|
882 | return;
|
---|
883 | }
|
---|
884 |
|
---|
885 | if (table.editCellAt(row, column, e)) {
|
---|
886 | setDispatchComponent(e);
|
---|
887 | repostEvent(e);
|
---|
888 | }
|
---|
889 | else if (table.isRequestFocusEnabled()) {
|
---|
890 | table.requestFocus();
|
---|
891 | }
|
---|
892 |
|
---|
893 | CellEditor editor = table.getCellEditor();
|
---|
894 | if (editor == null || editor.shouldSelectCell(e)) {
|
---|
895 | boolean adjusting = (e.getID() == MouseEvent.MOUSE_PRESSED) ? true : false;
|
---|
896 | setValueIsAdjusting(adjusting);
|
---|
897 |
|
---|
898 | // Special code for clicking the first column (folder-level metadata)
|
---|
899 | if (column == 0) {
|
---|
900 | selected_metadata = model.getMetadataAtRow(row);
|
---|
901 |
|
---|
902 | // If there is folder-level metadata, switch to the folder it came from
|
---|
903 | File folder_inherited_from = selected_metadata.getFile();
|
---|
904 | if (folder_inherited_from != null) {
|
---|
905 | collection_tree.setSelection(folder_inherited_from);
|
---|
906 | }
|
---|
907 |
|
---|
908 | table.changeSelection(row, 0, e.isControlDown(), e.isShiftDown());
|
---|
909 | }
|
---|
910 | else {
|
---|
911 | table.changeSelection(row, 1, e.isControlDown(), e.isShiftDown());
|
---|
912 | }
|
---|
913 | }
|
---|
914 | }
|
---|
915 |
|
---|
916 | public void mouseReleased(MouseEvent e) {
|
---|
917 | if (selectedOnPress) {
|
---|
918 | if (shouldIgnore(e)) {
|
---|
919 | return;
|
---|
920 | }
|
---|
921 |
|
---|
922 | repostEvent(e);
|
---|
923 | dispatchComponent = null;
|
---|
924 | setValueIsAdjusting(false);
|
---|
925 | } else {
|
---|
926 | adjustFocusAndSelection(e);
|
---|
927 | }
|
---|
928 | }
|
---|
929 |
|
---|
930 |
|
---|
931 | public void mouseEntered(MouseEvent e) {}
|
---|
932 |
|
---|
933 | public void mouseExited(MouseEvent e) {}
|
---|
934 |
|
---|
935 | // The Table's mouse motion listener methods.
|
---|
936 |
|
---|
937 | public void mouseMoved(MouseEvent e) {}
|
---|
938 |
|
---|
939 | public void mouseDragged(MouseEvent e) {
|
---|
940 | if (shouldIgnore(e)) {
|
---|
941 | return;
|
---|
942 | }
|
---|
943 |
|
---|
944 | repostEvent(e);
|
---|
945 |
|
---|
946 | CellEditor editor = table.getCellEditor();
|
---|
947 | if (editor == null || editor.shouldSelectCell(e)) {
|
---|
948 | Point p = e.getPoint();
|
---|
949 | int row = table.rowAtPoint(p);
|
---|
950 | int column = table.columnAtPoint(p);
|
---|
951 | // The autoscroller can generate drag events outside the Table's range.
|
---|
952 | if ((column == -1) || (row == -1)) {
|
---|
953 | return;
|
---|
954 | }
|
---|
955 | table.changeSelection(row, column, false, true);
|
---|
956 | }
|
---|
957 | }
|
---|
958 | }
|
---|
959 |
|
---|
960 |
|
---|
961 | /** A listener for right mouse button clicks over the collection tree. */
|
---|
962 | private class RightButtonListener
|
---|
963 | extends MouseAdapter {
|
---|
964 | /** Called whenever a mouse click occurs (right or left) over a target component.
|
---|
965 | * @param event A <strong>MouseEvent</strong> containing further information about the mouse click action.
|
---|
966 | * @see org.greenstone.gatherer.gui.MetaEditPane.RightButtonMenu
|
---|
967 | */
|
---|
968 | public void mouseClicked(MouseEvent event) {
|
---|
969 | if(SwingUtilities.isRightMouseButton(event)) {
|
---|
970 | new RightButtonMenu(event);
|
---|
971 | }
|
---|
972 | }
|
---|
973 | }
|
---|
974 |
|
---|
975 |
|
---|
976 | /** Provides a popup menu to display when a right mouse button click is detected over the collection tree. */
|
---|
977 | private class RightButtonMenu
|
---|
978 | extends JPopupMenu
|
---|
979 | implements ActionListener {
|
---|
980 |
|
---|
981 | private FileNode node;
|
---|
982 | private JMenuItem collapse_expand_folder_menuitem;
|
---|
983 | private JMenuItem show_metaaudit;
|
---|
984 | private TreePath path;
|
---|
985 |
|
---|
986 | /** Constructor.
|
---|
987 | * @param event The <strong>MouseEvent</strong> that triggered the creation of this menu. Used to determine where the menu will be located.
|
---|
988 | */
|
---|
989 | private RightButtonMenu(MouseEvent event) {
|
---|
990 | super();
|
---|
991 | this.path = collection_tree.getClosestPathForLocation(event.getX(), event.getY());
|
---|
992 | this.node = (FileNode)path.getLastPathComponent();
|
---|
993 | // Creation
|
---|
994 | // Any folder node gets a menu item allowing you to collapse or expand it depending on its current status
|
---|
995 | if(!node.isLeaf()) {
|
---|
996 | // Collapse
|
---|
997 | if(collection_tree.isExpanded(path)) {
|
---|
998 | collapse_expand_folder_menuitem = new JMenuItem(Dictionary.get("Menu.Collapse"), KeyEvent.VK_C);
|
---|
999 | }
|
---|
1000 | // Expand
|
---|
1001 | else {
|
---|
1002 | collapse_expand_folder_menuitem = new JMenuItem(Dictionary.get("Menu.Expand"), KeyEvent.VK_O);
|
---|
1003 | }
|
---|
1004 | collapse_expand_folder_menuitem.addActionListener(this);
|
---|
1005 | add(collapse_expand_folder_menuitem);
|
---|
1006 | }
|
---|
1007 | String[] args = new String[1];
|
---|
1008 | args[0] = collection_tree.getSelectionDetails();
|
---|
1009 | show_metaaudit = new JMenuItem(Dictionary.get("Menu.Metadata_View", args), KeyEvent.VK_V);
|
---|
1010 | show_metaaudit.addActionListener(this);
|
---|
1011 | add(show_metaaudit);
|
---|
1012 | // Display
|
---|
1013 | show(collection_tree, event.getX(), event.getY());
|
---|
1014 | show_metaaudit = null;
|
---|
1015 | }
|
---|
1016 |
|
---|
1017 | /** Called whenever a user clicks on the single menu item, view all assigned metadata.
|
---|
1018 | * @param event An <strong>ActionEvent</strong> containing further information about the action.
|
---|
1019 | * @see org.greenstone.gatherer.Gatherer
|
---|
1020 | * @see org.greenstone.gatherer.gui.GUIManager
|
---|
1021 | */
|
---|
1022 | public void actionPerformed(ActionEvent event) {
|
---|
1023 | Object source = event.getSource();
|
---|
1024 | if(source == show_metaaudit) {
|
---|
1025 | Gatherer.g_man.showMetaAuditBox();
|
---|
1026 | }
|
---|
1027 | else if(source == collapse_expand_folder_menuitem && path != null && node != null && !node.isLeaf()) {
|
---|
1028 | // Collapse
|
---|
1029 | if(collection_tree.isExpanded(path)) {
|
---|
1030 | collection_tree.collapsePath(path);
|
---|
1031 | }
|
---|
1032 | // Expand
|
---|
1033 | else {
|
---|
1034 | collection_tree.expandPath(path);
|
---|
1035 | }
|
---|
1036 | }
|
---|
1037 | }
|
---|
1038 | }
|
---|
1039 |
|
---|
1040 |
|
---|
1041 | /**
|
---|
1042 | * This class is a little bit complex, a little bit subtle, and a little bit odd.
|
---|
1043 | * The strange interaction model is due to the fact that it is very tightly
|
---|
1044 | * coupled to the MetaEditPane.
|
---|
1045 | *
|
---|
1046 | * The interaction is complex because there are three separate controls that the
|
---|
1047 | * user may interact with, each of which can affect the other two. The three
|
---|
1048 | * controls are:
|
---|
1049 | * - The "assigned metadata" table, at the top right of the meta edit pane.
|
---|
1050 | * - The "metadata value" text field, where users can type in new values.
|
---|
1051 | * - The "metadata value tree", which shows other values that have been
|
---|
1052 | * assigned to the selected metadata element.
|
---|
1053 | *
|
---|
1054 | * The interactions are:
|
---|
1055 | * - The "assigned metadata" table
|
---|
1056 | * Users may select one (and only one) row in this table. Selecting a row
|
---|
1057 | * chooses one metadata element. The text field will be set to the current
|
---|
1058 | * value of the metadata element. This value will also be selected in the
|
---|
1059 | * metadata value tree.
|
---|
1060 | *
|
---|
1061 | * - The "metadata value" text field
|
---|
1062 | * If the value the user has typed in this is a prefix of an entry in the
|
---|
1063 | * value tree, this value will be selected in the value tree. In this case,
|
---|
1064 | * pressing "Enter" will complete the value (ie. make the value in the text
|
---|
1065 | * field the same as the value in the tree). This is to allow users to
|
---|
1066 | * quickly and easily assign existing metadata values to new documents.
|
---|
1067 | *
|
---|
1068 | * - The "metadata value tree"
|
---|
1069 | * Selecting a value in the tree will set the text field to this value.
|
---|
1070 | */
|
---|
1071 | private class GValueTree
|
---|
1072 | extends JPanel
|
---|
1073 | implements TreeSelectionListener {
|
---|
1074 | private boolean ignore_tree_selection_event = false;
|
---|
1075 | private boolean manual_text_edit_event;
|
---|
1076 | private CardLayout card_layout;
|
---|
1077 | private String card_showing = null;
|
---|
1078 | /** The metadata element that is currently selected. */
|
---|
1079 | private ElementWrapper selected_metadata_element = null;
|
---|
1080 | private GValueModel vm;
|
---|
1081 | private JTextArea extracted_message;
|
---|
1082 | private JTextField value;
|
---|
1083 | private JTree tree;
|
---|
1084 | /** Stock standard size for labels. */
|
---|
1085 | final private Dimension VALUE_LABEL_SIZE = new Dimension(66, 26);
|
---|
1086 |
|
---|
1087 | static final private String NONE = "None";
|
---|
1088 | static final private String TREE = "Tree";
|
---|
1089 |
|
---|
1090 |
|
---|
1091 | public GValueTree(int width, int height)
|
---|
1092 | {
|
---|
1093 | super();
|
---|
1094 |
|
---|
1095 | setFont(Gatherer.config.getFont("general.font", false));
|
---|
1096 | setPreferredSize(new Dimension(width, height));
|
---|
1097 |
|
---|
1098 | JPanel metadata_pane = new JPanel();
|
---|
1099 |
|
---|
1100 | JPanel value_pane = new JPanel();
|
---|
1101 | JLabel value_label = new JLabel();
|
---|
1102 | value_label.setPreferredSize(VALUE_LABEL_SIZE);
|
---|
1103 | Dictionary.registerText(value_label, "MetaEdit.Value");
|
---|
1104 |
|
---|
1105 | JPanel value_field_pane = new JPanel();
|
---|
1106 | TransformCharacterTextField value_init = new TransformCharacterTextField();
|
---|
1107 | //value_init.replaceCharacter(StaticStrings.PIPE_CHAR, StaticStrings.FORWARDSLASH_CHAR);
|
---|
1108 | value = value_init;
|
---|
1109 | value_init = null;
|
---|
1110 | value.setBackground(Gatherer.config.getColor("coloring.editable_background", false));
|
---|
1111 | value.setForeground(Gatherer.config.getColor("coloring.editable_foreground", false));
|
---|
1112 | value.setPreferredSize(new Dimension(413, 24));
|
---|
1113 | value.getDocument().addDocumentListener(new DocumentListenerImpl());
|
---|
1114 | value.addKeyListener(new MetadataFieldListener());
|
---|
1115 | value.setFocusTraversalKeysEnabled(false);
|
---|
1116 | Dictionary.setTooltip(value, "MetaEdit.Value_Field_Tooltip");
|
---|
1117 |
|
---|
1118 | JPanel button_pane = new JPanel();
|
---|
1119 |
|
---|
1120 | JPanel tree_pane = new JPanel();
|
---|
1121 | JLabel tree_label = new JLabel();
|
---|
1122 | Dictionary.registerText(tree_label, "MetaEdit.Tree");
|
---|
1123 |
|
---|
1124 | tree = new JTree(new GValueModel());
|
---|
1125 | tree.addTreeSelectionListener(this);
|
---|
1126 | tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
---|
1127 | tree.setRootVisible(false);
|
---|
1128 | tree.putClientProperty("JTree.lineStyle", "Angled");
|
---|
1129 |
|
---|
1130 | JPanel extracted_pane = new JPanel();
|
---|
1131 | JPanel extracted_header_pane = new JPanel();
|
---|
1132 | extracted_message = new JTextArea("");
|
---|
1133 | extracted_message.setEditable(false);
|
---|
1134 | extracted_message.setLineWrap(true);
|
---|
1135 | extracted_message.setOpaque(false);
|
---|
1136 | extracted_message.setWrapStyleWord(true);
|
---|
1137 |
|
---|
1138 | card_layout = new CardLayout();
|
---|
1139 |
|
---|
1140 | // Layout
|
---|
1141 | value_field_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,5));
|
---|
1142 | value_field_pane.setLayout(new BorderLayout(0, 0));
|
---|
1143 | value_field_pane.add(value, BorderLayout.CENTER);
|
---|
1144 |
|
---|
1145 | button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
|
---|
1146 | button_pane.setLayout(new GridLayout());
|
---|
1147 | button_pane.add(add);
|
---|
1148 | button_pane.add(update);
|
---|
1149 | button_pane.add(remove);
|
---|
1150 |
|
---|
1151 | value_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
|
---|
1152 | value_pane.setLayout(new BorderLayout());
|
---|
1153 | value_pane.add(value_label, BorderLayout.WEST);
|
---|
1154 | value_pane.add(value_field_pane, BorderLayout.CENTER);
|
---|
1155 | value_pane.add(expand, BorderLayout.EAST);
|
---|
1156 | value_pane.add(button_pane, BorderLayout.SOUTH);
|
---|
1157 |
|
---|
1158 | tree_pane.setLayout(new BorderLayout());
|
---|
1159 | tree_pane.add(tree_label, BorderLayout.NORTH);
|
---|
1160 | tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
|
---|
1161 |
|
---|
1162 | metadata_pane.setLayout(new BorderLayout());
|
---|
1163 | metadata_pane.add(value_pane, BorderLayout.NORTH);
|
---|
1164 | metadata_pane.add(tree_pane, BorderLayout.CENTER);
|
---|
1165 |
|
---|
1166 | extracted_header_pane.setLayout(new BorderLayout());
|
---|
1167 | extracted_header_pane.add(expand_for_extracted, BorderLayout.EAST);
|
---|
1168 |
|
---|
1169 | extracted_pane.setBorder(BorderFactory.createEmptyBorder(0,10,25,0));
|
---|
1170 | extracted_pane.setLayout(new BorderLayout());
|
---|
1171 | extracted_pane.add(extracted_header_pane, BorderLayout.NORTH);
|
---|
1172 | extracted_pane.add(extracted_message, BorderLayout.CENTER);
|
---|
1173 |
|
---|
1174 | this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
|
---|
1175 | this.setLayout(card_layout);
|
---|
1176 | this.add(metadata_pane, TREE);
|
---|
1177 | this.add(extracted_pane, NONE);
|
---|
1178 | card_showing = TREE;
|
---|
1179 | }
|
---|
1180 |
|
---|
1181 |
|
---|
1182 | public GValueModel getModel() {
|
---|
1183 | return (GValueModel) tree.getModel();
|
---|
1184 | }
|
---|
1185 |
|
---|
1186 |
|
---|
1187 | public ElementWrapper getSelectedMetadataElement() {
|
---|
1188 | return selected_metadata_element;
|
---|
1189 | }
|
---|
1190 |
|
---|
1191 |
|
---|
1192 | public String getSelectedValue() {
|
---|
1193 | //return /odec.transform(/odec.transformUnicode(value.getText()), /odec.TEXT_TO_GREENSTONE);
|
---|
1194 | String raw_value = value.getText();
|
---|
1195 | raw_value = Codec.transformUnicode(raw_value);
|
---|
1196 | raw_value = Codec.transform(raw_value, Codec.ENCODE_PATH);
|
---|
1197 | raw_value = Codec.transform(raw_value, Codec.ENCODE_SQUARE_BRACKETS);
|
---|
1198 | return raw_value;
|
---|
1199 | }
|
---|
1200 |
|
---|
1201 |
|
---|
1202 | public void setSelectedMetadataElement(ElementWrapper element)
|
---|
1203 | {
|
---|
1204 | ///ystem.err.println("Setting selected metadata element to: " + element);
|
---|
1205 | if (element == null) {
|
---|
1206 | selected_metadata_element = null;
|
---|
1207 | return;
|
---|
1208 | }
|
---|
1209 |
|
---|
1210 | // Don't bother if the element selected is the same as the current one
|
---|
1211 | if (selected_metadata_element != null && element.equals(selected_metadata_element)) {
|
---|
1212 | return;
|
---|
1213 | }
|
---|
1214 |
|
---|
1215 | if (vm != null) {
|
---|
1216 | // Close any expanded paths in the tree
|
---|
1217 | GValueNode root_node = (GValueNode) vm.getRoot();
|
---|
1218 | for (int i = 0; i < root_node.getChildCount(); i++) {
|
---|
1219 | GValueNode child_node = (GValueNode) root_node.getChildAt(i);
|
---|
1220 | TreePath child_path = new TreePath(child_node.getPath());
|
---|
1221 | if (tree.isExpanded(child_path)) {
|
---|
1222 | tree.collapsePath(child_path);
|
---|
1223 | }
|
---|
1224 | }
|
---|
1225 | }
|
---|
1226 |
|
---|
1227 | // Load the model for the newly selected metadata element
|
---|
1228 | selected_metadata_element = element;
|
---|
1229 | vm = Gatherer.c_man.getCollection().msm.getValueTree(element);
|
---|
1230 | tree.setModel(vm);
|
---|
1231 |
|
---|
1232 | // Special case the extracted metadata set
|
---|
1233 | if (selected_metadata_element.getNamespace().equals(Utility.EXTRACTED_METADATA_NAMESPACE)) {
|
---|
1234 | // Display the panel showing the "you cannot edit this metadata" message
|
---|
1235 | String[] args = new String[1];
|
---|
1236 | args[0] = selected_metadata_element.toString();
|
---|
1237 | Dictionary.registerText(extracted_message, "MetaEdit.AutoMessage", args);
|
---|
1238 | card_layout.show(this, NONE);
|
---|
1239 | card_showing = NONE;
|
---|
1240 | }
|
---|
1241 | else {
|
---|
1242 | // Display the panel for viewing and assigning metadata
|
---|
1243 | card_layout.show(this, TREE);
|
---|
1244 | card_showing = TREE;
|
---|
1245 | }
|
---|
1246 | }
|
---|
1247 |
|
---|
1248 |
|
---|
1249 | /** Handles TreeSelectionEvents. */
|
---|
1250 | public void valueChanged(TreeSelectionEvent event)
|
---|
1251 | {
|
---|
1252 | if (tree.getSelectionCount() != 0 && !ignore_tree_selection_event) {
|
---|
1253 | TreePath path = tree.getSelectionPath();
|
---|
1254 | GValueNode node = (GValueNode) path.getLastPathComponent();
|
---|
1255 | setSelectedValue(node.getFullPath(true));
|
---|
1256 | }
|
---|
1257 | }
|
---|
1258 |
|
---|
1259 |
|
---|
1260 | /** Sets the value in the text field. */
|
---|
1261 | public void setSelectedValue(String val)
|
---|
1262 | {
|
---|
1263 | // Setting the text of the field causes the DocumentListener to be notified, and
|
---|
1264 | // updating the tree is handled there (DocumentListenerImpl::validate())
|
---|
1265 | if (!card_showing.equals(NONE)) {
|
---|
1266 | // Decode val
|
---|
1267 | //System.err.println("Before transforms: " + val);
|
---|
1268 | //val = Codec.transform(val, Codec.DECODE_SQUARE_BRACKETS);
|
---|
1269 | //val = Codec.transform(val, Codec.DECODE_PATH);
|
---|
1270 | //System.err.println("Setting selected value to: " + val);
|
---|
1271 | manual_text_edit_event = val.equals(""); // Set to false unless val == ""
|
---|
1272 | value.setText(val); ///odec.transform(val, /odec.GREENSTONE_TO_TEXT));
|
---|
1273 | value.setCaretPosition(0);
|
---|
1274 | manual_text_edit_event = true;
|
---|
1275 | }
|
---|
1276 | }
|
---|
1277 |
|
---|
1278 |
|
---|
1279 | /** Returns a TreePath for the node most closely matching the metadata value. */
|
---|
1280 | private TreePath getClosestPath(String val)
|
---|
1281 | {
|
---|
1282 | System.err.println("Select closest path to: " + val);
|
---|
1283 | // Start at the root of the tree
|
---|
1284 | GValueNode tree_node = (GValueNode) tree.getModel().getRoot();
|
---|
1285 |
|
---|
1286 | // Hierarchical values are separated using '|'
|
---|
1287 | PatternTokenizer tokenizer = new PatternTokenizer(val, StaticStrings.PIPE_STR);
|
---|
1288 | while (tokenizer.hasMoreTokens()) {
|
---|
1289 | String token = tokenizer.nextToken();
|
---|
1290 |
|
---|
1291 | // All components except the last must match exactly
|
---|
1292 | if (tokenizer.hasMoreTokens()) {
|
---|
1293 | for (int i = 0; i < tree_node.getChildCount(); i++) {
|
---|
1294 | GValueNode child_node = (GValueNode) tree_node.getChildAt(i);
|
---|
1295 | if (child_node.toString(GValueNode.DOM).equals(token)) {
|
---|
1296 | // The matching node has been found, so move onto the next token
|
---|
1297 | tree_node = child_node;
|
---|
1298 | break;
|
---|
1299 | }
|
---|
1300 | }
|
---|
1301 | }
|
---|
1302 |
|
---|
1303 | // The last component may match partially
|
---|
1304 | else {
|
---|
1305 | for (int i = 0; i < tree_node.getChildCount(); i++) {
|
---|
1306 | GValueNode child_node = (GValueNode) tree_node.getChildAt(i);
|
---|
1307 | if (child_node.toString(GValueNode.DOM).startsWith(token)) {
|
---|
1308 | // The closest node has been found, so return its path
|
---|
1309 | return new TreePath(child_node.getPath());
|
---|
1310 | }
|
---|
1311 | }
|
---|
1312 |
|
---|
1313 | // Not even a partial match
|
---|
1314 | return null;
|
---|
1315 | }
|
---|
1316 | }
|
---|
1317 |
|
---|
1318 | // If nothing else, return the path of the root node
|
---|
1319 | return new TreePath(tree_node.getPath());
|
---|
1320 | }
|
---|
1321 |
|
---|
1322 |
|
---|
1323 | /** This function is copied from JTree::setPathToVisible(), and modified so tree rows
|
---|
1324 | are always shown flushed left. Note: doesn't do accessibleContext stuff. */
|
---|
1325 | private void scrollTreePathToVisible(TreePath path)
|
---|
1326 | {
|
---|
1327 | if (path != null) {
|
---|
1328 | tree.makeVisible(path);
|
---|
1329 |
|
---|
1330 | Rectangle bounds = tree.getPathBounds(path);
|
---|
1331 | if (bounds != null) {
|
---|
1332 | bounds.width += bounds.x;
|
---|
1333 | bounds.x = 0;
|
---|
1334 | tree.scrollRectToVisible(bounds);
|
---|
1335 | }
|
---|
1336 | }
|
---|
1337 | }
|
---|
1338 |
|
---|
1339 |
|
---|
1340 | private class MetadataFieldListener
|
---|
1341 | implements KeyListener {
|
---|
1342 |
|
---|
1343 | /** Gives notification of key events on the text field */
|
---|
1344 | public void keyPressed(KeyEvent e) {
|
---|
1345 | if (e.getKeyCode() == KeyEvent.VK_TAB) {
|
---|
1346 | // Tab: Auto-complete
|
---|
1347 | if (tree.getSelectionCount() != 0 && !getSelectedValue().equals("")) {
|
---|
1348 | TreePath path = tree.getSelectionPath();
|
---|
1349 | GValueNode node = (GValueNode) path.getLastPathComponent();
|
---|
1350 | String val = node.getFullPath(true);
|
---|
1351 | System.err.println("Setting value to: " + val);
|
---|
1352 | value.setText(val);
|
---|
1353 | val = null;
|
---|
1354 | }
|
---|
1355 | }
|
---|
1356 | if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
---|
1357 | // Enter: Append the metadata value, if we're allowed to
|
---|
1358 | if (add.isEnabled()) {
|
---|
1359 | add.doClick();
|
---|
1360 | }
|
---|
1361 | }
|
---|
1362 | }
|
---|
1363 |
|
---|
1364 | public void keyReleased(KeyEvent e) { }
|
---|
1365 |
|
---|
1366 | public void keyTyped(KeyEvent e) { }
|
---|
1367 | }
|
---|
1368 |
|
---|
1369 |
|
---|
1370 | private class DocumentListenerImpl
|
---|
1371 | implements DocumentListener {
|
---|
1372 | /** Gives notification that an attribute or set of attributes changed */
|
---|
1373 | public void changedUpdate(DocumentEvent e) {
|
---|
1374 | validate();
|
---|
1375 | }
|
---|
1376 |
|
---|
1377 | /** Gives notification that there was an insert into the document */
|
---|
1378 | public void insertUpdate(DocumentEvent e) {
|
---|
1379 | validate();
|
---|
1380 | }
|
---|
1381 |
|
---|
1382 | /** Gives notification that a portion of the document has been removed */
|
---|
1383 | public void removeUpdate(DocumentEvent e) {
|
---|
1384 | validate();
|
---|
1385 | }
|
---|
1386 |
|
---|
1387 |
|
---|
1388 | /** Ensures that the value text field and value tree are synchronized */
|
---|
1389 | private void validate()
|
---|
1390 | {
|
---|
1391 | String value_text = getSelectedValue(); ///odec.transform(getSelectedValue(), /odec.TEXT_TO_DOM);
|
---|
1392 | ///ystem.err.println("\n(Validate) Value text: " + value_text);
|
---|
1393 |
|
---|
1394 | // Ignore the validate() with empty text that occurs when value.setText() is used
|
---|
1395 | if (!value_text.equals("") || manual_text_edit_event == true) {
|
---|
1396 | TreePath closest_path = getClosestPath(value_text);
|
---|
1397 |
|
---|
1398 | ///ystem.err.println("The closest path is: " + closest_path);
|
---|
1399 |
|
---|
1400 | // Select the new path in the tree
|
---|
1401 | // The tree selection event this causes must be ignored, since it alters value
|
---|
1402 | ignore_tree_selection_event = true;
|
---|
1403 | tree.setSelectionPath(closest_path);
|
---|
1404 | ignore_tree_selection_event = false;
|
---|
1405 |
|
---|
1406 | // If a folder has been specified, make sure it is expanded
|
---|
1407 | if (value_text.endsWith("\\") && !tree.isExpanded(closest_path)) {
|
---|
1408 | tree.expandPath(closest_path);
|
---|
1409 | }
|
---|
1410 |
|
---|
1411 | // Make sure the path is visible on the screen
|
---|
1412 | scrollTreePathToVisible(closest_path);
|
---|
1413 | // One call should be enough, but it isn't in some cases (for some reason)
|
---|
1414 | scrollTreePathToVisible(closest_path);
|
---|
1415 |
|
---|
1416 | // Update the status of the buttons
|
---|
1417 | validateControls();
|
---|
1418 | }
|
---|
1419 | }
|
---|
1420 | }
|
---|
1421 | }
|
---|
1422 | }
|
---|