/** *######################################################################### * * 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.DebugStream; import org.greenstone.gatherer.Dictionary; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.gui.GLIButton; import org.greenstone.gatherer.util.CheckList; import org.greenstone.gatherer.util.JarTools; import org.greenstone.gatherer.util.StaticStrings; import org.w3c.dom.*; /** This class maintains a list of indexes partitions for the purpose of defining subcollections. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.4 */ public class SubcollectionIndexManager extends DOMProxyListModel { static final private Dimension FIELD_SIZE = new Dimension(200, 30); private Control controls; private DOMProxyListModel model; private SubcollectionIndex default_index; /** Constructor. */ public SubcollectionIndexManager(Element subindexes) { super(subindexes, StaticStrings.INDEX_ELEMENT, new SubcollectionIndex()); DebugStream.println("SubcollectionIndexManager: " + getSize() + " subcollection indexes parsed."); this.model = this; // Parse and retrieve the default index NodeList default_index_elements = CollectionConfiguration.getElementsByTagName(StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT); if (default_index_elements.getLength() > 0) { default_index = new SubcollectionIndex((Element) default_index_elements.item(0)); } } /** Method to add a subindex. * @param subindex a SubcollectionIndex * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ private void addSubcollectionIndex(SubcollectionIndex subcollection_index) { if (!contains(subcollection_index)) { // add a pseudo metadata CollectionMeta metadatum = new CollectionMeta(StaticStrings.STOP_CHARACTER + subcollection_index.getID()); metadatum.setValue(subcollection_index.getID()); CollectionDesignManager.collectionmeta_manager.addMetadatum(metadatum); add(getSize(), subcollection_index); } } public void destroy() { if (controls != null) { controls.destroy(); controls = null; } default_index = null; model = null; } public Control getControls() { if (controls == null) { controls = new SubcollectionIndexControls(); } return controls; } /** Method to get all of the subindexes set. * @return an ArrayList containing all the defined indexes */ public ArrayList getSubcollectionIndexes() { return children(); } /** Called when the detail mode has changed which in turn may cause several design elements to be available/hidden * @param mode the new mode as an int */ public void modeChanged(int mode) { } private int moveSubcollectionIndex(SubcollectionIndex subcollection_index, boolean move_up) { // Determine the current position of the subcollection index int position = indexOf(subcollection_index); int new_position; // Attempt to move the subcollection index up if (move_up) { // Check it's not already at the top if (position == 0) { return position; } // This automatically removes the index first, as an Element can only exist once in a particular document new_position = position - 1; addBefore(subcollection_index, (SubcollectionIndex) getElementAt(new_position)); } // Attempt to move the subcollection index down else { // Check it's not already at the bottom if (position == (getSize()) - 1) { return position; } // This automatically removes the index first, as an Element can only exist once in a particular document new_position = position + 1; addAfter(subcollection_index, (SubcollectionIndex) getElementAt(new_position)); } return new_position; } /** Method to remove a certain subcollection index. */ private void removeSubcollectionIndex(SubcollectionIndex subcollection_index) { if (subcollection_index != null) { // Remove any current metadata from this index CollectionDesignManager.collectionmeta_manager.removeMetadata(StaticStrings.STOP_CHARACTER + subcollection_index.getID()); // Check if the index removed happens to be the default index if (default_index != null && default_index.equals(subcollection_index)) { setDefault(null); } // Remove the index remove(subcollection_index); } } /** Method to remove all of the subindexes that contain a certain subcollection. * @param subcollection the Subcollection that has been removed * @see org.greenstone.gatherer.cdm.Subcollection * @see org.greenstone.gatherer.cdm.SubcollectionIndex */ public void removeSubcollectionIndexes(Subcollection subcollection) { String subcollection_name = subcollection.getName(); int size = getSize(); for(int i = size - 1; i >= 0; i--) { SubcollectionIndex subcollection_index = (SubcollectionIndex) getElementAt(i); if (subcollection_index.getSources().contains(subcollection_name)) { removeSubcollectionIndex(subcollection_index); } } } private void replaceSubcollectionIndex(SubcollectionIndex old_subcollection, SubcollectionIndex new_subcollection) { // Remove old collection meta CollectionDesignManager.collectionmeta_manager.removeMetadata(StaticStrings.STOP_CHARACTER + old_subcollection.getID()); // Add new one CollectionMeta metadatum = new CollectionMeta(StaticStrings.STOP_CHARACTER + new_subcollection.getID()); metadatum.setValue(new_subcollection.getID()); CollectionDesignManager.collectionmeta_manager.addMetadatum(metadatum); if(default_index != null && default_index.equals(old_subcollection)) { setDefault(new_subcollection); } // get the position of the old one int position = indexOf(old_subcollection); remove(old_subcollection); add(position, new_subcollection); } /** Method to set the default subcollection index. * @param index The SubcollectionIndex to use as the default index. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager * @see org.greenstone.gatherer.cdm.SubcollectionIndex */ private void setDefault(SubcollectionIndex subcollection_index) { if (subcollection_index != null) { if (default_index == null) { // Create the default index element, and place immediately after indexes element. Element default_index_element = root.getOwnerDocument().createElement(StaticStrings.SUBCOLLECTION_DEFAULT_INDEX_ELEMENT); default_index = new SubcollectionIndex(default_index_element); Node target_node = CollectionConfiguration.findInsertionPoint(default_index_element); if (target_node != null) { root.getOwnerDocument().getDocumentElement().insertBefore(default_index_element, target_node); } else { root.getOwnerDocument().getDocumentElement().appendChild(default_index_element); } } default_index.setAssigned(true); default_index.setSources(subcollection_index.getSources()); } else { if (default_index != null) { default_index.setAssigned(false); } } } /** This class creates a set of controls for editing the indexes. */ private class SubcollectionIndexControls extends JPanel implements Control { private CheckList source_list; private JButton add_button; private JButton move_down_button; private JButton move_up_button; private JButton remove_button; private JButton replace_button; private JButton select_all_button; private JButton select_none_button; private JButton set_default_button; private JList subcollection_index_list; private JTextField subcollection_index_name_textfield; /** Constructor. */ public SubcollectionIndexControls() { super(); ArrayList sources = new ArrayList(); ListModel source_model = CollectionDesignManager.subcollection_manager; for (int i = 0; i < source_model.getSize(); i++) { sources.add(source_model.getElementAt(i)); } // Creation JPanel assigned_indexes_pane = new JPanel(); JLabel index_label = new JLabel(Dictionary.get("CDM.SubcollectionIndexManager.Subindexes")); subcollection_index_list = new JList(model); subcollection_index_list.setCellRenderer(new SubcollectionIndexListCellRenderer()); subcollection_index_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); subcollection_index_list.setVisibleRowCount(2); JPanel movement_pane = new JPanel(); move_up_button = new GLIButton(Dictionary.get("CDM.Move.Move_Up"), JarTools.getImage("arrow-up.gif"), Dictionary.get("CDM.Move.Move_Up_Tooltip")); move_up_button.setEnabled(false); move_down_button = new GLIButton(Dictionary.get("CDM.Move.Move_Down"), JarTools.getImage("arrow-down.gif"), Dictionary.get("CDM.Move.Move_Down_Tooltip")); move_down_button.setEnabled(false); set_default_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Set_Default_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Set_Default_Subindex_Tooltip")); set_default_button.setEnabled(false); JPanel index_pane = new JPanel(); JPanel details_pane = new JPanel(); JPanel labels_pane = new JPanel(); select_all_button = new GLIButton(Dictionary.get("CDM.IndexManager.Select_All"), Dictionary.get("CDM.IndexManager.Select_All_Tooltip")); select_none_button = new GLIButton(Dictionary.get("CDM.IndexManager.Select_None"), Dictionary.get("CDM.IndexManager.Select_None_Tooltip")); JPanel boxes_pane = new JPanel(); JPanel content_pane = new JPanel(); JLabel source_label = new JLabel(Dictionary.get("CDM.SubcollectionIndexManager.Source")); source_list = new CheckList(false); source_list.setListData(sources); source_list.setToolTipText(Dictionary.get("CDM.SubcollectionIndexManager.Source_Tooltip")); select_all_button.setEnabled(isSelectAllEnabled()); select_none_button.setEnabled(isSelectAllEnabled()); JPanel button_pane = new JPanel(); add_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Add_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Add_Subindex_Tooltip")); add_button.setEnabled(false); remove_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Remove_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Remove_Subindex_Tooltip")); remove_button.setEnabled(false); replace_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Replace_Subindex"), Dictionary.get("CDM.SubcollectionIndexManager.Replace_Subindex_Tooltip")); replace_button.setEnabled(false); // Listeners add_button.addActionListener(new AddListener()); add_button.addActionListener(CollectionDesignManager.buildcol_change_listener); move_down_button.addActionListener(new MoveListener(false)); move_down_button.addActionListener(CollectionDesignManager.buildcol_change_listener); move_up_button.addActionListener(new MoveListener(true)); move_up_button.addActionListener(CollectionDesignManager.buildcol_change_listener); remove_button.addActionListener(new RemoveListener()); remove_button.addActionListener(CollectionDesignManager.buildcol_change_listener); replace_button.addActionListener(new ReplaceListener()); replace_button.addActionListener(CollectionDesignManager.buildcol_change_listener); select_all_button.addActionListener(new SelectAllListener()); select_all_button.addActionListener(CollectionDesignManager.buildcol_change_listener); select_none_button.addActionListener(new SelectNoneListener()); select_none_button.addActionListener(CollectionDesignManager.buildcol_change_listener); set_default_button.addActionListener(new SetDefaultListener()); set_default_button.addActionListener(CollectionDesignManager.buildcol_change_listener); subcollection_index_list.addListSelectionListener(new AssignedListListener()); source_list.addListSelectionListener(new SourceListListener()); // Layout movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0)); movement_pane.setLayout(new GridLayout(3,1)); movement_pane.add(move_up_button); movement_pane.add(move_down_button); movement_pane.add(set_default_button); assigned_indexes_pane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); assigned_indexes_pane.setLayout(new BorderLayout()); assigned_indexes_pane.add(index_label, BorderLayout.NORTH); assigned_indexes_pane.add(new JScrollPane(subcollection_index_list), BorderLayout.CENTER); assigned_indexes_pane.add(movement_pane, BorderLayout.EAST); labels_pane.setLayout(new BorderLayout()); labels_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5)); // add the select all and select none buttons to the label panel GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); labels_pane.setLayout(gridbag); c.fill = GridBagConstraints.BOTH; c.weightx = 1.0; c.weighty = 1.0; c.gridx = 0; c.gridy = 0; gridbag.setConstraints(source_label, c); labels_pane.add(source_label); c.fill = GridBagConstraints.HORIZONTAL; c.weighty = 0.0; c.gridy = 1; gridbag.setConstraints(select_all_button, c); labels_pane.add(select_all_button); c.gridy = 2; gridbag.setConstraints(select_none_button, c); labels_pane.add(select_none_button); boxes_pane.setLayout(new BorderLayout()); boxes_pane.add(new JScrollPane(source_list), BorderLayout.CENTER); details_pane.setLayout(new BorderLayout()); details_pane.add(labels_pane, BorderLayout.WEST); details_pane.add(boxes_pane, BorderLayout.CENTER); button_pane.setLayout(new GridLayout(1,3)); button_pane.add(add_button); button_pane.add(replace_button); button_pane.add(remove_button); index_pane.setLayout(new BorderLayout()); index_pane.add(details_pane, BorderLayout.CENTER); index_pane.add(button_pane, BorderLayout.SOUTH); content_pane.setLayout(new BorderLayout()); content_pane.add(assigned_indexes_pane, BorderLayout.NORTH); content_pane.add(index_pane, BorderLayout.CENTER); setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setLayout(new BorderLayout()); add(content_pane, BorderLayout.CENTER); } public void destroy() { } public void gainFocus() { // Reload the source list ArrayList sources = new ArrayList(); ListModel source_model = CollectionDesignManager.subcollection_manager; for (int i = 0; i < source_model.getSize(); i++) { sources.add(source_model.getElementAt(i)); } source_list.setListData(sources); // Refresh the subcollection index list subcollection_index_list.updateUI(); clearControls(); } public void loseFocus() { } private void clearControls() { subcollection_index_list.clearSelection(); source_list.clearTicked(); add_button.setEnabled(false); remove_button.setEnabled(false); replace_button.setEnabled(false); set_default_button.setEnabled(false); move_down_button.setEnabled(false); move_up_button.setEnabled(false); select_all_button.setEnabled(isSelectAllEnabled()); select_none_button.setEnabled(isSelectAllEnabled()); } private boolean isSelectAllEnabled(){ return source_list.getModel().getSize() > 0 ? true : false; } private void updateControlsWithSelectedIndex() { SubcollectionIndex selected_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue(); if (selected_index == null) { source_list.clearTicked(); return; } // Display the selected subcollection index's sources source_list.clearTicked(); source_list.setTickedObjects(selected_index.getSources().toArray()); } private void validateButtons() { boolean add_enabled = false; boolean replace_enabled = false; // Can't add a new index if no sources are selected if (!source_list.isNothingTicked()) { ArrayList sources = source_list.getTicked(); SubcollectionIndex subcollection_index = new SubcollectionIndex(sources.toArray()); // Subcollection index already exists: can't add, but can replace if the name has changed if (!model.contains(subcollection_index)) { add_enabled = true; if (!subcollection_index_list.isSelectionEmpty()) { replace_enabled = true; } } } select_all_button.setEnabled(isSelectAllEnabled()); select_none_button.setEnabled(isSelectAllEnabled()); // We should now know the add_button state add_button.setEnabled(add_enabled); replace_button.setEnabled(replace_enabled); } private class AddListener implements ActionListener { public void actionPerformed(ActionEvent event) { if (!source_list.isNothingTicked()) { ArrayList sources = source_list.getTicked(); SubcollectionIndex subcollection_index = new SubcollectionIndex(sources.toArray()); addSubcollectionIndex(subcollection_index); clearControls(); } } } private class MoveListener implements ActionListener { private boolean move_up; public MoveListener(boolean move_up) { this.move_up = move_up; } public void actionPerformed(ActionEvent event) { // Retrieve the selected subcollection index SubcollectionIndex subcollection_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue(); if (subcollection_index != null) { int new_position = moveSubcollectionIndex(subcollection_index, move_up); // Ensure the subcollection index that moved is still selected subcollection_index_list.setSelectedIndex(new_position); } } } private class RemoveListener implements ActionListener { public void actionPerformed(ActionEvent event) { SubcollectionIndex delete_me = (SubcollectionIndex) subcollection_index_list.getSelectedValue(); if (delete_me != null) { removeSubcollectionIndex(delete_me); } } } private class ReplaceListener implements ActionListener { public void actionPerformed(ActionEvent event) { if (subcollection_index_list.isSelectionEmpty()|| source_list.isNothingTicked()) { // This should never happen, but just in case... replace_button.setEnabled(false); return; } SubcollectionIndex old_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue(); ArrayList sources = source_list.getTicked(); SubcollectionIndex new_index = new SubcollectionIndex(sources.toArray()); replaceSubcollectionIndex(old_index, new_index); } } private class SetDefaultListener implements ActionListener { public void actionPerformed(ActionEvent event) { SubcollectionIndex subcollection_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue(); if (subcollection_index != null) { setDefault(subcollection_index); // This should cause a repaint of just the desired row subcollection_index_list.setSelectedValue(subcollection_index, true); } set_default_button.setEnabled(false); } } private class SourceListListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent event) { if (event.getValueIsAdjusting()) { return; } validateButtons(); } } private class SelectAllListener implements ActionListener { public void actionPerformed(ActionEvent event){ if(select_all_button.isEnabled()){ source_list.setAllTicked(); validateButtons(); } } } private class SelectNoneListener implements ActionListener { public void actionPerformed(ActionEvent event){ if(select_none_button.isEnabled()){ source_list.clearTicked(); validateButtons(); } } } /** Listens for selections within the assigned subcollection indexes list */ private class AssignedListListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent event) { if (event.getValueIsAdjusting()) { return; } if (subcollection_index_list.isSelectionEmpty()) { clearControls(); return; } int i = subcollection_index_list.getSelectedIndex(); int size = subcollection_index_list.getModel().getSize(); select_all_button.setEnabled(isSelectAllEnabled()); select_none_button.setEnabled(isSelectAllEnabled()); // Enable the buttons appropriately remove_button.setEnabled(true); replace_button.setEnabled(false); add_button.setEnabled(false); SubcollectionIndex selected_index = (SubcollectionIndex) subcollection_index_list.getSelectedValue(); set_default_button.setEnabled(default_index == null || !default_index.equals(selected_index)); if (i > 0) { move_up_button.setEnabled(true); } else { move_up_button.setEnabled(false); } if (i < size-1){ move_down_button.setEnabled(true); } else { move_down_button.setEnabled(false); } // Need to fill in the rest of the bits updateControlsWithSelectedIndex(); } } private class SubcollectionIndexListCellRenderer extends DefaultListCellRenderer { /** Return a component that has been configured to display the specified value. */ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel component = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (default_index != null && default_index.equals(value)) { component.setText(component.getText() + " " + Dictionary.get("CDM.SubcollectionIndexManager.Default_Partition_Indicator")); } return component; } } } }