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

Last change on this file since 4659 was 4652, checked in by mdewsnip, 21 years ago

Changed filter to be white (editable).

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