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

Last change on this file since 4448 was 4426, checked in by mdewsnip, 21 years ago

Removed debug statement.

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