/** *######################################################################### * * 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.io.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; 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.gui.GLIButton; import org.greenstone.gatherer.metadata.MetadataElement; import org.greenstone.gatherer.metadata.MetadataSetManager; import org.greenstone.gatherer.util.CheckList; import org.greenstone.gatherer.util.CheckListEntry; import org.greenstone.gatherer.util.JarTools; import org.greenstone.gatherer.util.StaticStrings; import org.greenstone.gatherer.util.XMLTools; import org.w3c.dom.*; /** This class manages the language commands, remembering both a list of languages to build indexes in, plus the default language. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ public class LanguageManager extends DOMProxyListModel { static public Document LANGUAGES_DOCUMENT = XMLTools.parseXMLFile("xml/languages.xml", true); static final private Dimension COMPONENT_SIZE = new Dimension(125,25); /** The visual controls for this manager. */ private Control controls = null; /** A reference to this class as a model, for the inner controls class. */ private DOMProxyListModel model = null; /** A hashtable of code->name mappings of known languages. */ private LinkedHashMap known_languages = null; /** The default language object. */ private Language default_language = null; /** The language metadata element - specifies which metadata the language should be read from to determine the partitons */ private Element language_metadata = null; /** Constructor. */ public LanguageManager(Element languages_element) { super(languages_element, StaticStrings.LANGUAGE_ELEMENT, new Language()); DebugStream.println("LanguageManager: " + getSize() + " languages parsed."); this.model = this; // Retrieve the default language NodeList default_language_elements = CollectionConfiguration.getElementsByTagName(StaticStrings.LANGUAGE_DEFAULT_ELEMENT); if(default_language_elements.getLength() > 0) { default_language = new Language((Element)default_language_elements.item(0)); } // Retrieve the language metadata language_metadata = CollectionDesignManager.collect_config.getLanguageMetadata(); // Load a series of code->language mappings into known_languages, by reading from the 'languages.xml' file, which is essentially a subset of the ISO 639 Standard. known_languages = new LinkedHashMap(); NodeList language_elements = LANGUAGES_DOCUMENT.getDocumentElement().getElementsByTagName(StaticStrings.LANGUAGE_ELEMENT); for(int i = 0; i < language_elements.getLength(); i++) { Element language_element = (Element) language_elements.item(i); String code = language_element.getAttribute(StaticStrings.CODE_ATTRIBUTE); String name = language_element.getAttribute(StaticStrings.NAME_ATTRIBUTE); known_languages.put(code.toLowerCase(), name); name = null; code = null; language_element = null; } } /** Method to add a new language. * @param language The Language to add. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ private void addLanguage(Language language) { if(!contains(language)) { // need to add a pseudo metadata CollectionMeta metadatum = new CollectionMeta(StaticStrings.STOP_CHARACTER + language.getCode()); metadatum.setValue(language.getName()); CollectionDesignManager.collectionmeta_manager.addMetadatum(metadatum); add(getSize(), language); } } public void destroy() { if(controls != null) { controls.destroy(); controls = null; } known_languages.clear(); known_languages = null; default_language = null; } /** Method to retrieve the control for this manager. * @return the Control for editing the language partitions */ public Control getControls() { if(controls == null) { // Build controls controls = new LanguageControl(); } return controls; } /** Method to retrieve a certain language object by its code. * @param code The two letter code of a language, as a String. * @return The Language that matches the given code, or null if no such language exists. */ public Language getLanguage(String code) { int size = getSize(); for(int i = 0; i < size; i++) { Language language = (Language) getElementAt(i); if(language.getCode().equals(code)) { return language; } } return null; } public ArrayList getLanguages() { return children(); } /** Method to return a list of the known language codes. * @return an ArrayList containing the series of known language codes as per the languages.dat file */ public ArrayList getLanguageCodes() { return new ArrayList(known_languages.keySet()); } public String getLanguageName(String code) { return (String) known_languages.get(code); } /** 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 moveLanguage(Language lang, boolean move_up) { // Determine the current position of the language int position = indexOf(lang); int new_position; // Attempt to move the language up if (move_up) { // Check it's not already at the top if (position == 0) { return position; } // This automatically removes the language first, as an Element can only exist once in a particular document new_position = position - 1; addBefore(lang, (Language) getElementAt(new_position)); } // Attempt to move the language down else { // Check it's not already at the bottom if (position == (getSize()) - 1) { return position; } // This automatically removes the language first, as an Element can only exist once in a particular document new_position = position + 1; addAfter(lang, (Language) getElementAt(new_position)); } return new_position; } /** Method to remove a certain language. * @param language The Language to remove. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ private void removeLanguage(Language language) { remove(language); // Remove any collection metadata for this language CollectionDesignManager.collectionmeta_manager.removeMetadata(StaticStrings.STOP_CHARACTER + language.getCode()); if(default_language != null && default_language.equals(language)) { setDefault(null); } } private void replaceLanguage(Language old_language, Language new_language) { // Remove old lang collection meta CollectionDesignManager.collectionmeta_manager.removeMetadata(StaticStrings.STOP_CHARACTER + old_language.getCode()); // Add new one CollectionMeta metadatum = new CollectionMeta(StaticStrings.STOP_CHARACTER + new_language.getCode()); metadatum.setValue(new_language.getName()); CollectionDesignManager.collectionmeta_manager.addMetadatum(metadatum); if(default_language != null && default_language.equals(old_language)) { setDefault(new_language); } // get the position of the old one int position = indexOf(old_language); remove(old_language); add(position, new_language); } /** Method to set the default language. * @param language The Language to use as a default, or null for no default. * @see org.greenstone.gatherer.Gatherer * @see org.greenstone.gatherer.collection.CollectionManager */ public void setDefault(Language language) { if(language != null) { if(default_language == null) { // Create the default index element, and place immediately after indexes element. Element default_language_element = root.getOwnerDocument().createElement(StaticStrings.LANGUAGE_DEFAULT_ELEMENT); default_language = new Language(default_language_element); Node target_node = CollectionConfiguration.findInsertionPoint(default_language_element); if(target_node != null) { root.getOwnerDocument().getDocumentElement().insertBefore(default_language_element, target_node); } else { root.getOwnerDocument().getDocumentElement().appendChild(default_language_element); } } default_language.setAssigned(true); default_language.setCode(language.getCode()); } else { if(default_language != null) { default_language.setAssigned(false); } } } /** This class represents the visual component of the Language Manager. */ private class LanguageControl extends JPanel implements Control { /** The list of available languages */ private CheckList language_list = null; /** The button to add a new language support. */ private JButton add_button = null; /** The button to replace a language support. */ private JButton replace_button = null; /** The button to remove a supported language. */ private JButton remove_button = null; /** button to move a language up in the list */ private JButton move_down_button; /** button to move a language down in the list */ private JButton move_up_button; /** button to select all the languages in the list */ private JButton select_all_button; /** button to clear the current selection in the list */ private JButton select_none_button; /** The button to set the current language as the default one. */ private JButton set_default_button = null; /** A list of currently supported languages. */ private JList selected_languages_list = null; /** A list of metadata elements that may hold the language metadata */ private JComboBox language_metadata_combo = null; /** Constructor. * @see org.greenstone.gatherer.cdm.LanguageManager.LanguageControl.AddListener * @see org.greenstone.gatherer.cdm.LanguageManager.LanguageControl.ClearDefaultListener * @see org.greenstone.gatherer.cdm.LanguageManager.LanguageControl.ListListener * @see org.greenstone.gatherer.cdm.LanguageManager.LanguageControl.RemoveListener * @see org.greenstone.gatherer.cdm.LanguageManager.LanguageControl.SelectorListener * @see org.greenstone.gatherer.cdm.LanguageManager.LanguageControl.SetDefaultListener */ public LanguageControl() { super(); // Creation. JPanel center_panel = new JPanel(); JLabel selected_languages_list_label = new JLabel(Dictionary.get("CDM.LanguageManager.Assigned_Languages")); selected_languages_list = new JList(model); selected_languages_list.setCellRenderer(new MyLanguageListCellRenderer()); selected_languages_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); selected_languages_list.setVisibleRowCount(5); JPanel control_panel = new JPanel(); JPanel labels_pane = new JPanel(); JLabel selector_label = new JLabel(Dictionary.get("CDM.LanguageManager.Selector")); 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")); language_metadata_combo = new JComboBox(MetadataSetManager.getEveryMetadataSetElement().toArray()); language_metadata_combo.setOpaque(false); language_metadata_combo.setToolTipText(Dictionary.get("CDM.LanguageManager.LanguageMetadata_Tooltip")); JLabel language_metadata_label = new JLabel(Dictionary.get("CDM.LanguageManager.LanguageMetadata")); String current_value = "ex.Language"; if (language_metadata.getAttribute(StaticStrings.ASSIGNED_ATTRIBUTE).equals(StaticStrings.TRUE_STR) && !language_metadata.getAttribute(StaticStrings.NAME_ATTRIBUTE).equals("")) { current_value = language_metadata.getAttribute(StaticStrings.NAME_ATTRIBUTE); } ArgumentControl.selectValue(language_metadata_combo, current_value); language_list = new CheckList(false); language_list.setListData(getLanguageCodes()); language_list.setToolTipText(Dictionary.get("CDM.LanguageManager.Selector_Tooltip")); language_list.setCellRenderer(new LanguageCheckListCellRenderer()); select_all_button.setEnabled(isSelectAllEnabled()); select_none_button.setEnabled(isSelectAllEnabled()); 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.LanguageManager.Set_Default"), Dictionary.get("CDM.LanguageManager.Set_Default_Tooltip")); set_default_button.setEnabled(false); JPanel button_panel = new JPanel(); add_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Add_Subindex"), Dictionary.get("CDM.LanguageManager.Add_Tooltip")); add_button.setEnabled(false); replace_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Replace_Subindex"), Dictionary.get("CDM.LanguageManager.Replace_Tooltip")); replace_button.setEnabled(false); remove_button = new GLIButton(Dictionary.get("CDM.SubcollectionIndexManager.Remove_Subindex"), Dictionary.get("CDM.LanguageManager.Remove_Tooltip")); remove_button.setEnabled(false); // Set up and connect 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); language_list.addListSelectionListener(new LanguageListListener()); language_metadata_combo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { String value = ((MetadataElement)language_metadata_combo.getSelectedItem()).getFullName(); language_metadata.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.TRUE_STR); language_metadata.setAttribute(StaticStrings.NAME_ATTRIBUTE, value); } }); set_default_button.addActionListener(new SetDefaultListener()); set_default_button.addActionListener(CollectionDesignManager.buildcol_change_listener); selected_languages_list.addListSelectionListener(new AssignedListListener()); // Layout components button_panel.setLayout(new GridLayout(1,3)); button_panel.add(add_button); button_panel.add(replace_button); button_panel.add(remove_button); JPanel metadata_panel = new JPanel(); metadata_panel.setLayout(new BorderLayout()); JPanel inner_panel = new JPanel(); inner_panel.setLayout(new BorderLayout(10,10)); inner_panel.add(language_metadata_label, BorderLayout.WEST); inner_panel.add(language_metadata_combo, BorderLayout.CENTER); metadata_panel.add(new JPanel(), BorderLayout.CENTER); metadata_panel.add(inner_panel, BorderLayout.EAST); 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); control_panel.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); control_panel.setLayout(new BorderLayout()); 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(selector_label, c); labels_pane.add(selector_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); control_panel.add(labels_pane, BorderLayout.WEST); control_panel.add(new JScrollPane(language_list), BorderLayout.CENTER); control_panel.add(button_panel, BorderLayout.SOUTH); center_panel.setLayout(new BorderLayout()); center_panel.add(selected_languages_list_label, BorderLayout.NORTH); center_panel.add(new JScrollPane(selected_languages_list), BorderLayout.CENTER); center_panel.add(movement_pane, BorderLayout.EAST); JPanel top_panel = new JPanel(); top_panel.setLayout(new BorderLayout()); top_panel.add(metadata_panel, BorderLayout.NORTH); top_panel.add(center_panel, BorderLayout.SOUTH); setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setLayout(new BorderLayout()); add(top_panel, BorderLayout.NORTH); add(control_panel, BorderLayout.CENTER); } /** Destructor. */ public void destroy() { } public void gainFocus() { } public void loseFocus() { } private void clearControls() { selected_languages_list.clearSelection(); language_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 void updateControlsWithSelectedLanguage() { Language selected_lang = (Language) selected_languages_list.getSelectedValue(); if (selected_lang == null) { clearControls(); return; } // Display the selected subcollection index's sources language_list.clearTicked(); // for multi-languages indexes, getCode returns null if(selected_lang.getCode()==null){ return; } language_list.setTickedObjects(selected_lang.getCode().split(",")); } private void validateButtons() { boolean add_enabled = false; boolean replace_enabled = false; if (!language_list.isNothingTicked()) { // Create a dummy Langauge and see if its in the collection ArrayList langs = language_list.getTicked(); StringBuffer code_str = new StringBuffer(); boolean first = true; for (int i=0; iActionEvent. * @see org.greenstone.gatherer.cdm.Language */ public void actionPerformed(ActionEvent event) { Language delete_me = (Language)selected_languages_list.getSelectedValue(); if(delete_me != null) { removeLanguage(delete_me); } } } private class ReplaceListener implements ActionListener { public void actionPerformed(ActionEvent event) { if (selected_languages_list.isSelectionEmpty() || language_list.isNothingTicked()) { // This should never happen, but just in case... replace_button.setEnabled(false); return; } Language old_language = (Language) selected_languages_list.getSelectedValue(); Language new_language = new Language(language_list.getTicked()); replaceLanguage(old_language, new_language); } } private class LanguageListListener 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()){ language_list.setAllTicked(); validateButtons(); } } } private class SelectNoneListener implements ActionListener { public void actionPerformed(ActionEvent event){ if(select_all_button.isEnabled()){ language_list.clearTicked(); validateButtons(); } } } /** Listens for actions apon the 'set default' button in the LanguageManager controls, and if detected calls the setDefault() method of the manager with the language selected for default. */ private class SetDefaultListener implements ActionListener { /** Set the default index to the one currently selected, if any. * @param event An ActionEvent. * @see org.greenstone.gatherer.cdm.Language */ public void actionPerformed(ActionEvent event) { Language selected_language = (Language) selected_languages_list.getSelectedValue(); if(selected_language != null) { setDefault(selected_language); // This should cause a repaint of just the desired row selected_languages_list.setSelectedValue(selected_language, true); } set_default_button.setEnabled(false); } } 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 language Language language = (Language) selected_languages_list.getSelectedValue(); if (language != null) { int new_position = moveLanguage(language, move_up); // Ensure the language that moved is still selected selected_languages_list.setSelectedIndex(new_position); } } } /** Listens for selections within the list on the LanguageManager controls, and if a change is detected enables, or disables, controls appropriately. */ private class AssignedListListener implements ListSelectionListener { /** Enable or disable controls depending on the current list selection. * @param event A ListSelectionEvent. */ public void valueChanged(ListSelectionEvent event) { if (event.getValueIsAdjusting()) { return; } if(selected_languages_list.isSelectionEmpty()) { clearControls(); return; } int i = selected_languages_list.getSelectedIndex(); int size = selected_languages_list.getModel().getSize(); select_all_button.setEnabled(isSelectAllEnabled()); select_none_button.setEnabled(isSelectAllEnabled()); Language selected_lang = (Language)selected_languages_list.getSelectedValue(); remove_button.setEnabled(true); replace_button.setEnabled(false); add_button.setEnabled(false); set_default_button.setEnabled(default_language == null || !default_language.equals(selected_lang)); 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); } updateControlsWithSelectedLanguage(); } } private class MyLanguageListCellRenderer 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_language != null && default_language.equals(value)) { component.setText(component.getText() + " [" + Dictionary.get("CDM.LanguageManager.Default_Language")+"]"); } return component; } } private boolean isSelectAllEnabled(){ return language_list.getModel().getSize() > 0 ? true : false; } } /** A custom list cell renderer for producing rows which contain clickable check boxes. */ private class LanguageCheckListCellRenderer implements ListCellRenderer { /** Return a component that has been configured to display the specified value. That component's paint method is then called to "render" the cell. If it is necessary to compute the dimensions of a list because the list cells do not have a fixed size, this method is called to generate a component on which getPreferredSize can be invoked. * @param list The JList we're painting. * @param value The value returned by list.getModel().getElementAt(index), as an Object. * @param index The cells index as an int. * @param is_selected true if the specified cell was selected, false otherwise. * @param cell_has_focus true if and only if the specified cell has the focus. * @return A Component whose paint() method will render the specified value. */ public Component getListCellRendererComponent(JList list, Object value, int index, boolean is_selected, boolean cell_has_focus) { JCheckBox checkbox = (JCheckBox) value; checkbox.setBackground(list.getBackground()); checkbox.setForeground(list.getForeground()); checkbox.setBorderPainted(false); checkbox.setEnabled(list.isEnabled()); checkbox.setFont(list.getFont()); checkbox.setFocusPainted(false); checkbox.setBorder((is_selected) ? UIManager.getBorder("List.focusCellHighlightBorder") : new EmptyBorder(1, 1, 1, 1)); String code = (String)((CheckListEntry)list.getModel().getElementAt(index)).getObject(); checkbox.setText((String)known_languages.get(code)); return checkbox; } } }