/**
*#########################################################################
*
* 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() + "\"";
}
}
}