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

Last change on this file since 4595 was 4595, checked in by kjdon, 21 years ago

tidied up gatherer.debug - > gatherer.println

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