/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * Author: John Thompson, Greenstone Digital Library, University of Waikato * * Copyright (C) 1999 New Zealand Digital Library Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.cdm; import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import org.greenstone.gatherer.Configuration; import org.greenstone.gatherer.DebugStream; import org.greenstone.gatherer.Dictionary; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.cdm.CollectionDesignManager; import org.greenstone.gatherer.cdm.Control; import org.greenstone.gatherer.cdm.ElementWrapper; import org.greenstone.gatherer.gui.GLIButton; import org.greenstone.gatherer.mem.MetadataEditorManager; import org.greenstone.gatherer.msm.MetadataSet; import org.greenstone.gatherer.msm.MSMEvent; import org.greenstone.gatherer.msm.MSMListener; import org.w3c.dom.*; /** This class only knows how to produce a simple visual representation of the currently imported metadata sets. It is also read-only, so should be fairly straight forward. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3d */ public class MetadataSetView extends DynamicListModel implements MSMListener { /** The visual contols used to review the metadata sets. */ private Control controls = null; /** A reference to ourselves so our inner classes can refer to us. */ private DynamicListModel model = null; /** Constructor. */ public MetadataSetView() { DebugStream.println("MetadataSetView: Initialized."); model = this; Gatherer.c_man.getCollection().msm.addMSMListener(this); loadMetadataSets(); // Build the controls controls = new MetadataSetControl(); } /** Destructor. Remove any references of this class from persistant objects. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public void destroy() { controls.destroy(); controls = null; Gatherer.c_man.msm.removeMSMListener(this); model = null; } /** Called when an element is changed within a set in the MSM, prompting us to refresh our list of elements being shown. * @param event A MSMEvent which encapsulates relevant data about the change. * @see org.greenstone.gatherer.cdm.MetadataSetView.MetadataSetControl */ public void elementChanged(MSMEvent event) { // Get the controls to refresh element list. ((MetadataSetControl)controls).refreshElementList(); } /** A method for retrieve the controls for this manager. * @see org.greenstone.gatherer.Dictionary * @see org.greenstone.gatherer.gui.Coloring */ public Control getControls() { return controls; } /** Called when a metadata value has undergone significant change. * @param event A MSMEvent which encapsulates relevant data about the change. */ public void metadataChanged(MSMEvent event) { // Couldn't care less. } /** Called when a set is added or removed from the MSM. * @param event A MSMEvent which encapsulates relevant data about the change. */ public void setChanged(MSMEvent event) { // Reload model. clear(); loadMetadataSets(); } /** Prints out the contents of this manager, as they would appear in the collection configuration file. * @return A String containing a block of commands. * @see org.greenstone.gatherer.cdm.MetadataSetView.SetWrapper */ public String toString() { String text = ""; for(int i = 0; i < size(); i++) { SetWrapper set = (SetWrapper)get(i); text = text + set.toString() + "\n"; } text = text + "\n"; return text; } /** Called when a significant change has occured to a value tree for a certain element, however we take no further action. * @param event A MSMEvent containing information relevant to the event. */ public void valueChanged(MSMEvent event) { } /** Retrieves the current list of loaded metadata sets, and builds a list model around them. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.cdm.MetadataSetView.SetWrapper * @see org.greenstone.gatherer.collection.CollectionManager */ private void loadMetadataSets() { // We initialize the set_model with wrapped metadata sets. Note that when we call getSets() we also end up adding ourselves as a listener to the metadata set manager. Vector sets = Gatherer.c_man.getCollection().msm.getSets(); for(int i = 0; i < sets.size(); i++) { addElement(new SetWrapper((MetadataSet)sets.get(i))); } } /** This class creates and lays-out the various controls for reviewing the metadata sets, and their commands as they would appear in the collection configuration file. */ private class MetadataSetControl extends JPanel implements Control { /** Listens for clicks upon the configure button, or double clicks from the metadata set list. */ private ConfigureActionListener configure_action_listener; /** Opens the MEM and systematically performs as if the add set button were clicked. */ private JButton add_button; /** Opens the MEM with the appropriate set open for editing. */ private JButton configure_button; /** Opens the MEM and systematically performs as if the remove set button were clicked. */ private JButton remove_button; /** The label denoting the element list. */ private JLabel element_label = null; /** The label denoting the set list. */ private JLabel set_label = null; /** The title of these controls. */ private JLabel title = null; /** The list of elements for the choosen set. */ private JList element_list = null; /** The list of sets in this collection. */ private JList set_list = null; /** The panel onto which all other panels will be placed. */ private JPanel central_pane = null; /** The panel onto which the element list will be placed. */ private JPanel element_pane = null; /** The panel containing the title and instructions. */ private JPanel header_pane = null; /** The panel containing the set list. */ private JPanel set_pane = null; private JScrollPane element_list_scroll_pane; /** The text area of inline instructions. */ private JTextArea instructions = null; private ListListener list_listener; private MultilingualListCellRenderer element_list_cell_renderer; /** The element model for the currently selected set. */ private Vector element_model = null; /* Constructor. * @see org.greenstone.gatherer.Coloring; * @see org.greenstone.gatherer.Dictionary * @see org.greenstone.gatherer.cdm.MetadataSetView.ListListener */ public MetadataSetControl() { // Create visual components central_pane = new JPanel(); element_label = new JLabel(); element_label.setHorizontalAlignment(JLabel.CENTER); element_label.setOpaque(true); Dictionary.registerText(element_label, "CDM.MetadataSetManager.Elements"); element_list_scroll_pane = new JScrollPane(); element_list_cell_renderer = new MultilingualListCellRenderer(element_list_scroll_pane); element_list = new JList(); element_list_scroll_pane.setViewportView(element_list); element_list.setCellRenderer(element_list_cell_renderer); element_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); element_pane = new JPanel(); header_pane = new JPanel(); instructions = new JTextArea(); instructions.setBackground(Configuration.getColor("coloring.collection_tree_background", false)); instructions.setEditable(false); instructions.setLineWrap(true); instructions.setRows(6); instructions.setWrapStyleWord(true); Dictionary.registerText(instructions, "CDM.MetadataSetManager.Instructions"); set_label = new JLabel(); set_label.setHorizontalAlignment(JLabel.CENTER); set_label.setOpaque(true); Dictionary.registerText(set_label, "CDM.MetadataSetManager.Sets"); set_list = new JList(model); set_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); if(model.size() > 0) { set_list.setSelectedIndex(0); } set_pane = new JPanel(); title = new JLabel(); title.setHorizontalAlignment(JLabel.CENTER); title.setOpaque(true); Dictionary.registerText(title, "CDM.MetadataSetManager.Title"); JPanel button_pane = new JPanel(); add_button = new GLIButton(); add_button.setEnabled(true); add_button.setMnemonic(KeyEvent.VK_A); Dictionary.registerBoth(add_button, "CDM.MetadataSetManager.Add", "CDM.MetadataSetManager.Add_Tooltip"); configure_button = new GLIButton(); configure_button.setEnabled(false); configure_button.setMnemonic(KeyEvent.VK_C); Dictionary.registerBoth(configure_button, "CDM.MetadataSetManager.Configure", "CDM.MetadataSetManager.Configure_Tooltip"); remove_button = new GLIButton(); remove_button.setEnabled(false); remove_button.setMnemonic(KeyEvent.VK_R); Dictionary.registerBoth(remove_button, "CDM.MetadataSetManager.Remove", "CDM.MetadataSetManager.Remove_Tooltip"); configure_action_listener = new ConfigureActionListener(); list_listener = new ListListener(); // Add listeners add_button.addActionListener(new AddButtonListener()); configure_button.addActionListener(configure_action_listener); remove_button.addActionListener(new RemoveButtonListener()); set_list.addListSelectionListener(list_listener); set_list.addMouseListener(configure_action_listener); // Layout instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5)); header_pane.setLayout(new BorderLayout()); header_pane.add(title, BorderLayout.NORTH); header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER); set_pane.setLayout(new BorderLayout()); set_pane.add(set_label, BorderLayout.NORTH); set_pane.add(new JScrollPane(set_list), BorderLayout.CENTER); element_pane.setLayout(new BorderLayout()); element_pane.add(element_label, BorderLayout.NORTH); element_pane.add(element_list_scroll_pane, BorderLayout.CENTER); central_pane.setLayout(new GridLayout(2,1)); central_pane.add(set_pane); central_pane.add(element_pane); button_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); button_pane.setLayout(new GridLayout(1,3,0,0)); button_pane.add(add_button); button_pane.add(configure_button); button_pane.add(remove_button); setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setLayout(new BorderLayout()); add(header_pane, BorderLayout.NORTH); add(central_pane, BorderLayout.CENTER); add(button_pane, BorderLayout.SOUTH); } /** Destructor. */ public void destroy() { } /** Update the element list. */ public void refreshElementList() { element_list.updateUI(); } /** Overriden to ensure the instruction area is scrolled to top. */ public void gainFocus() { if(instructions != null) { instructions.setCaretPosition(0); } // If no current selection, select first available set if(set_list.isSelectionEmpty() && set_list.getModel().getSize() > 0) { set_list.setSelectedIndex(0); list_listener.valueChanged(new ListSelectionEvent(set_list, 0, 0, true)); } } public void loseFocus() { } /** Listens for clicks on the add button, and actions them accordingly by opening the MEM and clicking add set. */ private class AddButtonListener implements ActionListener { /** Called when the add button is clicked. * @param event an ActionEvent containing information about the mouse click * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.Collection * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.mem.MetadataEditorManager */ public void actionPerformed(ActionEvent event) { Gatherer.c_man.getCollection().msm.editMDS(null, MetadataEditorManager.ADD_SET); } } /** Listens for clicks on the configure button or double clicks upon the metadata set list and chains to the MEM as appropriate. */ private class ConfigureActionListener extends MouseAdapter implements ActionListener { /** Called when the configure button is clicked. * @param event an ActionEvent containing information about the mouse click */ public void actionPerformed(ActionEvent event) { configureSet(); } /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt. * @param event a MouseEvent containing information about the mouse click */ public void mouseClicked(MouseEvent event) { if(event.getClickCount() == 2 ) { configureSet(); } } /** If some set is selected, then open the metadata set manager with the current set selected and expanded * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.Collection * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.cdm.MetadataSetView.SetWrapper * @see org.greenstone.gatherer.mem.MetadataEditorManager */ private void configureSet() { if(!set_list.isSelectionEmpty()) { MetadataSet set = ((SetWrapper)set_list.getSelectedValue()).getSet(); Gatherer.c_man.getCollection().msm.editMDS(set, MetadataEditorManager.NORMAL); } else { configure_button.setEnabled(false); } } } private class ListListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent event) { // Wait until we get a stable event if(event.getValueIsAdjusting()) { return; } // Now we can process it if(!set_list.isSelectionEmpty()) { MetadataSet set = ((SetWrapper)set_list.getSelectedValue()).getSet(); NodeList elements = set.getElements(); element_model = new Vector(); for(int i = 0; i < elements.getLength(); i++) { element_model.add(new ElementWrapper(elements.item(i))); } //Collections.sort(element_model); element_list.setListData(element_model); configure_button.setEnabled(true); remove_button.setEnabled(true); // Finally check the directionality and scroll as necessary JScrollBar scroll_bar = element_list_scroll_pane.getHorizontalScrollBar(); if(element_list_cell_renderer.getDirectionality() == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { scroll_bar.setValue(scroll_bar.getMaximum()); } else { scroll_bar.setValue(scroll_bar.getMinimum()); } } else { configure_button.setEnabled(false); remove_button.setEnabled(false); } } } /** Listens for clicks on the remove button, and actions them accordingly by opening the MEM and clicking remove set. */ private class RemoveButtonListener implements ActionListener { /** Called when the remove button is clicked. * @param event an ActionEvent containing information about the mouse click * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.Collection * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.cdm.MetadataSetView.SetWrapper * @see org.greenstone.gatherer.mem.MetadataEditorManager */ public void actionPerformed(ActionEvent event) { if(!set_list.isSelectionEmpty()) { MetadataSet set = ((SetWrapper)set_list.getSelectedValue()).getSet(); Gatherer.c_man.getCollection().msm.editMDS(set, MetadataEditorManager.REMOVE_SET); } else { remove_button.setEnabled(false); } } } private class MultilingualListCellRenderer extends DefaultListCellRenderer { private byte previous_directionality = Character.DIRECTIONALITY_UNDEFINED; private JScrollPane scroll_pane; public MultilingualListCellRenderer(JScrollPane scroll_pane) { super(); this.scroll_pane = scroll_pane; } public byte getDirectionality() { if(previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT || previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC || previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING || previous_directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE) { return Character.DIRECTIONALITY_RIGHT_TO_LEFT; } else { return Character.DIRECTIONALITY_LEFT_TO_RIGHT; } } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel component = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); // Determine if the text should be left aligned or right aligned String text = value.toString(); int text_index = 0; byte directionality = Character.DIRECTIONALITY_UNDEFINED; while(directionality == Character.DIRECTIONALITY_UNDEFINED && text_index < text.length()) { directionality = Character.getDirectionality(text.charAt(text_index)); text_index++; } if(directionality != previous_directionality) { previous_directionality = directionality; if(directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE) { ///ystem.err.println("R2L for: " + text); component.setHorizontalAlignment(JLabel.TRAILING); component.setHorizontalTextPosition(JLabel.TRAILING); component.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); //scroll_pane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); } else { component.setHorizontalAlignment(JLabel.LEADING); component.setHorizontalTextPosition(JLabel.LEADING); ///ystem.err.println("L2R for: " + text); component.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); //scroll_pane.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); } } text = null; return component; } } } /** Provides a convience wrapper around a metadata set, that allows it to appear in the JList the same way it would in the collection configuration file. */ private class SetWrapper { private MetadataSet set = null; public SetWrapper(MetadataSet set) { this.set = set; } public MetadataSet getSet() { return set; } public String toString() { String namespace = set.getNamespace(); // Watch out for the greenstone set, with its namespace of "" return "metadataset " + namespace + " \"" + set.getName() + "\""; } } }