source: trunk/gli/src/org/greenstone/gatherer/gui/MetaEditPane.java@ 4529

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

Moved the functionality of the "replace" and "remove" buttons into their own thread, in the same way as the "append" button. This (hopefully) will mean no more nasty AWT deadlocks.

  • Property svn:keywords set to Author Date Id Revision
File size: 45.6 KB
Line 
1package org.greenstone.gatherer.gui;
2/**
3 *#########################################################################
4 *
5 * A component of the Gatherer application, part of the Greenstone digital
6 * library suite from the New Zealand Digital Library Project at the
7 * University of Waikato, New Zealand.
8 *
9 * <BR><BR>
10 *
11 * Author: John Thompson, Greenstone Digital Library, University of Waikato
12 *
13 * <BR><BR>
14 *
15 * Copyright (C) 1999 New Zealand Digital Library Project
16 *
17 * <BR><BR>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * <BR><BR>
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * <BR><BR>
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
36 *########################################################################
37 */
38import java.awt.*;
39import java.awt.event.*;
40import java.io.*;
41import java.util.*;
42import javax.swing.*;
43import javax.swing.border.*;
44import javax.swing.event.*;
45import javax.swing.table.*;
46import javax.swing.tree.*;
47import org.greenstone.gatherer.Gatherer;
48import org.greenstone.gatherer.file.FileNode;
49import org.greenstone.gatherer.file.FileOpenActionListener;
50import org.greenstone.gatherer.gui.Filter;
51import org.greenstone.gatherer.gui.GComboBox;
52import org.greenstone.gatherer.gui.MetaEditPrompt;
53import org.greenstone.gatherer.gui.WarningDialog;
54import org.greenstone.gatherer.gui.table.GTableModel;
55import org.greenstone.gatherer.gui.table.TableCellRenderer;
56import org.greenstone.gatherer.gui.tree.DragTree;
57import org.greenstone.gatherer.msm.ElementWrapper;
58import org.greenstone.gatherer.msm.Metadata;
59import org.greenstone.gatherer.msm.MSMEvent;
60import org.greenstone.gatherer.msm.MSMListener;
61import org.greenstone.gatherer.util.ArrayTools;
62import org.greenstone.gatherer.util.DragGroup;
63import org.greenstone.gatherer.util.TreeSynchronizer;
64import org.greenstone.gatherer.util.Utility;
65import org.greenstone.gatherer.util.XORToggleButtonGroup;
66import org.greenstone.gatherer.valuetree.GValueTree;
67import org.greenstone.gatherer.valuetree.GValueModel;
68import org.greenstone.gatherer.valuetree.GValueNode;
69import org.w3c.dom.Element;
70/** 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).
71* @author John Thompson, Greenstone Digital Libraries, University of Waikato
72* @version 2.3b
73*/
74public class MetaEditPane
75 extends JPanel
76 implements ActionListener, ListSelectionListener, TreeModelListener, TreeSelectionListener, MSMListener {
77 /** <i>true</i> if the selection has changed since the last time it was asked for, <i>false</i> otherwise. */
78 public boolean has_changed = false;
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 public GValueTree tree = null;
81 /** If only files are selected, then show folder is not available. */
82 private boolean show_folder_enabled = false;
83 /** The layout manager used to display either the GTable (when records are selected) or a placeholder panel with the immortal words 'No Record Selected'. */
84 private CardLayout card_layout = null;
85 /** The layout manager used to display either the GValueTree (when records are selected) or a placeholder panel with the immortal words 'No Record Selected'. */
86 private CardLayout card_layout2 = null;
87 /** Used to dynamically filter the collection tree at user request. Note that this is synchronized with the collection tree filter in the Gather view. */
88 private Filter filter = null;
89 /** The data model behind the GTable, to which we hold a reference for convenient access. */
90 private GTableModel model = null;
91 /** 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. */
92 private FileNode records[] = null;
93 /** Records which of the three GTable views is currently being shown, so we can determine what view to change to next (as they cycle). */
94 //private int current_table_view;
95 /** The mode is determined by the metadata edit action choosen. */
96 private int mode;
97 /** The button, which when clicked, adds metadata to the selected records. */
98 private JButton add;
99 /** The button, which when clicked, removes the selected metadata from the selected records. */
100 private JButton remove;
101 /** The button, which when clicked, changes the currently shown GTable view. */
102 //private JButton table_view;
103 /** The button, which when clicked, updates the selected metadata from the selected records. */
104 private JButton update;
105 /** This button creates an editor dialog to allow for an expanded area to type in text. */
106 private JButton expand;
107 /** The label at the top of the collection tree, which shows the collection name. */
108 private JLabel collection_label;
109 /** The panel in which the metadata value card layout resides. */
110 private JPanel control_pane;
111 /** The panel in which the metadata table card layout resides. */
112 private JPanel table_card_pane;
113 /** The panel the table is added to. */
114 private JPanel table_pane;
115 /** An icon showing the current state of the table build. */
116 //private JProgressBar activity_bar;
117 /** The splitpane dividing the collection tree and the metadata based controls. */
118 private JSplitPane external_split;
119 /** Divides the metadata table and the value tree controls. */
120 private JSplitPane main_split_pane;
121 /** A reference to the metadata table, via its superclass. */
122 private JTable table;
123 /** The label shown at the top of the metadata table detailing the current selection statistics. */
124 private JTextField table_label;
125 /** The button to control whether assigned metadata is shown. */
126 //private JToggleButton assigned_metadata_view;
127 /** The button to control whether unassigned metadata is shown. */
128 //private JToggleButton unassigned_metadata_view;
129 /** A reference to the collection tree. */
130 public DragTree collection_tree;
131 /** The currently selected metadata determined by listening to every second list selection event from the metadata table. */
132 private Metadata selected_metadata;
133 /** Listens for right clicks over the collection tree. */
134 private RightButtonListener right_button_listener = null;
135 /** A temporary storage array from Strings passed to the dictionary to be inserted in the phrase returned. */
136 private String args[];
137 /** Provide synchronization between the collection trees in this view and the collection pane view. */
138 private TreeSynchronizer tree_sync = null;
139 static public Dimension BUTTON_SIZE = new Dimension(190, 25);
140 static private Dimension CONTROL_SIZE = new Dimension(560, 240);
141 static private Dimension LABEL_SIZE = new Dimension(75, 25);
142 static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
143 static private Dimension TABLE_SIZE = new Dimension(560, 25);
144 static private Dimension TREE_SIZE = new Dimension(250, 500);
145 /** An element of the Mode type enumeration, indicating we are adding metadata (ie no metadata rows are selected in the metadata table). */
146 static private int ADD = 0;
147 /** An element of the Mode type enumeration, indicating we are editing existing metadata (ie some row is selected in the metadata table). */
148 static private int EDIT = 1;
149 /** The name of the panel containing the metadata table. */
150 static final private String CARD_ONE = "Card One";
151 /** The name of the panel containing the 'no file' placeholder for the metadata table. */
152 static final private String CARD_TWO = "Card Two";
153 /** The name of the panel containing the 'no metadata' placeholder for the metadata table. */
154 static final private String CARD_ZERO = "Card Zero";
155 /** The name of the panel containing the placeholder for the value tree. */
156 static final private String TOOLS_OFF = "Tools Off";
157 /** The name of the panel containing the value tree. */
158 static final private String TOOLS_ON = "Tools On";
159 /** Constructor.
160 * @param tree_sync The <strong>TreeSynchronizer</strong> to be used on the collection tree.
161 * @see org.greenstone.gatherer.Configuration
162 * @see org.greenstone.gatherer.gui.table.GTable
163 * @see org.greenstone.gatherer.valuetree.GValueTree
164 */
165 public MetaEditPane(TreeSynchronizer tree_sync) {
166 //this.current_table_view = GTableModel.SHOW_FILE;
167 this.tree = null;
168 this.tree_sync = tree_sync;
169
170 add = new JButton(get("MetaEditPrompt.Accumulate"));
171 add.addActionListener(this);
172 add.setEnabled(false);
173 add.setMnemonic(KeyEvent.VK_A);
174 add.setPreferredSize(BUTTON_SIZE);
175
176 update = new JButton(get("MetaEditPrompt.Overwrite"));
177 update.addActionListener(this);
178 update.setEnabled(false);
179 update.setMnemonic(KeyEvent.VK_P);
180 update.setPreferredSize(BUTTON_SIZE);
181
182 remove = new JButton(get("MetaEditPrompt.Remove"));
183 remove.addActionListener(this);
184 remove.setEnabled(false);
185 remove.setMnemonic(KeyEvent.VK_R);
186 remove.setPreferredSize(BUTTON_SIZE);
187
188 expand = new JButton(get("General.Expand"));
189 expand.addActionListener(this);
190 expand.setEnabled(true);
191 expand.setMnemonic(KeyEvent.VK_E);
192 expand.setPreferredSize(new Dimension(25, 25));
193
194 tree = new GValueTree(this, CONTROL_SIZE.width, CONTROL_SIZE.height, add, update, remove, expand);
195 tree.setRootVisible(false);
196 }
197 /** Called whenever an action occurs on one of our registered buttons.
198 * @param event An <strong>ActionEvent</strong> containing information about the event.
199 * @see org.greenstone.gatherer.collection.CollectionManager
200 * @see org.greenstone.gatherer.gui.table.GTableModel
201 * @see org.greenstone.gatherer.msm.ElementWrapper
202 * @see org.greenstone.gatherer.msm.MetadataSetManager
203 * @see org.greenstone.gatherer.msm.MSMEvent
204 * @see org.greenstone.gatherer.valuetree.GValueTree
205 */
206 public void actionPerformed(ActionEvent event) {
207 Object esrc = event.getSource();
208 if(esrc == add) {
209 (new AppendMetadataTask()).start();
210 }
211 else if(esrc == update) {
212 (new UpdateMetadataTask()).start();
213 }
214 else if(esrc == remove) {
215 (new RemoveMetadataTask()).start();
216 }
217 else if(esrc == expand) {
218 EditorDialog ed = new EditorDialog();
219 String temp = ed.display(tree.getSelectedValue());
220 if(temp != null) {
221 tree.setSelectedValue(temp);
222 }
223 }
224 //else if(esrc == assigned_metadata_view || esrc == unassigned_metadata_view) {
225 // model.changeView();
226 //}
227 validateControls();
228 }
229
230 private class AppendMetadataTask
231 extends Thread {
232
233 public AppendMetadataTask() { }
234
235 public void run() {
236 // Check the new metadata is valid
237 ElementWrapper element = tree.getSelectedMetadataElement();
238 String value = tree.getSelectedValue();
239 if(records != null && element != null && value != null) {
240 // Check the records, and if they are folders then display the warning.
241 if(!records[0].isLeaf()) {
242 WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", true);
243 if(dialog.display() == JOptionPane.OK_OPTION) {
244 Gatherer.c_man.getCollection().msm.addMetadata(System.currentTimeMillis(), records, element, value);
245 }
246 dialog.dispose();
247 dialog = null;
248 }
249 else {
250 Gatherer.c_man.getCollection().msm.addMetadata(System.currentTimeMillis(), records, element, value);
251 }
252 GValueNode value_node = ((GValueModel) tree.getModel()).addValue(value);
253 model.setSelectedMetadata(new Metadata(element, value_node));
254 }
255 }
256 }
257
258 private class UpdateMetadataTask
259 extends Thread {
260
261 public UpdateMetadataTask() { }
262
263 public void run() {
264 // You can only update if there is a selected_metadata and
265 // you have valid values in all fields.
266 ElementWrapper element = tree.getSelectedMetadataElement();
267 String value = tree.getSelectedValue();
268 if(selected_metadata != null && records != null && element != null && value != null) {
269 selected_metadata = Gatherer.c_man.getCollection().msm.updateMetadata(System.currentTimeMillis(), selected_metadata, records, value, MetaEditPrompt.OVERWRITE, selected_metadata.isFileLevel());
270 }
271 GValueNode value_node = ((GValueModel) tree.getModel()).addValue(value);
272 model.setSelectedMetadata(new Metadata(element, value_node));
273 }
274 }
275
276 private class RemoveMetadataTask
277 extends Thread {
278
279 public RemoveMetadataTask() { }
280
281 public void run() {
282 if(selected_metadata != null && records != null) {
283 Gatherer.c_man.getCollection().msm.removeMetadata(System.currentTimeMillis(), selected_metadata, records);
284 }
285 }
286 }
287
288 /** 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.
289 * @see org.greenstone.gatherer.gui.table.GTableModel
290 */
291 public void afterDisplay() {
292 external_split.setDividerLocation(0.3);
293 if(table != null) {
294 Dimension table_size = table.getPreferredSize();
295 TableColumnModel column_model = table.getColumnModel();
296
297 TableColumn inherited_column = column_model.getColumn(0);
298 inherited_column.setPreferredWidth(20);
299 inherited_column.setCellRenderer(new TableCellRenderer(model));
300
301 TableColumn element_column = column_model.getColumn(1);
302 element_column.setPreferredWidth(table_size.width / 4);
303 element_column.setCellRenderer(new TableCellRenderer(model));
304
305 TableColumn value_column = column_model.getColumn(2);
306 value_column.setPreferredWidth(((3 * table_size.width) / 4) - 15);
307 value_column.setCellRenderer(new TableCellRenderer(model));
308 }
309 table.doLayout();
310 model.fireTableDataChanged();
311 }
312 /** Called whenever a significant change has occured in the state of the currently loaded collection.
313 * @param ready <i>true</i> if there is a collection currently ready to be editing, <i>false</i> otherwise.
314 * @see org.greenstone.gatherer.collection.CollectionManager
315 * @see org.greenstone.gatherer.collection.CollectionModel
316 * @see org.greenstone.gatherer.tree.GTree
317 * @see org.greenstone.gatherer.util.TreeSynchronizer
318 */
319 public void collectionChanged(boolean ready) {
320 if(ready) {
321 TreeModel collection_model = Gatherer.c_man.getRecordSet();
322 args = new String[1];
323 args[0] = Gatherer.c_man.getCollection().getName();
324 collection_label.setText(get("Collection.Collection", args));
325 // Update label coloring.
326 collection_label.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
327 collection_label.setForeground(Gatherer.config.getColor("coloring.collection_heading_foreground", false));
328 collection_tree.setModel(collection_model);
329 // Update tree coloring.
330 collection_tree.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
331 collection_tree.setForeground(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
332 // Register our msm dependant components (or correctly their models) with msm.
333 if(model != null) {
334 // remove the listener first - in case its already present
335 Gatherer.c_man.getCollection().msm.removeMSMListener(model);
336 Gatherer.c_man.getCollection().msm.addMSMListener(model);
337 }
338 // 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
339 Gatherer.c_man.getCollection().msm.removeMSMListener(this);
340 Gatherer.c_man.getCollection().msm.addMSMListener(this);
341 }
342 else {
343 collection_label.setText(get("Collection.No_Collection"));
344 collection_label.setBackground(Color.lightGray);
345 collection_label.setForeground(Color.black);
346 collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode(get("Collection.No_Collection"))));
347 }
348
349 filter.setEnabled(ready);
350 // Ensure that this collection tree view is synchronized with any others.
351 tree_sync.add(collection_tree);
352 }
353 /** Used to create, connect and layout the components to be shown on this control panel.
354 * @see org.greenstone.gatherer.Gatherer
355 * @see org.greenstone.gatherer.collection.CollectionManager
356 * @see org.greenstone.gatherer.collection.CollectionModel
357 * @see org.greenstone.gatherer.file.FileOpenActionListener
358 * @see org.greenstone.gatherer.gui.Filter
359 * @see org.greenstone.gatherer.gui.GComboBox
360 * @see org.greenstone.gatherer.gui.table.GTableModel
361 * @see org.greenstone.gatherer.tree.GTree
362 * @see org.greenstone.gatherer.tree.GTreeModel
363 * @see org.greenstone.gatherer.valuetree.GValueTree
364 */
365 public void display() {
366 right_button_listener = new RightButtonListener();
367 // Creation
368 JPanel collection_pane = new JPanel(new BorderLayout());
369 collection_pane.setMinimumSize(MINIMUM_SIZE);
370 collection_pane.setPreferredSize(TREE_SIZE);
371
372 external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
373
374 ///atherer.println("\tCreating collection_label");
375 args = new String[1];
376 args[0] = get("Collection.No_Collection");
377 collection_label = new JLabel(get("Collection.Collection"));
378 collection_label.setOpaque(true);
379
380
381 DragGroup group = new DragGroup();
382 TreeModel collection_model = Gatherer.c_man.getRecordSet();
383 if(collection_model != null) {
384 collection_tree = new DragTree("MetaEdit", collection_model, null);
385 collection_model.addTreeModelListener(this);
386 }
387 else {
388 collection_tree = new DragTree("MetaEdit", null);
389 }
390 group.add(collection_tree);
391 collection_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
392 collection_tree.putClientProperty("JTree.lineStyle", "Angled");
393 collection_tree.addMouseListener(Gatherer.g_man.foa_listener);
394 collection_tree.addMouseListener(right_button_listener);
395 collection_tree.addTreeSelectionListener(this);
396 collection_tree.addTreeExpansionListener(Gatherer.g_man.foa_listener);
397 collection_tree.setLargeModel(true);
398 collection_tree.setRootVisible(false);
399
400 JScrollPane collection_scroll = new JScrollPane(collection_tree);
401
402 filter = Gatherer.g_man.getFilter(collection_tree);
403 filter.setBackground(Gatherer.config.getColor("coloring.collection_heading_background", false));
404 // Change the default colours of this filters combobox.
405 GComboBox fcb = filter.getComboBox();
406 fcb.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.collection_tree_background", false));
407 fcb.setTextNonSelectionColor(Gatherer.config.getColor("coloring.collection_tree_foreground", false));
408 fcb.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
409 fcb.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
410 fcb = null;
411
412 // Connection
413
414 // Layout
415 collection_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,3,3,3), BorderFactory.createLoweredBevelBorder()));
416 collection_pane.setMinimumSize(MINIMUM_SIZE);
417 collection_pane.setPreferredSize(new Dimension(Gatherer.g_man.getSize().width / 3, Gatherer.g_man.getSize().height));
418
419 // Collection Pane
420
421 collection_pane.add(collection_label, BorderLayout.NORTH);
422 collection_pane.add(collection_scroll, BorderLayout.CENTER);
423 collection_pane.add(filter, BorderLayout.SOUTH);
424
425 // Main pane
426 //main_pane = new JPanel(new BorderLayout());
427 //main_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
428
429 main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
430 main_split_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
431 main_split_pane.setDividerSize(8);
432
433 // Thumbnail - Currently disabled.
434 // At least until I figure out how to render thumbnails off screen,
435 // which possibly may happen with the discovery of CalpaHTML.
436 /*
437 thumbnail_pane = new JPanel(new BorderLayout());
438
439 gatherer.debug("\tCreating thumbnail_label");
440 thumbnail_label = new JLabel(get("Thumbnail"));
441 thumbnail_label.setHorizontalAlignment(JLabel.CENTER);
442
443 gatherer.debug("\tCreating thumbnail_view_pane");
444 thumbnail_view_pane = new JPanel(new FlowLayout(FlowLayout.CENTER));
445 thumbnail_scroll = new JScrollPane(thumbnail_view_pane);
446
447 thumbnail_pane.add(thumbnail_label, BorderLayout.NORTH);
448 thumbnail_pane.add(thumbnail_scroll, BorderLayout.CENTER);
449 */
450
451 // Metadata Table. In order to achieve what Ian envisions I'm going
452 // to have to do a tricky. The table itself will sit in a stacked
453 // CardLayout. Card zero will be a see-through JPanel with a message
454 // smack bang in the center, something like "No Metadata". If any
455 // page of the table would appear to have no metadata this card will
456 // be swapped to the front.
457
458 table_pane = new JPanel();
459 table_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
460
461 JPanel table_title_pane = new JPanel();
462
463 ///atherer.println("\tCreating metadata_label");
464 table_label = new JTextField(get("No_File"));
465 table_label.setBackground(Color.white);
466 table_label.setEditable(false);
467
468 //activity_bar = new JProgressBar();
469 //activity_bar.setPreferredSize(LABEL_SIZE);
470 //activity_bar.setString(get("Ready"));
471 //activity_bar.setStringPainted(true);
472 //activity_bar.setValue(activity_bar.getMaximum());
473
474 card_layout = new CardLayout();
475
476 table_card_pane = new JPanel();
477
478 JPanel table_pane_zero = new JPanel();
479 table_pane_zero.setOpaque(false);
480
481 JPanel table_pane_two = new JPanel();
482 table_pane_two.setOpaque(false);
483
484 JLabel no_file_message = new JLabel(get("No_File"));
485 no_file_message.setHorizontalAlignment(JLabel.CENTER);
486 no_file_message.setOpaque(false);
487 no_file_message.setVerticalAlignment(JLabel.CENTER);
488
489 JLabel no_metadata_message = new JLabel(get("No_Metadata"));
490 no_metadata_message.setHorizontalAlignment(JLabel.CENTER);
491 no_metadata_message.setOpaque(false);
492 no_metadata_message.setVerticalAlignment(JLabel.CENTER);
493
494 JPanel table_pane_one = new JPanel();
495
496 //JPanel view_pane = new JPanel();
497
498 //JLabel view_label = new JLabel(get("View"));
499 //view_label.setPreferredSize(LABEL_SIZE);
500
501 //JPanel view_button_pane = new JPanel();
502
503 //assigned_metadata_view = new JToggleButton(get("View_Assigned"), Utility.OFF_ICON);
504 //assigned_metadata_view.setSelectedIcon(Utility.ON_ICON);
505 //assigned_metadata_view.setSelected(true);
506 //assigned_metadata_view.addActionListener(this);
507 //unassigned_metadata_view = new JToggleButton(get("View_Unassigned"), Utility.OFF_ICON);
508 //unassigned_metadata_view.setSelectedIcon(Utility.ON_ICON);
509 //unassigned_metadata_view.setSelected(true);
510 //unassigned_metadata_view.addActionListener(this);
511 //XORToggleButtonGroup view_group = new XORToggleButtonGroup();
512 //view_group.add(assigned_metadata_view);
513 //view_group.add(unassigned_metadata_view);
514
515 ///atherer.println("\tCreating metadata_table");
516 table = new JTable();
517 //model = new GTableModel(table, assigned_metadata_view, unassigned_metadata_view, activity_bar);
518 model = new GTableModel(table);
519 table.setModel(model);
520 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
521 table.getSelectionModel().addListSelectionListener(this);
522 table.setColumnSelectionAllowed(false);
523
524 // The default JTable doesn't quite have the desired properties - when a
525 // table cell is clicked on, the table is scrolled so the cell is as
526 // visible as possible. This means that the left-most cells can become
527 // hidden. To fix this, the MouseInputHandler in BasicTableUI is removed
528 // as a MouseListener on the table, and a very slightly altered copy is
529 // added in its place (TableMouseInputHandler).
530 MouseListener[] mls = table.getMouseListeners();
531 for (int i = 0; i < mls.length; i++) {
532 // Remove the instances of MouseInputHandler
533 if (mls[i].toString().startsWith("javax.swing.plaf.basic.BasicTableUI$MouseInputHandler@")) {
534 table.removeMouseListener(mls[i]);
535 }
536 }
537 // Add the slightly-altered mouse listener
538 table.addMouseListener(new TableMouseInputHandler());
539
540 JScrollPane table_scroll = new JScrollPane(table);
541 table_scroll.getViewport().setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
542 table_scroll.setOpaque(true);
543
544 // Control pane
545 ///atherer.println("\tCreating Metadata Controls");
546
547 control_pane = new JPanel();
548 control_pane.setBorder(BorderFactory.createLoweredBevelBorder());
549 control_pane.setMinimumSize(MINIMUM_SIZE);
550 control_pane.setSize(CONTROL_SIZE);
551 control_pane.setPreferredSize(CONTROL_SIZE);
552
553 card_layout2 = new CardLayout();
554
555 JPanel tools_off_pane = new JPanel();
556
557 JLabel tools_off_label = new JLabel(get("No_Metadata_Element"));
558 tools_off_label.setHorizontalAlignment(JLabel.CENTER);
559 tools_off_label.setOpaque(false);
560 tools_off_label.setVerticalAlignment(JLabel.CENTER);
561
562 JPanel tools_on_pane = new JPanel();
563 tools_on_pane.setMinimumSize(MINIMUM_SIZE);
564 tools_on_pane.setSize(TABLE_SIZE);
565 tools_on_pane.setPreferredSize(TABLE_SIZE);
566
567 // Layout.
568
569 // Metaedit controls
570 tools_off_pane.setLayout(new BorderLayout());
571 tools_off_pane.add(tools_off_label, BorderLayout.CENTER);
572
573 tools_on_pane.setLayout(new BorderLayout());
574 tools_on_pane.add(tree, BorderLayout.CENTER);
575
576 control_pane.setLayout(card_layout2);
577 control_pane.add(tools_off_pane, TOOLS_OFF);
578 control_pane.add(tools_on_pane, TOOLS_ON);
579
580 // Table components
581 table_title_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
582 table_title_pane.setLayout(new BorderLayout());
583 //table_title_pane.setLayout(new GridLayout(1,2,5,0));
584 table_title_pane.add(table_label, BorderLayout.CENTER);
585 //table_title_pane.add(activity_bar);
586
587 table_pane_zero.setLayout(new BorderLayout());
588 table_pane_zero.add(no_file_message, BorderLayout.CENTER);
589
590 table_pane_one.setLayout(new BorderLayout());
591 table_pane_one.add(table_scroll, BorderLayout.CENTER);
592
593 table_pane_two.setLayout(new BorderLayout());
594 table_pane_two.add(no_metadata_message, BorderLayout.CENTER);
595
596 table_card_pane.setLayout(card_layout);
597 table_card_pane.add(table_pane_zero, CARD_ZERO);
598 table_card_pane.add(table_pane_one, CARD_ONE);
599 table_card_pane.add(table_pane_two, CARD_TWO);
600 //view_button_pane.setLayout(new GridLayout(1,2,5,0));
601 //view_button_pane.add(assigned_metadata_view);
602 //view_button_pane.add(unassigned_metadata_view);
603
604 //view_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
605 //view_pane.setLayout(new BorderLayout());
606 //view_pane.add(view_label, BorderLayout.WEST);
607 //view_pane.add(view_button_pane, BorderLayout.CENTER);
608
609 table_pane.setLayout(new BorderLayout());
610 table_pane.add(table_title_pane, BorderLayout.NORTH);
611 table_pane.add(table_card_pane, BorderLayout.CENTER);
612 //table_pane.add(view_pane, BorderLayout.SOUTH);
613
614 //main_split_pane.add(control_pane, JSplitPane.TOP);
615 //main_split_pane.add(table_pane, JSplitPane.BOTTOM);
616 main_split_pane.add(table_pane, JSplitPane.TOP);
617 main_split_pane.add(control_pane, JSplitPane.BOTTOM);
618 main_split_pane.setDividerLocation(250);
619
620 external_split.add(collection_pane, JSplitPane.LEFT);//BorderLayout.WEST);
621 //this.add(main_pane, BorderLayout.CENTER);
622 external_split.add(main_split_pane, JSplitPane.RIGHT);//BorderLayout.CENTER);
623
624 this.setLayout(new BorderLayout());
625 this.add(external_split, BorderLayout.CENTER);
626 }
627
628 /** Method that is called whenever an element within a set is changed or modified. - needed for MSMListener
629 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
630 */
631 public void elementChanged(MSMEvent event) {}
632
633 /** Ensures a certain tree path is expanded, visible and selected within the collection tree.
634 * @param path The <strong>TreePath</strong> to make visible.
635 * @see org.greenstone.gatherer.tree.GTree
636 */
637 public void expandPath(TreePath path) {
638 collection_tree.expandPath(path);
639 collection_tree.setSelectionPath(path);
640 }
641 /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
642 * @see org.greenstone.gatherer.tree.GTree
643 */
644 public void gainFocus() {
645 // Use this opportunity to update the table model etc.
646 valueChanged(new TreeSelectionEvent(this, null, false, null, null));
647 // tree.setElementModel(Gatherer.c_man.getCollection().msm.getElementModel());
648 // Update the menubars idea of whats been selected
649 if(collection_tree != null) {
650 if(collection_tree.isSelectionEmpty()) {
651 Gatherer.g_man.menu_bar.setMetaAuditSuffix(null);
652 }
653 else {
654 Gatherer.g_man.menu_bar.setMetaAuditSuffix(collection_tree.getSelectionDetails());
655 }
656 }
657 }
658 /** Used to determine if this class contains a reference to an existing file record that matches the presumtively new record, and if one is found, returns it. Note that there is no need to search the metadata table model, as any file record that exists there must also exist in the records selection.
659 * @param new_record The <strong>FileNode</strong> which may or may not be the first occurance of a certain record.
660 * @return The matching <strong>FileNode</strong> if there is one, or <i>null</i>.
661 */
662 public FileNode getAnyExistingRecord(FileNode new_record) {
663 FileNode match = null;
664 for(int i = 0; records != null && i < records.length && match == null; i++) {
665 if(records[i].equals(new_record)) {
666 match = records[i];
667 }
668 }
669 return match;
670 }
671
672 /** Retrieve the currently selected records.
673 * @return A <strong>FileNode[]</strong> containing the current, and up to date, selection.
674 */
675 public FileNode[] getSelectedNode() {
676 return records;
677 }
678
679 /** Retrieve the currently selected metadata.
680 * @return The <strong>Metadata</strong> in question.
681 */
682 public Metadata getSelectedMetadata() {
683 return selected_metadata;
684 }
685
686 /** Called whenever the metadata value changes in some way, such as the addition of a new value. - needed for MSMListener
687 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
688 */
689 public void metadataChanged(MSMEvent event) {}
690
691 /** 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
692 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
693 */
694 public void setChanged(MSMEvent event){
695 // call the collection tree value changed - we need to do the same stuff here
696 valueChanged((TreeSelectionEvent) null);
697
698 }
699
700 public void setSelection(File file) {
701 collection_tree.setSelection(file);
702 }
703
704 /** Determine the bounds of the selected metadata within the table, used during search and replace actions.
705 * @param metadata The <strong>Metadata</strong> to search for.
706 */
707 public Rectangle setSelectedMetadata(Metadata metadata) {
708 return model.setSelectedMetadata(metadata);
709 }
710
711 public void refreshTrees() {
712 collection_tree.refresh(null); // Refresh entire tree.
713 }
714
715 /* Called when nodes have been modified, but the model hasn't changed.
716 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.
717 */
718 public void treeNodesChanged(TreeModelEvent event) {
719 has_changed = true;
720 }
721
722 /* Called when nodes have been added to the model.
723 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.
724 */
725 public void treeNodesInserted(TreeModelEvent event) {
726 has_changed = true;
727 }
728
729 /* Called when nodes have been removed from the model.
730 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.
731 */
732 public void treeNodesRemoved(TreeModelEvent event) {
733 has_changed = true;
734 }
735
736 /** Called when nodes have been rearranged within the model.
737 * @param event Everything you ever wanted to know about model changes in one tidy <strong>TreeModelEvent</strong> package.
738 */
739 public void treeStructureChanged(TreeModelEvent event) {
740 has_changed = true;
741 }
742 /** Called to ensure that if the tree model has changed in some way, the view has been updated.
743 * @see org.greenstone.gatherer.tree.GTree
744 */
745 public void updateTree() {
746 if(has_changed) {
747 tree.updateUI();
748 has_changed = false;
749 }
750 }
751
752 /** 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.
753 */
754 public void validateControls() {
755 validateControls(true);
756 }
757 public void validateControls(boolean and_tree) {
758 validateMetadataTable();
759 // Validate card_layout_2
760 if (selected_metadata != null) {
761 card_layout2.show(control_pane, TOOLS_ON);
762 }
763 else {
764 card_layout2.show(control_pane, TOOLS_OFF);
765 }
766 if(and_tree) {
767 // Validate the buttons in the lower pane
768 if (selected_metadata == null) {
769 // Nothing selected
770 return;
771 }
772
773 ElementWrapper selected_element = selected_metadata.getElement();
774 if (selected_element.getNamespace() == "") {
775 // Extracted metadata
776 add.setEnabled(false);
777 update.setEnabled(false);
778 remove.setEnabled(false);
779 return;
780 }
781
782 // Does the metadata element have no current value?
783 GValueNode value_node = selected_metadata.getValueNode();
784 if (value_node == null) {
785 // Can only append if something has been entered
786 add.setEnabled((tree.getSelectedValue().length() > 0));
787 // Nothing to replace or remove
788 update.setEnabled(false);
789 remove.setEnabled(false);
790 }
791
792 else {
793 // Check if the text in the value field is the same as the metadata value
794 if (tree.getSelectedValue().equals(value_node.getFullPath())) {
795 // Can't append or replace
796 add.setEnabled(false);
797 update.setEnabled(false);
798 }
799 else {
800 // Can append or replace
801 add.setEnabled(true);
802 update.setEnabled(true);
803 }
804
805 // Can only remove if the metadata is file level
806 remove.setEnabled(selected_metadata.isFileLevel());
807 }
808
809 // tree.validateControls();
810 }
811 }
812
813 /** Validate the metadata table area, and if no metadata is available display the no metadata panel. */
814 public void validateMetadataTable() {
815 // Validate card_layout
816 if (records == null) {
817 card_layout.show(table_card_pane, CARD_ZERO);
818 } else if (model.getRowCount() == 0) {
819 card_layout.show(table_card_pane, CARD_TWO);
820 } else {
821 card_layout.show(table_card_pane, CARD_ONE);
822 }
823 /*
824 if(records != null && model.getRowCount() > 0) {
825 card_layout.show(table_card_pane, CARD_ONE);
826 }
827 else {
828 card_layout.show(table_card_pane, CARD_ZERO);
829 }*/
830 }
831
832 /** Another in a long list of listeners, this one is called whenever the
833 * selection within the JTable changes. When it does we load the new data
834 * and set the selected_metadata if applicable (ie the metadata is non-
835 * empty).
836 * @param event The <strong>ListSelectionEvent</strong> which contains all the information about the event that has been fired.
837 * @see org.greenstone.gatherer.gui.table.GTableModel
838 * @see org.greenstone.gatherer.valuetree.GValueTree
839 */
840 public void valueChanged(ListSelectionEvent event) {
841 // We only want to handle one event per selection, so wait for the value to stabilise
842 ListSelectionModel lsm = table.getSelectionModel();
843 if (lsm.getValueIsAdjusting() == false) {
844 // We have a SINGLE_SELECTION model set so there is at most one
845 // selection. Get the offending row.
846 int selected = table.getSelectedRow();
847 selected_metadata = model.getMetadataAtRow(selected);
848 if(selected_metadata != null) { // Check something is selected.
849 tree.setSelectedMetadataElement(selected_metadata.getElement());
850 GValueNode value_node = selected_metadata.getValueNode();
851 if(value_node != null) {
852 tree.setSelectedValue(value_node.getFullPath());
853 }
854 else {
855 tree.setSelectedValue("");
856 }
857 }
858 validateControls(true);
859 }
860 }
861
862 /** Called whenever the value tree of an metadata element changes in some way, such as the addition of a new value. - needed for MSMListener
863 * @param event A <strong>MSMEvent</strong> containing details of the event that caused this message to be fired.
864 */
865 public void valueChanged(MSMEvent event) {}
866
867 /** This method is called whenever the selection within the collection
868 * tree changes. This causes the table to be rebuilt with a new model
869 * @param event A <strong>TreeSelectionEvent</strong> containing information about the event.
870 * @see org.greenstone.gatherer.Gatherer
871 * @see org.greenstone.gatherer.collection.FileNode
872 * @see org.greenstone.gatherer.gui.GUIManager
873 * @see org.greenstone.gatherer.gui.MenuBar
874 * @see org.greenstone.gatherer.gui.table.GTableModel
875 * @see org.greenstone.gatherer.msm.MetadataSetManager
876 * @see org.greenstone.gatherer.tree.GTree
877 * @see org.greenstone.gatherer.util.ArrayTools
878 */
879 public void valueChanged(TreeSelectionEvent event) {
880 // System.err.println("(MEP) valueChanged(TreeSelectionEvent)...");
881
882 // Don't bother if this isn't the selected view
883 if (Gatherer.g_man.getSelectedView() != this) {
884 return;
885 }
886
887 if(collection_tree.getSelectionCount() > 0) {
888 records = null;
889 TreePath paths[] = collection_tree.getSelectionPaths();
890 for(int i = 0; i < paths.length; i++) {
891 FileNode record = (FileNode) paths[i].getLastPathComponent();
892 records = ArrayTools.add(records, record);
893 }
894
895 table_label.setText(collection_tree.getSelectionDetails());
896
897 // Remove old model from msm
898 Gatherer.c_man.getCollection().msm.removeMSMListener(model);
899 // Create new model (which will add itself to msm).
900 model = new GTableModel(table, records);
901 table.setModel(model);
902 }
903 else {
904 records = null;
905 table_label.setText(get("No_File"));
906 // Remove old model from msm
907 if(Gatherer.c_man.ready()) {
908 Gatherer.c_man.getCollection().msm.removeMSMListener(model);
909 }
910 // Create new model (which will add itself to msm).
911 model = new GTableModel(table);
912 table.setModel(model);
913 }
914
915 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
916 if(table != null) {
917 Dimension table_size = table.getPreferredSize();
918 TableColumnModel column_model = table.getColumnModel();
919
920 TableColumn inherited_column = column_model.getColumn(0);
921 inherited_column.setPreferredWidth(20);
922 inherited_column.setCellRenderer(new TableCellRenderer(model));
923
924 TableColumn element_column = column_model.getColumn(1);
925 element_column.setPreferredWidth(TABLE_SIZE.width / 4);
926 element_column.setCellRenderer(new TableCellRenderer(model));
927
928 TableColumn value_column = column_model.getColumn(2);
929 value_column.setPreferredWidth(((3 * TABLE_SIZE.width) / 4) - 15);
930 value_column.setCellRenderer(new TableCellRenderer(model));
931 }
932 table.doLayout();
933 validateControls();
934 }
935
936
937 /** Retrieve a phrase from the Dictionary based on the given key.
938 * @param key The <strong>String</strong> which maps to the phrase to retrieve.
939 * @return The desired phrase as a <strong>String</strong>, or possibly an error message if no such phrase exists.
940 */
941 private String get(String key) {
942 return get(key, null);
943 }
944 /** Retrieve a phrase from the Dictionary based on the given key and filled in with the given arguments.
945 * @param key The <strong>String</strong> which maps to the phrase to retrieve.
946 * @param args A <strong>String[]</strong> of arguments to be inserted in the phrase.
947 * @return The desired phrase as a <strong>String</strong>, or possibly an error message if no such phrase exists.
948 * @see org.greenstone.gatherer.Dictionary
949 * @see org.greenstone.gatherer.Gatherer
950 */
951 private String get(String key, String args[]) {
952 if(key.indexOf('.') == -1) {
953 key = "MetaEdit." + key;
954 }
955 return Gatherer.dictionary.get(key,args);
956 }
957
958
959 /** 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. */
960 private class TableMouseInputHandler implements MouseInputListener
961 {
962 // Component receiving mouse events during editing.
963 // May not be editorComponent.
964 private Component dispatchComponent;
965 private boolean selectedOnPress;
966
967 // The Table's mouse listener methods.
968
969 public void mouseClicked(MouseEvent e) {}
970
971 private void setDispatchComponent(MouseEvent e) {
972 Component editorComponent = table.getEditorComponent();
973 Point p = e.getPoint();
974 Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
975 dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
976 p2.x, p2.y);
977 }
978
979 private boolean repostEvent(MouseEvent e) {
980 // Check for isEditing() in case another event has
981 // caused the editor to be removed. See bug #4306499.
982 if (dispatchComponent == null || !table.isEditing()) {
983 return false;
984 }
985 MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e, dispatchComponent);
986 dispatchComponent.dispatchEvent(e2);
987 return true;
988 }
989
990 private void setValueIsAdjusting(boolean flag) {
991 table.getSelectionModel().setValueIsAdjusting(flag);
992 table.getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
993 }
994
995 private boolean shouldIgnore(MouseEvent e) {
996 return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && table.isEnabled()));
997 }
998
999 public void mousePressed(MouseEvent e) {
1000 if (e.isConsumed()) {
1001 selectedOnPress = false;
1002 return;
1003 }
1004 selectedOnPress = true;
1005 adjustFocusAndSelection(e);
1006 }
1007
1008 void adjustFocusAndSelection(MouseEvent e) {
1009 if (shouldIgnore(e)) {
1010 return;
1011 }
1012
1013 Point p = e.getPoint();
1014 int row = table.rowAtPoint(p);
1015 int column = table.columnAtPoint(p);
1016 // The autoscroller can generate drag events outside the Table's range.
1017 if ((column == -1) || (row == -1)) {
1018 return;
1019 }
1020
1021 if (table.editCellAt(row, column, e)) {
1022 setDispatchComponent(e);
1023 repostEvent(e);
1024 }
1025 else if (table.isRequestFocusEnabled()) {
1026 table.requestFocus();
1027 }
1028
1029 CellEditor editor = table.getCellEditor();
1030 if (editor == null || editor.shouldSelectCell(e)) {
1031 boolean adjusting = (e.getID() == MouseEvent.MOUSE_PRESSED) ? true : false;
1032 setValueIsAdjusting(adjusting);
1033 if (column == 0) {
1034 table.changeSelection(row, 0, e.isControlDown(), e.isShiftDown());
1035 }
1036 else {
1037 table.changeSelection(row, 1, e.isControlDown(), e.isShiftDown());
1038 }
1039 // table.changeSelection(row, column, e.isControlDown(), e.isShiftDown());
1040 }
1041 }
1042
1043 public void mouseReleased(MouseEvent e) {
1044 if (selectedOnPress) {
1045 if (shouldIgnore(e)) {
1046 return;
1047 }
1048
1049 repostEvent(e);
1050 dispatchComponent = null;
1051 setValueIsAdjusting(false);
1052 } else {
1053 adjustFocusAndSelection(e);
1054 }
1055 }
1056
1057
1058 public void mouseEntered(MouseEvent e) {}
1059
1060 public void mouseExited(MouseEvent e) {}
1061
1062 // The Table's mouse motion listener methods.
1063
1064 public void mouseMoved(MouseEvent e) {}
1065
1066 public void mouseDragged(MouseEvent e) {
1067 if (shouldIgnore(e)) {
1068 return;
1069 }
1070
1071 repostEvent(e);
1072
1073 CellEditor editor = table.getCellEditor();
1074 if (editor == null || editor.shouldSelectCell(e)) {
1075 Point p = e.getPoint();
1076 int row = table.rowAtPoint(p);
1077 int column = table.columnAtPoint(p);
1078 // The autoscroller can generate drag events outside the Table's range.
1079 if ((column == -1) || (row == -1)) {
1080 return;
1081 }
1082 table.changeSelection(row, column, false, true);
1083 }
1084 }
1085 }
1086
1087
1088 /** A listener for right mouse button clicks over the collection tree. */
1089 private class RightButtonListener
1090 extends MouseAdapter {
1091 /** Called whenever a mouse click occurs (right or left) over a target component.
1092 * @param event A <strong>MouseEvent</strong> containing further information about the mouse click action.
1093 * @see org.greenstone.gatherer.gui.MetaEditPane.RightButtonMenu
1094 */
1095 public void mouseClicked(MouseEvent event) {
1096 if(SwingUtilities.isRightMouseButton(event)) {
1097 new RightButtonMenu(event);
1098 }
1099 }
1100 }
1101 /** Provides a popup menu to display when a right mouse button click is detected over the collection tree. */
1102 private class RightButtonMenu
1103 extends JPopupMenu
1104 implements ActionListener {
1105 /** Constructor.
1106 * @param event The <strong>MouseEvent</strong> that triggered the creation of this menu. Used to determine where the menu will be located.
1107 */
1108 public RightButtonMenu(MouseEvent event) {
1109 super();
1110 // Creation
1111 JMenuItem show_metaaudit = new JMenuItem(get("Menu.Metadata_View") + " " + collection_tree.getSelectionDetails(), KeyEvent.VK_V);
1112 show_metaaudit.addActionListener(this);
1113 add(show_metaaudit);
1114 // Display
1115 show(collection_tree, event.getX(), event.getY());
1116 show_metaaudit = null;
1117 args = null;
1118
1119 }
1120 /** Called whenever a user clicks on the single menu item, view all assigned metadata.
1121 * @param event An <strong>ActionEvent</strong> containing further information about the action.
1122 * @see org.greenstone.gatherer.Gatherer
1123 * @see org.greenstone.gatherer.gui.GUIManager
1124 */
1125 public void actionPerformed(ActionEvent event) {
1126 Gatherer.g_man.showMetaAuditBox();
1127 }
1128 }
1129
1130}
Note: See TracBrowser for help on using the repository browser.