Ignore:
Timestamp:
2005-05-12T12:24:38+12:00 (19 years ago)
Author:
mdewsnip
Message:

Major changes to the metadata value table and tree in the Enrich pane. The metadata value table now allows direct editing in the table -- hopefully much more obvious to the user. Classes involved have been untangled and are much more re-usable. Special code for replacing metadata has been added, which uses a more direct and efficient method rather than removing then adding metadata as before. And many many minor tidy-ups.

Location:
trunk/gli/src/org/greenstone/gatherer/gui
Files:
2 added
1 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/gli/src/org/greenstone/gatherer/gui/EditorDialog.java

    r8994 r9856  
    6262    private String result = null;
    6363    /** The size of the edit pop-up. */
    64     final static private Dimension SIZE = new Dimension(400,425);
     64    final static private Dimension SIZE = new Dimension(400, 300);
    6565
    6666    /** Constructor */
  • trunk/gli/src/org/greenstone/gatherer/gui/EnrichPane.java

    r9350 r9856  
    11/**
    2  *#########################################################################
    3  *
    4  * A component of the Gatherer application, part of the Greenstone digital
    5  * library suite from the New Zealand Digital Library Project at the
     2 *############################################################################
     3 * A component of the Greenstone Librarian Interface, part of the Greenstone
     4 * digital library suite from the New Zealand Digital Library Project at the
    65 * University of Waikato, New Zealand.
    76 *
    8  * <BR><BR>
     7 * Author: Michael Dewsnip, NZDL Project, University of Waikato, NZ
     8 * Based on code by John Thompson
    99 *
    10  * Author: John Thompson, Greenstone Digital Library, University of Waikato
    11  *
    12  * <BR><BR>
    13  *
    14  * Copyright (C) 1999 New Zealand Digital Library Project
    15  *
    16  * <BR><BR>
     10 * Copyright (C) 2005 New Zealand Digital Library Project
    1711 *
    1812 * This program is free software; you can redistribute it and/or modify
     
    2115 * (at your option) any later version.
    2216 *
    23  * <BR><BR>
    24  *
    2517 * This program is distributed in the hope that it will be useful,
    2618 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     
    2820 * GNU General Public License for more details.
    2921 *
    30  * <BR><BR>
    31  *
    3222 * You should have received a copy of the GNU General Public License
    3323 * along with this program; if not, write to the Free Software
    3424 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    35  *########################################################################
     25 *############################################################################
    3626 */
     27
    3728package org.greenstone.gatherer.gui;
    3829
     
    4435import javax.swing.*;
    4536import javax.swing.event.*;
    46 import javax.swing.table.*;
     37import javax.swing.text.*;
    4738import javax.swing.tree.*;
    4839import org.greenstone.gatherer.Configuration;
     
    5647import org.greenstone.gatherer.metadata.MetadataValue;
    5748import org.greenstone.gatherer.metadata.MetadataValueTableEntry;
    58 import org.greenstone.gatherer.metadata.MetadataValueTableModel;
    59 import org.greenstone.gatherer.metadata.MetadataValueTreeModel;
    6049import org.greenstone.gatherer.metadata.MetadataValueTreeNode;
    61 import org.greenstone.gatherer.metadata.MetadataXMLFileManager;
    6250import org.greenstone.gatherer.util.DragGroup;
    63 import org.greenstone.gatherer.util.PatternTokenizer;
    6451import org.greenstone.gatherer.util.TreeSynchronizer;
    65 import org.greenstone.gatherer.util.Utility;
    66 import org.greenstone.gatherer.util.XMLTools;
    6752
    6853
     
    7156public class EnrichPane
    7257    extends JPanel
    73     implements ActionListener, TreeSelectionListener
     58    implements TreeSelectionListener
    7459{
    75     static private Dimension CONTROL_SIZE = new Dimension(560, 240);
    7660    static private Dimension MINIMUM_SIZE = new Dimension(100, 100);
    77     static private Dimension TABLE_SIZE = new Dimension(560, 25);
    78     static private Dimension TREE_SIZE = new Dimension(250, 500);
    79     static private int MINIMUM_TABLE_HEADER_SIZE = 15;
    80 
    81     private MetadataValueTable metadata_value_table = null;
    82     /** The layout manager used to display either the MetadataValueTable (when files are selected) or a placeholder panel */
    83     private CardLayout metadata_value_table_card_layout = null;
    84     /** The name of the panel containing the metadata table */
    85     static final private String TABLE_CARD = "";
    86     /** The name of the panel containing the "no files selected" placeholder for the metadata table */
    87     static final private String TABLE_CARD_NO_FILES_SELECTED = "No files selected";
    88     /** The name of the panel containing the "no metadata available" placeholder for the metadata table */
    89     static final private String TABLE_CARD_NO_METADATA_AVAILABLE = "No metadata available";
    90 
    91     private JTextField metadata_value_text_field = null;
    92     /** The MetadataValueTree graphically shows the available metadata that has been previously assigned, and provides controls for adding, updating and removing metadata, depending on the user's selections in the other important components. */
    93     private MetadataValueTree metadata_value_tree = null;
    94     /** The layout manager used to display either the MetadataValueTree (when metadata is selected) or a placeholder panel */
    95     private CardLayout metadata_value_tree_card_layout = null;
    96     /** The name of the panel containing the metadata value tree */
    97     static final private String TREE_CARD = "";
    98     /** The name of the panel containing the "no metadata element selected" placeholder for the metadata table */
    99     static final private String TREE_CARD_NO_METADATA_ELEMENT_SELECTED  = "No metadata element selected";
    100 
    101     /** Used to dynamically filter the collection tree at user request. Note that this is synchronized with the collection tree filter in the Gather view. */
    102     private Filter collection_filter = null;
     61    static private Dimension COLLECTION_TREE_SIZE = new Dimension(250, 500);
     62
     63    /** The collection tree. */
     64    private CollectionTree collection_tree = null;
    10365    /** The currently reported selection. */
    10466    private CollectionTreeNode[] file_nodes = null;
    105     /** The button, which when clicked, adds metadata to the selected records. */
    106     private JButton add;
    107     /** The button, which when clicked, removes the selected metadata from the selected records. */
    108     private JButton remove;
    109     /** The button, which when clicked, updates the selected metadata from the selected records. */
    110     private JButton replace;
    111     /** This button creates an editor dialog to allow for an expanded area to type in text. */
    112     private JButton expand;
    113     private JButton expand_for_extracted;
     67    /** Used to dynamically filter the collection tree. Synchronized with the same filter in the Gather view. */
     68    private Filter collection_filter = null;
    11469    /** The label at the top of the collection tree, which shows the collection name. */
    11570    private JLabel collection_label;
    116     /** The panel in which the metadata value card layout resides. */
    117     private JPanel control_pane;
    118     /** The panel in which the metadata table card layout resides. */
    119     private JPanel table_card_pane;
    120     /** The splitpane dividing the collection tree and the metadata based controls. */
     71    /** The splitpane dividing the collection tree and the metadata editing controls. */
    12172    private JSplitPane external_split;
    122     /** The label shown at the top of the metadata table detailing the current selection statistics. */
    123     private JTextField table_label;
    124     /** A reference to the collection tree. */
    125     private CollectionTree collection_tree;
     73    /** The metadata value table shows the metadata values that are currently assigned to a file. */
     74    private MetadataValueTablePane metadata_value_table_pane = null;
     75    /** The metadata value tree shows the metadata values that are currently assigned to a metadata element. */
     76    private MetadataValueTreePane metadata_value_tree_pane = null;
    12677    /** Provide synchronization between the collection trees in this view and the collection pane view. */
    12778    private TreeSynchronizer collection_tree_sync = null;
    128     private boolean metadata_edit_event = false;
    12979
    13080
     
    13686    this.collection_tree_sync = collection_tree_sync;
    13787
    138     add = new GLIButton();
    139     add.addActionListener(this);
    140     add.setEnabled(false);
    141     add.setMnemonic(KeyEvent.VK_A);
    142     add.setPreferredSize(Utility.MINIMUM_BUTTON_SIZE);
    143     Dictionary.registerBoth(add, "EnrichPane.Accumulate", "EnrichPane.Accumulate_Tooltip");
    144 
    145     replace = new GLIButton();
    146     replace.addActionListener(this);
    147     replace.setEnabled(false);
    148     replace.setMnemonic(KeyEvent.VK_P);
    149     replace.setPreferredSize(Utility.MINIMUM_BUTTON_SIZE);
    150     Dictionary.registerBoth(replace, "EnrichPane.Overwrite", "EnrichPane.Overwrite_Tooltip");
    151 
    152     remove = new GLIButton();
    153     remove.addActionListener(this);
    154     remove.setEnabled(false);
    155     remove.setMnemonic(KeyEvent.VK_R);
    156     remove.setPreferredSize(Utility.MINIMUM_BUTTON_SIZE);
    157     Dictionary.registerBoth(remove, "EnrichPane.Remove", "EnrichPane.Remove_Tooltip");
    158 
    159     expand = new GLIButton();
    160     expand.addActionListener(this);
    161     expand.setEnabled(true);
    162     expand.setMnemonic(KeyEvent.VK_E);
    163     expand.setPreferredSize(new Dimension(25, 25));
    164     Dictionary.registerBoth(expand, "EnrichPane.Expand", "EnrichPane.Expand_Tooltip");
    165 
    166     expand_for_extracted = new GLIButton();
    167     expand_for_extracted.addActionListener(this);
    168     expand_for_extracted.setEnabled(true);
    169     expand_for_extracted.setMnemonic(KeyEvent.VK_E);
    170     expand_for_extracted.setPreferredSize(new Dimension(25, 25));
    171     Dictionary.registerBoth(expand_for_extracted, "EnrichPane.Expand", "EnrichPane.Expand_Tooltip");
    172 
    173     metadata_value_text_field = new JTextField();
    174     metadata_value_table = new MetadataValueTable();
    175     metadata_value_tree = new MetadataValueTree(CONTROL_SIZE.width, CONTROL_SIZE.height);
    176     }
    177 
    178 
    179     /** Called whenever an action occurs on one of our registered buttons.
    180      * @param event An <strong>ActionEvent</strong> containing information about the event.
    181      */
    182     public void actionPerformed(ActionEvent event)
    183     {
    184     Object esrc = event.getSource();
    185 
    186     // One of the metadata editing buttons was pressed
    187     if (esrc == add || esrc == replace || esrc == remove) {
    188         // Double-check that some files and a metadata element have been selected
    189         MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
    190         if (file_nodes == null || selected_metadata_value_table_entry == null) {
    191         return;
    192         }
    193 
    194         // If we're adding metadata to folders display the warning
    195         if (esrc == add && !file_nodes[0].isLeaf()) {
    196         WarningDialog dialog = new WarningDialog("warning.DirectoryLevelMetadata", "DirectoryLevelMetadata.Title", "DirectoryLevelMetadata.Message", null, true);
    197         int dialog_result = dialog.display();
    198         dialog.dispose();
    199 
    200         if (dialog_result != JOptionPane.OK_OPTION) {
    201             return;
    202         }
    203         }
    204 
    205         // Lock the interface so nothing can be changed while the metadata edit is being processed
    206         Gatherer.g_man.wait(true);
    207 
    208         // Add button pressed
    209         if (esrc == add) {
    210         MetadataElement metadata_element = metadata_value_table.getSelectedMetadataElement();
    211         MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue(metadata_value_tree.getSelectedValue());
    212         MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
    213         metadata_value.setIsAccumulatingMetadata(true);
    214         (new AppendMetadataTask(metadata_value)).run();
    215         }
    216 
    217         // Replace button pressed
    218         else if (esrc == replace) {
    219         MetadataElement metadata_element = metadata_value_table.getSelectedMetadataElement();
    220         MetadataValueTreeNode metadata_value_tree_node = metadata_element.addMetadataValue(metadata_value_tree.getSelectedValue());
    221         MetadataValue metadata_value = new MetadataValue(metadata_element, metadata_value_tree_node);
    222         metadata_value.setIsAccumulatingMetadata(!metadata_value_table.getSelectedMetadataValueTableEntry().isInheritedMetadata());
    223         (new ReplaceMetadataTask(metadata_value, selected_metadata_value_table_entry)).run();
    224         }
    225 
    226         // Remove button pressed
    227         else if (esrc == remove) {
    228         (new RemoveMetadataTask(selected_metadata_value_table_entry)).run();
    229         }
    230 
    231         // Note that the collection's metadata has been changed
    232         Gatherer.c_man.getCollection().setMetadataChanged(true);
    233     }
    234 
    235     // Expand button pressed
    236     else if (esrc == expand) {
    237         EditorDialog ed = new EditorDialog();
    238         String new_metadata_value = ed.display(metadata_value_tree.getSelectedValue());
    239         if (new_metadata_value != null) {
    240         metadata_value_tree.setSelectedValue(new_metadata_value);
    241         }
    242     }
    243 
    244     // Expand button for extracted metadata pressed
    245     else if (esrc == expand_for_extracted) {
    246         EditorDialog ed = new EditorDialog();
    247         ed.setEditable(false);
    248         ed.display(metadata_value_table.getSelectedMetadataValueTableEntry().getFullValue());
    249     }
     88    // Create the metadata value tree pane
     89    metadata_value_tree_pane = new MetadataValueTreePane();
     90    metadata_value_tree_pane.addMetadataValueTreeSelectionListener(new MetadataValueTreeSelectionListener());
     91
     92    // Create metadata value table pane
     93    metadata_value_table_pane = new MetadataValueTablePane();
     94    metadata_value_table_pane.addMetadataValueTableListSelectionListener(new MetadataValueTableListSelectionListener());
     95    metadata_value_table_pane.addMetadataValueTableMouseListener(new MetadataValueTableMouseListener());
     96    metadata_value_table_pane.addMetadataValueTextFieldDocumentListener(new MetadataValueTextFieldDocumentListener());
     97    metadata_value_table_pane.addMetadataValueTextFieldKeyListener(new MetadataValueTextFieldKeyListener());
    25098    }
    25199
     
    269117    JPanel collection_pane = new JPanel(new BorderLayout());
    270118    collection_pane.setMinimumSize(MINIMUM_SIZE);
    271     collection_pane.setPreferredSize(TREE_SIZE);
     119    collection_pane.setPreferredSize(COLLECTION_TREE_SIZE);
    272120
    273121    external_split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
     
    311159    collection_pane.add(collection_filter, BorderLayout.SOUTH);
    312160
    313     JSplitPane main_split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    314     main_split_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    315     main_split_pane.setDividerSize(8);
    316 
    317     // Metadata Table. In order to achieve what Ian envisions I'm going
    318     // to have to do a tricky. The table itself will sit in a stacked
    319     // CardLayout. Card zero will be a see-through JPanel with a message
    320     // smack bang in the center, something like "No Metadata". If any
    321     // page of the table would appear to have no metadata this card will
    322     // be swapped to the front.
    323 
    324     JPanel table_pane = new JPanel();
    325     table_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
    326 
    327     JPanel table_title_pane = new JPanel();
    328 
    329     table_label = new JTextField();
    330     table_label.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
    331     table_label.setEditable(false);
    332     Dictionary.registerText(table_label, "EnrichPane.No_File");
    333 
    334     table_card_pane = new JPanel();
    335 
    336     JPanel table_pane_zero = new JPanel();
    337     table_pane_zero.setOpaque(false);
    338 
    339     JPanel table_pane_two = new JPanel();
    340     table_pane_two.setOpaque(false);
    341 
    342     JLabel no_file_message = new JLabel();
    343     no_file_message.setHorizontalAlignment(JLabel.CENTER);
    344     no_file_message.setOpaque(false);
    345     no_file_message.setVerticalAlignment(JLabel.CENTER);
    346     Dictionary.registerText(no_file_message, "EnrichPane.No_File");
    347 
    348     JLabel no_metadata_message = new JLabel();
    349     no_metadata_message.setHorizontalAlignment(JLabel.CENTER);
    350     no_metadata_message.setOpaque(false);
    351     no_metadata_message.setVerticalAlignment(JLabel.CENTER);
    352     Dictionary.registerText(no_metadata_message, "EnrichPane.No_Metadata");
    353 
    354     JPanel table_pane_one = new JPanel();
    355 
    356     JScrollPane table_scroll = new JScrollPane(metadata_value_table);
    357     table_scroll.getViewport().setBackground(Configuration.getColor("coloring.collection_tree_background", false));
    358     table_scroll.setOpaque(true);
    359 
    360     // Create the cards for the metadata value table
    361     metadata_value_table_card_layout = new CardLayout();
    362 
    363     table_pane_zero.setLayout(new BorderLayout());
    364     table_pane_zero.add(no_file_message, BorderLayout.CENTER);
    365 
    366     table_pane_one.setLayout(new BorderLayout());
    367     table_pane_one.add(table_scroll, BorderLayout.CENTER);
    368 
    369     table_pane_two.setLayout(new BorderLayout());
    370     table_pane_two.add(no_metadata_message, BorderLayout.CENTER);
    371 
    372     table_card_pane.setLayout(metadata_value_table_card_layout);
    373     table_card_pane.add(table_pane_zero, TABLE_CARD_NO_FILES_SELECTED);
    374     table_card_pane.add(table_pane_one, TABLE_CARD);
    375     table_card_pane.add(table_pane_two, TABLE_CARD_NO_METADATA_AVAILABLE);
    376 
    377     // Create the cards for the metadata value tree
    378     metadata_value_tree_card_layout = new CardLayout();
    379 
    380     // Control pane
    381     control_pane = new JPanel();
    382     control_pane.setBorder(BorderFactory.createLoweredBevelBorder());
    383     control_pane.setMinimumSize(MINIMUM_SIZE);
    384     control_pane.setSize(CONTROL_SIZE);
    385     control_pane.setPreferredSize(CONTROL_SIZE);
    386 
    387     JPanel tools_off_pane = new JPanel();
    388 
    389     JLabel tools_off_label = new JLabel();
    390     tools_off_label.setHorizontalAlignment(JLabel.CENTER);
    391     tools_off_label.setOpaque(false);
    392     tools_off_label.setVerticalAlignment(JLabel.CENTER);
    393     Dictionary.registerText(tools_off_label, "EnrichPane.No_Metadata_Element");
    394 
    395     JPanel tools_on_pane = new JPanel();
    396     tools_on_pane.setMinimumSize(MINIMUM_SIZE);
    397     tools_on_pane.setSize(TABLE_SIZE);
    398     tools_on_pane.setPreferredSize(TABLE_SIZE);
    399 
    400     // Layout.
    401 
    402     // Metaedit controls
    403     tools_off_pane.setLayout(new BorderLayout());
    404     tools_off_pane.add(tools_off_label, BorderLayout.CENTER);
    405 
    406     tools_on_pane.setLayout(new BorderLayout());
    407     tools_on_pane.add(metadata_value_tree, BorderLayout.CENTER);
    408 
    409     control_pane.setLayout(metadata_value_tree_card_layout);
    410     control_pane.add(tools_off_pane, TREE_CARD_NO_METADATA_ELEMENT_SELECTED);
    411     control_pane.add(tools_on_pane, TREE_CARD);
    412 
    413     // Table components
    414     table_title_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    415     table_title_pane.setLayout(new BorderLayout());
    416     table_title_pane.add(table_label, BorderLayout.CENTER);
    417 
    418     table_pane.setLayout(new BorderLayout());
    419     table_pane.add(table_title_pane, BorderLayout.NORTH);
    420     table_pane.add(table_card_pane, BorderLayout.CENTER);
    421 
    422     main_split_pane.add(table_pane, JSplitPane.TOP);
    423     main_split_pane.add(control_pane, JSplitPane.BOTTOM);
    424     main_split_pane.setDividerLocation(250);
     161    JSplitPane metadata_editing_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     162    metadata_editing_pane.setBorder(BorderFactory.createEmptyBorder(0,5,5,5));
     163    metadata_editing_pane.setDividerSize(8);
     164
     165    metadata_editing_pane.add(metadata_value_table_pane, JSplitPane.TOP);
     166    metadata_editing_pane.add(metadata_value_tree_pane, JSplitPane.BOTTOM);
     167    metadata_editing_pane.setDividerLocation(250);
    425168
    426169    external_split.add(collection_pane, JSplitPane.LEFT);
    427     external_split.add(main_split_pane, JSplitPane.RIGHT);
     170    external_split.add(metadata_editing_pane, JSplitPane.RIGHT);
    428171
    429172    this.setLayout(new BorderLayout());
     
    432175
    433176
    434     /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
     177    /** Called to inform this pane that it has just gained focus as an effect of the user clicking on its tab
    435178     */
    436179    public void gainFocus()
     
    450193
    451194    // Force all of the controls to be updated
    452     valueChanged(new TreeSelectionEvent(this, null, false, null, null));
     195    valueChanged(null);
    453196    }
    454197
     
    502245    // Force the metadata table to be rebuilt (for switching extracted metadata on or off)
    503246    if (refresh_reason == Gatherer.PREFERENCES_CHANGED) {
    504         valueChanged(null);
    505     }
    506     }
    507 
    508 
    509     /** This method is called whenever the selection within the collection
    510      * tree changes. This causes the table to be rebuilt with a new model.
    511      */
     247        metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
     248    }
     249    }
     250
     251
     252    /** Called whenever the collection tree selection changes. This causes the metadata value table to be rebuilt. */
    512253    public void valueChanged(TreeSelectionEvent event)
    513254    {
     
    515256    if (collection_tree.getSelectionCount() == 0) {
    516257        file_nodes = null;
    517 
    518         // Update the label to show nothing is selected in the collection tree
    519         Dictionary.registerText(table_label, "EnrichPane.No_File");
    520 
    521         // Display the "no file selected" card
    522         metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD_NO_FILES_SELECTED);
    523258    }
    524259
     
    530265        file_nodes[i] = (CollectionTreeNode) paths[i].getLastPathComponent();
    531266        }
    532 
    533         // Update the label to show what is selected in the collection tree
    534         table_label.setText(collection_tree.getSelectionDetails());
    535267    }
    536268
    537269    // Update the metadata value table (and consequently, the metadata value tree)
    538     MetadataValue previously_selected_metadata_value = metadata_value_table.getSelectedMetadataValueTableEntry();
    539     metadata_value_table.rebuildAndSelectRowWithValueClosestTo(previously_selected_metadata_value);
    540     }
    541 
    542 
    543     private class AppendMetadataTask
    544     extends Thread
    545     {
    546     private MetadataValue metadata_value = null;
    547 
    548     private AppendMetadataTask(MetadataValue metadata_value)
    549     {
    550         this.metadata_value = metadata_value;
    551     }
    552 
    553     public void run()
     270    metadata_value_table_pane.stopEditingAndRebuild(file_nodes);
     271    }
     272
     273
     274    private class MetadataValueTableListSelectionListener
     275    implements ListSelectionListener
     276    {
     277    public void valueChanged(ListSelectionEvent list_selection_event)
     278    {
     279        // We only want to handle one event per selection, so wait for the value to stabilise
     280        if (list_selection_event.getValueIsAdjusting()) {
     281        return;
     282        }
     283
     284        // Update the metadata value tree for the current table selection
     285        metadata_value_tree_pane.rebuild(metadata_value_table_pane.getSelectedMetadataValueTableEntry());
     286    }
     287    }
     288
     289
     290    private class MetadataValueTableMouseListener
     291    extends MouseAdapter
     292    {
     293    public void mouseClicked(MouseEvent mouse_event)
     294    {
     295        // We're only interested in clicks on the inherited column
     296        if (metadata_value_table_pane.isMouseEventForInheritedMetadataValueTableColumn(mouse_event) == false) {
     297        return;
     298        }
     299
     300        // If the selected metadata is inherited, switch to the folder it came from
     301        MetadataValueTableEntry selected_metadata_value_table_entry = metadata_value_table_pane.getSelectedMetadataValueTableEntry();
     302        if (selected_metadata_value_table_entry.isInheritedMetadata()) {
     303        collection_tree.setSelection(selected_metadata_value_table_entry.getFolderMetadataInheritedFrom());
     304        }
     305    }
     306    }
     307
     308
     309    private class MetadataValueTextFieldDocumentListener
     310    implements DocumentListener
     311    {
     312    /** Gives notification that an attribute or set of attributes changed */
     313    public void changedUpdate(DocumentEvent document_event) {
     314        validate(document_event);
     315    }
     316
     317    /** Gives notification that there was an insert into the document */
     318    public void insertUpdate(DocumentEvent document_event) {
     319        validate(document_event);
     320    }
     321
     322    /** Gives notification that a portion of the document has been removed */
     323    public void removeUpdate(DocumentEvent document_event) {
     324        validate(document_event);
     325    }
     326
     327
     328    /** Ensures that the value tree is updated in response to changes in the value text field */
     329    private void validate(DocumentEvent document_event)
    554330    {
    555331        try {
    556         // Edit metadata.xml files to add the metadata
    557         MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
    558 
    559         // Update the metadata value table and tree
    560         metadata_edit_event = true;
    561         metadata_value_table.rebuildAndSelectRowWithValueClosestTo(metadata_value);
    562         metadata_edit_event = false;
     332        Document document = document_event.getDocument();
     333        String metadata_value_string = document.getText(0, document.getLength());
     334        metadata_value_tree_pane.selectBestPathForMetadataValue(metadata_value_string);
    563335        }
    564336        catch (Exception exception) {
    565         // We need to catch any exceptions here so the interface is unlocked below
    566337        DebugStream.printStackTrace(exception);
    567338        }
    568 
    569         // Operation finished, so turn busy cursor off and unlock interface
    570         Gatherer.g_man.wait(false);
    571     }
    572     }
    573 
    574 
    575     private class ReplaceMetadataTask
    576     extends Thread
    577     {
    578     private MetadataValue metadata_value = null;
    579     private MetadataValueTableEntry selected_metadata_value_table_entry = null;
    580 
    581     private ReplaceMetadataTask(MetadataValue metadata_value, MetadataValueTableEntry selected_metadata_value_table_entry)
    582     {
    583         this.metadata_value = metadata_value;
    584         this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
    585     }
    586 
    587     public void run()
    588     {
    589         try {
    590         // Edit metadata.xml files to replace the metadata
    591         MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
    592         MetadataXMLFileManager.addMetadata(file_nodes, metadata_value);
    593 
    594         // Update the metadata value table and tree
    595         metadata_edit_event = true;
    596         metadata_value_table.rebuildAndSelectRowWithValueClosestTo(metadata_value);
    597         metadata_edit_event = false;
    598         }
    599         catch (Exception exception) {
    600         // We need to catch any exceptions here so the interface is unlocked below
    601         DebugStream.printStackTrace(exception);
    602         }
    603 
    604         // Operation finished, so turn busy cursor off and unlock interface
    605         Gatherer.g_man.wait(false);
    606     }
    607     }
    608 
    609 
    610     private class RemoveMetadataTask
    611     extends Thread
    612     {
    613     private MetadataValueTableEntry selected_metadata_value_table_entry = null;
    614 
    615     private RemoveMetadataTask(MetadataValueTableEntry selected_metadata_value_table_entry)
    616     {
    617         this.selected_metadata_value_table_entry = selected_metadata_value_table_entry;
    618     }
    619 
    620     public void run()
    621     {
    622         try {
    623         // Edit metadata.xml files to remove the metadata
    624         MetadataXMLFileManager.removeMetadata(file_nodes, selected_metadata_value_table_entry);
    625 
    626         // Update the metadata value table and tree
    627         metadata_edit_event = true;
    628         metadata_value_table.rebuildAndSelectRowWithValueClosestTo(selected_metadata_value_table_entry);
    629         metadata_edit_event = false;
    630         }
    631         catch (Exception exception) {
    632         // We need to catch any exceptions here so the interface is unlocked below
    633         DebugStream.printStackTrace(exception);
    634         }
    635 
    636         // Operation finished, so turn busy cursor off and unlock interface
    637         Gatherer.g_man.wait(false);
    638     }
    639     }
    640 
    641 
    642     private class MetadataValueTable
    643     extends JTable
    644     {
    645     private MetadataValueTableModel metadata_value_table_model = null;
    646     /** The currently selected metadata value table entry */
    647     private MetadataValueTableEntry selected_metadata_value_table_entry = null;
    648 
    649 
    650     public MetadataValueTable()
    651     {
    652         metadata_value_table_model = new MetadataValueTableModel();
    653         setModel(metadata_value_table_model);
    654 
    655         // We allow only one row to be selected at a time
    656         setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    657         getSelectionModel().addListSelectionListener(new MetadataValueTableListSelectionListener());
    658 
    659         // The default JTable doesn't quite have the desired properties - when a
    660         // table cell is clicked on, the table is scrolled so the cell is as
    661         // visible as possible. This means that the left-most cells can become
    662         // hidden. To fix this, the MouseInputHandler in BasicTableUI is removed
    663         // as a MouseListener on the table, and a very slightly altered copy is
    664         // added in its place (TableMouseInputHandler).
    665         MouseListener[] mls = getMouseListeners();
    666         for (int i = 0; i < mls.length; i++) {
    667         // Remove the instances of MouseInputHandler
    668         if (mls[i].toString().startsWith("javax.swing.plaf.basic.BasicTableUI$MouseInputHandler@")) {
    669             removeMouseListener(mls[i]);
     339    }
     340    }
     341
     342
     343    private class MetadataValueTextFieldKeyListener
     344    extends KeyAdapter
     345    {
     346    /** Gives notification of key events on the text field */
     347    public void keyPressed(KeyEvent key_event)
     348    {
     349        // Tab: Auto-complete what is selected in the metadata value tree
     350        if (key_event.getKeyCode() == KeyEvent.VK_TAB) {
     351        MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
     352        if (selected_metadata_value_tree_node != null) {
     353            metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
    670354        }
    671         }
    672         // Add the slightly-altered mouse listener
    673         addMouseListener(new TableMouseInputHandler());
    674 
    675         // We also have to ensure that the table column header hasn't gone on a severe Jenny Craig binge and has somehow lost 7/8th of its component size
    676         JTableHeader table_header = getTableHeader();
    677         Dimension table_header_preferred_size = table_header.getPreferredSize();
    678         if (table_header_preferred_size.height < MINIMUM_TABLE_HEADER_SIZE) {
    679         table_header_preferred_size.setSize(table_header_preferred_size.width, MINIMUM_TABLE_HEADER_SIZE);
    680         table_header.setPreferredSize(table_header_preferred_size);
    681         }
    682 
    683         // Set the table columns as we want them
    684         setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    685         Dimension table_size = getPreferredSize();
    686         TableColumnModel column_model = getColumnModel();
    687 
    688         TableColumn inherited_column = column_model.getColumn(0);
    689         inherited_column.setPreferredWidth(20);
    690         inherited_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
    691 
    692         TableColumn element_column = column_model.getColumn(1);
    693         element_column.setPreferredWidth((TABLE_SIZE.width / 4) - 15);
    694         element_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
    695 
    696         TableColumn value_column = column_model.getColumn(2);
    697         value_column.setPreferredWidth(((3 * TABLE_SIZE.width) / 4) - 30);
    698         value_column.setCellRenderer(new MetadataValueTableCellRenderer(metadata_value_table_model));
    699     }
    700 
    701 
    702     public MetadataElement getSelectedMetadataElement()
    703     {
    704         return selected_metadata_value_table_entry.getMetadataElement();
    705     }
    706 
    707 
    708     public MetadataValueTableEntry getSelectedMetadataValueTableEntry()
    709     {
    710         return selected_metadata_value_table_entry;
    711     }
    712 
    713 
    714     public boolean isSelectedMetadataValueTableEntryCommon()
    715     {
    716         return metadata_value_table_model.isCommon(selected_metadata_value_table_entry);
    717     }
    718 
    719 
    720     public void moveSelectionDown()
    721     {
    722         int new_row_to_select = getSelectedRow() + 1;
    723         if (new_row_to_select < metadata_value_table_model.getRowCount()) {
    724         changeSelection(new_row_to_select, 1, false, false);
    725         }
    726     }
    727 
    728 
    729     public void moveSelectionUp()
    730     {
    731         int new_row_to_select = getSelectedRow() - 1;
    732         if (new_row_to_select >= 0) {
    733         changeSelection(new_row_to_select, 1, false, false);
    734         }
    735     }
    736 
    737 
    738     public void rebuildAndSelectRowWithValueClosestTo(MetadataValue optimal_metadata_value_to_select_when_building_complete)
    739     {
    740         // We don't want a lot of ListSelectionEvents while the table is rebuilding
    741         clearSelection();
    742 
    743         // Rebuild the metadata value table model
    744         metadata_value_table_model.rebuild(file_nodes);
    745 
    746         // If the metadata value table is empty there isn't much to do
    747         if (metadata_value_table_model.getRowCount() == 0) {
    748         // ...except display the "no metadata available" card, unless no files are selected
    749         if (file_nodes != null) {
    750             metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD_NO_METADATA_AVAILABLE);
    751         }
    752         return;
    753         }
    754 
    755         // Display the card with the metadata value table
    756         metadata_value_table_card_layout.show(table_card_pane, TABLE_CARD);
    757 
    758         // If we don't need to select a row in the table after rebuilding, we're done
    759         if (optimal_metadata_value_to_select_when_building_complete == null) {
    760         return;
    761         }
    762 
    763         MetadataElement optimal_metadata_element = optimal_metadata_value_to_select_when_building_complete.getMetadataElement();
    764        
    765         // Find the row containing the closest value to the optimal value
    766         int optimal_row_to_select = 0;
    767         for (int i = 0; i < metadata_value_table_model.getRowCount(); i++) {
    768         MetadataValueTableEntry metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(i);
    769         MetadataElement metadata_element = metadata_value_table_entry.getMetadataElement();
    770         if (metadata_element.equals(optimal_metadata_element)) {
    771             // This row will be the optimal row, unless the next row matches better
    772             optimal_row_to_select = i;
    773         }
    774 
    775         int c = metadata_value_table_entry.compareTo(optimal_metadata_value_to_select_when_building_complete);
    776         if (c >= 0) {
    777             // We've either found an exact match, or there is no exact match, so stop here
    778             break;
    779         }
    780         }
    781 
    782         changeSelection(optimal_row_to_select, 1, false, false);
    783         metadata_value_text_field.requestFocus();
    784     }
    785 
    786 
    787     private class MetadataValueTableListSelectionListener
    788         implements ListSelectionListener
    789     {
    790         public void valueChanged(ListSelectionEvent event)
    791         {
    792         // We only want to handle one event per selection, so wait for the value to stabilise
    793         if (getSelectionModel().getValueIsAdjusting() == true) {
    794             return;
    795         }
    796 
    797         selected_metadata_value_table_entry = null;
    798         MetadataElement selected_metadata_element = null;
    799         String selected_metadata_value = "";
    800 
    801         // We have a SINGLE_SELECTION model set so there is at most one selection
    802         int selected_row = getSelectedRow();
    803         if (selected_row >= 0) {
    804             selected_metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(selected_row);
    805             selected_metadata_element = selected_metadata_value_table_entry.getMetadataElement();
    806             MetadataValueTreeNode metadata_value_tree_node = selected_metadata_value_table_entry.getMetadataValueTreeNode();
    807             if (metadata_value_tree_node != null) {
    808             selected_metadata_value = metadata_value_tree_node.getFullValue();
    809             }
    810         }
    811 
    812         // Update the metadata value tree
    813         metadata_value_tree.rebuild(selected_metadata_element, selected_metadata_value);
    814         }
    815     }
    816 
    817 
    818     /** 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. */
    819     private class TableMouseInputHandler implements MouseInputListener
    820     {
    821         // Component receiving mouse events during editing.
    822         // May not be editorComponent.
    823         private Component dispatchComponent;
    824         private boolean selectedOnPress;
    825 
    826         //  The Table's mouse listener methods.
    827 
    828         public void mouseClicked(MouseEvent e) {}
    829 
    830         private void setDispatchComponent(MouseEvent e) {
    831         Component editorComponent = getEditorComponent();
    832         Point p = e.getPoint();
    833         Point p2 = SwingUtilities.convertPoint(metadata_value_table, p, editorComponent);
    834         dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
    835                                      p2.x, p2.y);
    836         }
    837 
    838         private boolean repostEvent(MouseEvent e) {
    839         // Check for isEditing() in case another event has
    840         // caused the editor to be removed. See bug #4306499.
    841         if (dispatchComponent == null || !isEditing()) {
    842             return false;
    843         }
    844         MouseEvent e2 = SwingUtilities.convertMouseEvent(metadata_value_table, e, dispatchComponent);
    845         dispatchComponent.dispatchEvent(e2);
    846         return true;
    847         }
    848 
    849         private void setValueIsAdjusting(boolean flag) {
    850         getSelectionModel().setValueIsAdjusting(flag);
    851         getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
    852         }
    853 
    854         private boolean shouldIgnore(MouseEvent e) {
    855         return e.isConsumed() || (!(SwingUtilities.isLeftMouseButton(e) && isEnabled()));
    856         }
    857 
    858         public void mousePressed(MouseEvent e) {
    859         if (e.isConsumed()) {
    860             selectedOnPress = false;
    861             return;
    862         }
    863         selectedOnPress = true;
    864         adjustFocusAndSelection(e);
    865         }
    866 
    867         void adjustFocusAndSelection(MouseEvent e) {
    868         if (shouldIgnore(e)) {
    869             return;
    870         }
    871 
    872         Point p = e.getPoint();
    873         int row = rowAtPoint(p);
    874         int column = columnAtPoint(p);
    875         // The autoscroller can generate drag events outside the Table's range.
    876         if ((column == -1) || (row == -1)) {
    877             return;
    878         }
    879 
    880         if (editCellAt(row, column, e)) {
    881             setDispatchComponent(e);
    882             repostEvent(e);
    883         }
    884         // Commented this out and added the line below it to keep the value text field in focus
    885         // else if (isRequestFocusEnabled()) {
    886         // requestFocus();
    887         // }
    888         metadata_value_text_field.requestFocus();
    889 
    890         CellEditor editor = getCellEditor();
    891         if (editor == null || editor.shouldSelectCell(e)) {
    892             boolean adjusting = (e.getID() == MouseEvent.MOUSE_PRESSED) ? true : false;
    893             setValueIsAdjusting(adjusting);
    894 
    895             // Special code for clicking the first column (folder-level metadata)
    896             if (column == 0) {
    897             selected_metadata_value_table_entry = metadata_value_table_model.getMetadataValueTableEntry(row);
    898 
    899             // If this metadata is inherited, switch to the folder it came from
    900             if (selected_metadata_value_table_entry.isInheritedMetadata()) {
    901                 File folder_metadata_inherited_from = selected_metadata_value_table_entry.getFolderMetadataInheritedFrom();
    902                 if (folder_metadata_inherited_from != null) {
    903                 collection_tree.setSelection(folder_metadata_inherited_from);
    904                 }
    905             }
    906 
    907             changeSelection(row, 0, e.isControlDown(), e.isShiftDown());
    908             }
    909             else {
    910             changeSelection(row, 1, e.isControlDown(), e.isShiftDown());
    911             }
    912         }
    913         }
    914 
    915         public void mouseReleased(MouseEvent e) {
    916         if (selectedOnPress) {
    917             if (shouldIgnore(e)) {
    918             return;
    919             }
    920 
    921             repostEvent(e);
    922             dispatchComponent = null;
    923             setValueIsAdjusting(false);
    924         } else {
    925             adjustFocusAndSelection(e);
    926         }
    927         }
    928 
    929 
    930         public void mouseEntered(MouseEvent e) {}
    931 
    932         public void mouseExited(MouseEvent e) {}
    933 
    934         //  The Table's mouse motion listener methods.
    935 
    936         public void mouseMoved(MouseEvent e) {}
    937 
    938         public void mouseDragged(MouseEvent e) {
    939         if (shouldIgnore(e)) {
    940             return;
    941         }
    942 
    943         repostEvent(e);
    944 
    945         CellEditor editor = getCellEditor();
    946         if (editor == null || editor.shouldSelectCell(e)) {
    947             Point p = e.getPoint();
    948             int row = rowAtPoint(p);
    949             int column = columnAtPoint(p);
    950             // The autoscroller can generate drag events outside the Table's range.
    951             if ((column == -1) || (row == -1)) {
    952             return;
    953             }
    954             changeSelection(row, column, false, true);
    955         }
    956         }
    957     }
    958     }
    959 
    960 
    961     /**
    962      * This class is a little bit complex, a little bit subtle, and a little bit odd.
    963      * The strange interaction model is due to the fact that it is very tightly
    964      * coupled to the EnrichPane.
    965      *
    966      * The interaction is complex because there are three separate controls that the
    967      * user may interact with, each of which can affect the other two. The three
    968      * controls are:
    969      *   - The "assigned metadata" table, at the top right of the meta edit pane.
    970      *   - The "metadata value" text field, where users can type in new values.
    971      *   - The "metadata value tree", which shows other values that have been
    972      *     assigned to the selected metadata element.
    973      *
    974      * The interactions are:
    975      *   - The "assigned metadata" table
    976      *     Users may select one (and only one) row in this table. Selecting a row
    977      *     chooses one metadata element. The text field will be set to the current
    978      *     value of the metadata element. This value will also be selected in the
    979      *     metadata value tree.
    980      *
    981      *   - The "metadata value" text field
    982      *     If the value the user has typed in this is a prefix of an entry in the
    983      *     value tree, this value will be selected in the value tree. In this case,
    984      *     pressing "Tab" will complete the value (ie. make the value in the text
    985      *     field the same as the value in the tree). This is to allow users to
    986      *     quickly and easily assign existing metadata values to new documents.
    987      *
    988      *   - The "metadata value tree"
    989      *     Selecting a value in the tree will set the text field to this value.
    990      */
    991     private class MetadataValueTree
    992     extends JPanel
    993     {
    994     private boolean ignore_tree_selection_event = false;
    995     private boolean manual_text_edit_event = false;
    996     private CardLayout card_layout;
    997     private String card_showing = null;
    998     /** The metadata element that is currently selected. */
    999     private MetadataElement selected_metadata_element = null;
    1000     private JTextArea extracted_message;
    1001     private JTree tree;
    1002 
    1003     static final private String NONE = "None";
    1004     static final private String TREE = "Tree";
    1005 
    1006 
    1007     public MetadataValueTree(int width, int height)
    1008     {
    1009         super();
    1010 
    1011         setFont(Configuration.getFont("general.font", false));
    1012         setPreferredSize(new Dimension(width, height));
    1013 
    1014         JPanel metadata_pane = new JPanel();
    1015 
    1016         JPanel value_pane = new JPanel();
    1017         JLabel value_label = new JLabel();
    1018         Dictionary.registerText(value_label, "EnrichPane.Value");
    1019 
    1020         JPanel value_field_pane = new JPanel();
    1021         metadata_value_text_field.setBackground(Configuration.getColor("coloring.editable_background", false));
    1022         metadata_value_text_field.setForeground(Configuration.getColor("coloring.editable_foreground", false));
    1023         metadata_value_text_field.setPreferredSize(new Dimension(413, 24));
    1024         metadata_value_text_field.getDocument().addDocumentListener(new DocumentListenerImpl());
    1025         metadata_value_text_field.addKeyListener(new MetadataValueTextFieldKeyListener());
    1026         metadata_value_text_field.setFocusTraversalKeysEnabled(false);
    1027         Dictionary.setTooltip(metadata_value_text_field, "EnrichPane.Value_Field_Tooltip");
    1028 
    1029         JPanel button_pane = new JPanel();
    1030 
    1031         JPanel tree_pane = new JPanel();
    1032         JLabel tree_label = new JLabel();
    1033         Dictionary.registerText(tree_label, "EnrichPane.Tree");
    1034 
    1035         tree = new JTree();
    1036         tree.addMouseListener(new MetadataValueTreeMouseListener());
    1037         tree.addTreeSelectionListener(new MetadataValueTreeSelectionListener());
    1038         tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    1039         tree.setRootVisible(false);
    1040         tree.putClientProperty("JTree.lineStyle", "Angled");
    1041 
    1042         JPanel extracted_pane = new JPanel();
    1043         JPanel extracted_header_pane = new JPanel();
    1044         extracted_message = new JTextArea("");
    1045         extracted_message.setEditable(false);
    1046         extracted_message.setLineWrap(true);
    1047         extracted_message.setOpaque(false);
    1048         extracted_message.setWrapStyleWord(true);
    1049 
    1050         card_layout = new CardLayout();
    1051 
    1052         // Layout
    1053         value_field_pane.setBorder(BorderFactory.createEmptyBorder(0,5,0,5));
    1054         value_field_pane.setLayout(new BorderLayout(0, 0));
    1055         value_field_pane.add(metadata_value_text_field, BorderLayout.CENTER);
    1056 
    1057         button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
    1058         button_pane.setLayout(new GridLayout());
    1059         button_pane.add(add);
    1060         button_pane.add(replace);
    1061         button_pane.add(remove);
    1062 
    1063         value_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
    1064         value_pane.setLayout(new BorderLayout());
    1065         value_pane.add(value_label, BorderLayout.WEST);
    1066         value_pane.add(value_field_pane, BorderLayout.CENTER);
    1067         value_pane.add(expand, BorderLayout.EAST);
    1068         value_pane.add(button_pane, BorderLayout.SOUTH);
    1069 
    1070         tree_pane.setLayout(new BorderLayout());
    1071         tree_pane.add(tree_label, BorderLayout.NORTH);
    1072         tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
    1073 
    1074         metadata_pane.setLayout(new BorderLayout());
    1075         metadata_pane.add(value_pane, BorderLayout.NORTH);
    1076         metadata_pane.add(tree_pane, BorderLayout.CENTER);
    1077 
    1078         extracted_header_pane.setLayout(new BorderLayout());
    1079         extracted_header_pane.add(expand_for_extracted, BorderLayout.EAST);
    1080 
    1081         extracted_pane.setBorder(BorderFactory.createEmptyBorder(0,10,25,0));
    1082         extracted_pane.setLayout(new BorderLayout());
    1083         extracted_pane.add(extracted_header_pane, BorderLayout.NORTH);
    1084         extracted_pane.add(extracted_message, BorderLayout.CENTER);
    1085 
    1086         this.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
    1087         this.setLayout(card_layout);
    1088         this.add(metadata_pane, TREE);
    1089         this.add(extracted_pane, NONE);
    1090         card_showing = TREE;
    1091     }
    1092 
    1093 
    1094     /** Returns a TreePath for the node most closely matching the metadata value. */
    1095     private TreePath getClosestPath(String val)
    1096     {
    1097         // Start at the root of the tree
    1098         MetadataValueTreeNode tree_node = (MetadataValueTreeNode) tree.getModel().getRoot();
    1099 
    1100         // Separate hierarchical values
    1101         PatternTokenizer tokenizer = new PatternTokenizer(val, MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
    1102         while (tokenizer.hasMoreTokens()) {
    1103         String token = tokenizer.nextToken();
    1104 
    1105         // All components except the last must match exactly
    1106         if (tokenizer.hasMoreTokens()) {
    1107             for (int i = 0; i < tree_node.getChildCount(); i++) {
    1108             MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
    1109             if (child_node.getValue().equals(token)) {
    1110                 // The matching node has been found, so move onto the next token
    1111                 tree_node = child_node;
    1112                 break;
    1113             }
    1114             }
    1115         }
    1116 
    1117         // The last component may match partially
    1118         else {
    1119             for (int i = 0; i < tree_node.getChildCount(); i++) {
    1120             MetadataValueTreeNode child_node = (MetadataValueTreeNode) tree_node.getChildAt(i);
    1121             if (child_node.getFullValue().startsWith(val)) {
    1122                 // The closest node has been found, so return its path
    1123                 return new TreePath(child_node.getPath());
    1124             }
    1125             }
    1126 
    1127             // Not even a partial match
    1128             return null;
    1129         }
    1130         }
    1131 
    1132         // If nothing else, return the path of the root node
    1133         return new TreePath(tree_node.getPath());
    1134     }
    1135 
    1136 
    1137     public String getSelectedValue()
    1138     {
    1139         String metadata_value_raw = metadata_value_text_field.getText();
    1140         String metadata_value = XMLTools.removeInvalidCharacters(metadata_value_raw);
    1141         return metadata_value.replaceAll("\\\\", MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN);
    1142     }
    1143 
    1144 
    1145     public void rebuild(MetadataElement metadata_element, String metadata_value)
    1146     {
    1147         // System.err.println("In MetadataValueTree.rebuild()...");
    1148 
    1149         // Clear selection
    1150         if (metadata_element == null) {
    1151         if (metadata_edit_event == false) {
    1152             // System.err.println("Clearing selection...");
    1153             selected_metadata_element = null;
    1154             validateDisplay();
    1155         }
    1156         return;
    1157         }
    1158 
    1159         // Reload the value tree model if the selected metadata element has changed
    1160         if (selected_metadata_element != metadata_element) {
    1161         selected_metadata_element = metadata_element;
    1162         tree.setModel(selected_metadata_element.getMetadataValueTreeModel());
    1163         }
    1164 
    1165         validateDisplay();
    1166 
    1167         setSelectedValue(metadata_value);
    1168     }
    1169 
    1170 
    1171     /** This function is copied from JTree::setPathToVisible(), and modified so tree rows
    1172         are always shown flushed left. Note: doesn't do accessibleContext stuff. */
    1173     private void scrollTreePathToVisible(TreePath path)
    1174     {
    1175         if (path != null) {
    1176         tree.makeVisible(path);
    1177 
    1178         Rectangle bounds = tree.getPathBounds(path);
    1179         if (bounds != null) {
    1180             bounds.width += bounds.x;
    1181             bounds.x = 0;
    1182             tree.scrollRectToVisible(bounds);
    1183         }
    1184         }
    1185     }
    1186 
    1187 
    1188     /** Sets the value in the text field. */
    1189     private void setSelectedValue(String metadata_value)
    1190     {
    1191         // Setting the text of the field causes the DocumentListener to be notified, and
    1192         //   updating the tree is handled there (DocumentListenerImpl::validate())
    1193         if (!card_showing.equals(NONE)) {
    1194         manual_text_edit_event = metadata_value.equals("");  // Set to false unless val == ""
    1195         metadata_value_text_field.setText(metadata_value);
    1196         manual_text_edit_event = true;
    1197         }
    1198     }
    1199 
    1200 
    1201     private void validateDisplay()
    1202     {
    1203         // If no metadata is selected in the metadata value table, display "no metadata element selected" card
    1204         if (file_nodes == null || selected_metadata_element == null) {
    1205         metadata_value_tree_card_layout.show(control_pane, TREE_CARD_NO_METADATA_ELEMENT_SELECTED);
    1206         }
    1207 
    1208         // Otherwise, display the card with the metadata value tree
    1209         else {
    1210         metadata_value_tree_card_layout.show(control_pane, TREE_CARD);
    1211 
    1212         // Special case the extracted metadata set
    1213         if (selected_metadata_element.getNamespace().equals(Utility.EXTRACTED_METADATA_NAMESPACE)) {
    1214             // Display the panel showing the "you cannot edit this metadata" message
    1215             String[] args = new String[1];
    1216             args[0] = selected_metadata_element.getFullName();
    1217             Dictionary.registerText(extracted_message, "EnrichPane.AutoMessage", args);
    1218             card_layout.show(this, NONE);
    1219             card_showing = NONE;
    1220         }
    1221         else {
    1222             // Display the panel for viewing and assigning metadata
    1223             card_layout.show(this, TREE);
    1224             card_showing = TREE;
    1225         }
    1226 
    1227         // Validate the buttons above the value tree
    1228         MetadataValueTableEntry metadata_value_table_entry = metadata_value_table.getSelectedMetadataValueTableEntry();
    1229         if (metadata_value_table_entry.getValue().equals("")) {
    1230             // Can only append if something has been entered
    1231             add.setEnabled((getSelectedValue().length() > 0));
    1232 
    1233             // Nothing to replace or remove
    1234             replace.setEnabled(false);
    1235             remove.setEnabled(false);
    1236             return;
    1237         }
    1238 
    1239         // Check if the text in the value field is the same as the metadata value
    1240         if (getSelectedValue().equals(metadata_value_table_entry.getFullValue())) {
    1241             // Can't replace
    1242             replace.setEnabled(false);
    1243 
    1244             // Adding, however, is dependant on whether the selected metadata is common or uncommon. If the later then you can append so as to make it common.
    1245             add.setEnabled(!metadata_value_table.isSelectedMetadataValueTableEntryCommon());
    1246         }
    1247         else {
    1248             // Can append or replace, if something has been entered
    1249             add.setEnabled((getSelectedValue().length() > 0));
    1250             replace.setEnabled((getSelectedValue().length() > 0));
    1251         }
    1252 
    1253         // Can only remove if the metadata is file level
    1254         if (metadata_value_table_entry != null) {  // Shouldn't be necessary, but is
    1255             remove.setEnabled((metadata_value_table_entry.isInheritedMetadata() == false));
    1256         }
    1257         }
    1258     }
    1259 
    1260 
    1261     private class MetadataValueTextFieldKeyListener
    1262         extends KeyAdapter
    1263     {
    1264         /** Gives notification of key events on the text field */
    1265         public void keyPressed(KeyEvent e)
    1266         {
    1267         // Tab: Auto-complete
    1268         if (e.getKeyCode() == KeyEvent.VK_TAB) {
    1269             if (tree.getSelectionCount() != 0 && !getSelectedValue().equals("")) {
    1270             TreePath path = tree.getSelectionPath();
    1271             MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
    1272             setSelectedValue(node.getFullValue());
    1273             }
    1274         }
    1275 
    1276         // Enter: Append the metadata value, if we're allowed to
    1277         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
    1278             if (add.isEnabled()) {
    1279             add.doClick();
    1280             }
    1281         }
    1282 
    1283         // Down: Change the metadata value table selection
    1284         if (e.getKeyCode() == KeyEvent.VK_DOWN) {
    1285             metadata_value_table.moveSelectionDown();
    1286         }
    1287 
    1288         // Up: Change the metadata value table selection
    1289         if (e.getKeyCode() == KeyEvent.VK_UP) {
    1290             metadata_value_table.moveSelectionUp();
    1291         }
    1292         }
    1293     }
    1294 
    1295 
    1296     private class MetadataValueTreeMouseListener
    1297         extends MouseAdapter
    1298     {
    1299         public void mouseClicked(MouseEvent e)
    1300         {
    1301         // Double click: assign the selected value tree node immediately (if possible)
    1302         if (e.getClickCount() == 2 && tree.getSelectionCount() != 0) {
    1303             TreePath path = tree.getSelectionPath();
    1304             MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
    1305 
    1306             // Leaf nodes only (folder nodes will expand/contract)
    1307             if (node.getChildCount() == 0 && add.isEnabled()) {
    1308             add.doClick();
    1309             }
    1310         }
    1311         }
    1312     }
    1313 
    1314 
    1315     private class MetadataValueTreeSelectionListener
    1316         implements TreeSelectionListener
    1317     {
    1318         public void valueChanged(TreeSelectionEvent event)
    1319         {
    1320         // Select a node in the tree: fill the metadata value text field with the selected node's value
    1321         if (tree.getSelectionCount() != 0 && !ignore_tree_selection_event) {
    1322             TreePath path = tree.getSelectionPath();
    1323             MetadataValueTreeNode node = (MetadataValueTreeNode) path.getLastPathComponent();
    1324             setSelectedValue(node.getFullValue());
    1325         }
    1326         }
    1327     }
    1328 
    1329 
    1330     private class DocumentListenerImpl
    1331         implements DocumentListener
    1332     {
    1333         /** Gives notification that an attribute or set of attributes changed */
    1334         public void changedUpdate(DocumentEvent e) {
    1335         validate();
    1336         }
    1337 
    1338         /** Gives notification that there was an insert into the document */
    1339         public void insertUpdate(DocumentEvent e) {
    1340         validate();
    1341         }
    1342 
    1343         /** Gives notification that a portion of the document has been removed */
    1344         public void removeUpdate(DocumentEvent e) {
    1345         validate();
    1346         }
    1347 
    1348 
    1349         /** Ensures that the value text field and value tree are synchronized */
    1350         private void validate()
    1351         {
    1352         String value_text = getSelectedValue();
    1353 
    1354         // Ignore the validate() with empty text that occurs when value.setText() is used
    1355         if (!value_text.equals("") || manual_text_edit_event == true) {
    1356             TreePath closest_path = getClosestPath(value_text);
    1357 
    1358             // Select the new path in the tree
    1359             // The tree selection event this causes must be ignored, since it alters value
    1360             ignore_tree_selection_event = true;
    1361             tree.setSelectionPath(closest_path);
    1362             ignore_tree_selection_event = false;
    1363 
    1364             // If a folder has been specified, make sure it is expanded
    1365             if (value_text.endsWith(MetadataValueTreeNode.METADATA_VALUE_TREE_NODE_HIERARCHY_TOKEN) && !tree.isExpanded(closest_path)) {
    1366             tree.expandPath(closest_path);
    1367             }
    1368 
    1369             // Make sure the path is visible on the screen
    1370             scrollTreePathToVisible(closest_path);
    1371             // One call should be enough, but it isn't in some cases (for some reason)
    1372             scrollTreePathToVisible(closest_path);
    1373 
    1374             // Update the status of the buttons
    1375             validateDisplay();
    1376         }
     355
     356        // We do not want this event to be processed by the table also
     357        key_event.consume();
     358        }
     359
     360        // Enter: save the current value then add a blank row for the selected metadata element
     361        if (key_event.getKeyCode() == KeyEvent.VK_ENTER) {
     362        metadata_value_table_pane.stopEditingAndAddBlankRowForSelectedMetadataElement();
     363        }
     364    }
     365    }
     366
     367
     368    private class MetadataValueTreeSelectionListener
     369    implements TreeSelectionListener
     370    {
     371    public void valueChanged(TreeSelectionEvent tree_selection_event)
     372    {
     373        // When a node is selected in the tree, fill the metadata value text field with the selected node's value
     374        MetadataValueTreeNode selected_metadata_value_tree_node = metadata_value_tree_pane.getSelectedMetadataValueTreeNode();
     375        if (selected_metadata_value_tree_node != null) {
     376        metadata_value_table_pane.setMetadataValueTextFieldValue(selected_metadata_value_tree_node.getFullValue());
    1377377        }
    1378378    }
     
    1538538        }
    1539539        }
    1540         // finally we have the correct selection paths!!
     540        // finally we have the correct selection paths!
    1541541
    1542542        // Create an appropriate context menu, based on what is selected
Note: See TracChangeset for help on using the changeset viewer.