/** *######################################################################### * * 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 javax.swing.table.*; 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.DesignPaneHeader; import org.greenstone.gatherer.gui.GLIButton; import org.greenstone.gatherer.util.StaticStrings; import org.greenstone.gatherer.util.Utility; /** This class provides a graphical interface to allow a user to quickly and conviently (ie all in one place) translate the text fragments associated with general metadata and indexes into each of the assigned languages in the collection. It should provide clear controls for the editing of these text fragments, plus clear indicate what languages still need further translation, which it will do through a combination of coloring and other visual indicators. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3 */ public class TranslationView { private Control controls; static final private Dimension COMPONENT_SIZE = new Dimension(225,25); static final private int LANGUAGE_WIDTH = 75; static final private String GENERAL_STR = "General:"; static final private String INDEX_STR = "Index:"; static final private String LEVEL_STR = "Level:"; static final private String PARTITION_STR = "Partitions:"; public TranslationView() { DebugStream.println("TranslationView: Initialized."); } public void destroy() { if(controls != null) { controls.destroy(); controls = null; } } public Control getControls() { if(controls == null) { controls = new TranslationControl(); } return controls; } /** 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 Object[] getFeaturesList() { ArrayList features_model = new ArrayList(); // Add the indexes ArrayList indexes = CollectionDesignManager.index_manager.getIndexes(); int indexes_size = indexes.size(); for(int j = 0; j < indexes_size; j++) { Object bob = new BobTheMagicalComparableWrapper(indexes.get(j)); if(!features_model.contains(bob)) { features_model.add(bob); } } // Add the levels, if any ArrayList levels = CollectionDesignManager.index_manager.getLevels(); if (levels !=null) { int levels_size = levels.size(); for (int j=0; j0){ features_list.setSelectedIndex(0); } JPanel fragment_selection_panel = new JPanel(); fragment_selection_panel.setComponentOrientation(Dictionary.getOrientation()); JLabel fragment_label = new JLabel(Dictionary.get("CDM.TranslationManager.Assigned_Fragments")); fragment_label.setComponentOrientation(Dictionary.getOrientation()); fragment_table = new JTable(fragment_table_model); fragment_table.setComponentOrientation(Dictionary.getOrientation()); fragment_table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); fragment_table.setBackground(Configuration.getColor("coloring.collection_tree_background", false)); fragment_table.setColumnSelectionAllowed(false); fragment_table.setDefaultRenderer(Object.class, new FragmentTableRenderer()); fragment_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane table_scroll = new JScrollPane(fragment_table); table_scroll.getViewport().setBackground(Configuration.getColor("coloring.collection_tree_background", false)); table_scroll.setOpaque(true); JPanel south_panel = new JPanel(); south_panel.setComponentOrientation(Dictionary.getOrientation()); JPanel text_panel = new JPanel(); text_panel.setComponentOrientation(Dictionary.getOrientation()); JPanel language_panel = new JPanel(); language_panel.setComponentOrientation(Dictionary.getOrientation()); JLabel language_label = new JLabel(Dictionary.get("CDM.TranslationManager.Language")); language_label.setComponentOrientation(Dictionary.getOrientation()); language_combobox = new JComboBox(CollectionDesignManager.language_manager.getLanguageCodes().toArray()); language_combobox.setOpaque(!Utility.isMac()); language_combobox.setPreferredSize(COMPONENT_SIZE); language_combobox.setRenderer(new LanguageListCellRenderer()); language_combobox.setToolTipText(Dictionary.get("CDM.TranslationManager.Language_Tooltip")); language_combobox.setComponentOrientation(Dictionary.getOrientation()); JPanel default_text_panel = new JPanel(); default_text_panel.setComponentOrientation(Dictionary.getOrientation()); JLabel default_label = new JLabel(Dictionary.get("CDM.TranslationManager.Default_Text")); default_label.setComponentOrientation(Dictionary.getOrientation()); default_area = new JTextArea(); default_area.setComponentOrientation(Dictionary.getOrientation()); default_area.setBackground(Configuration.getColor("coloring.collection_tree_background", false)); default_area.setEditable(false); default_area.setLineWrap(true); default_area.setWrapStyleWord(true); JPanel translated_text_panel = new JPanel(); translated_text_panel.setComponentOrientation(Dictionary.getOrientation()); JLabel translation_label = new JLabel(Dictionary.get("CDM.TranslationManager.Translation")); translation_label.setComponentOrientation(Dictionary.getOrientation()); translation_area = new JTextArea(); translation_area.setComponentOrientation(Dictionary.getOrientation()); translation_area.setBackground(Configuration.getColor("coloring.disabled", false)); translation_area.setEnabled(false); translation_area.setLineWrap(true); translation_area.setWrapStyleWord(true); translation_area.setToolTipText(Dictionary.get("CDM.TranslationManager.Translation_Tooltip")); JPanel button_pane = new JPanel(); button_pane.setComponentOrientation(Dictionary.getOrientation()); add_button = new GLIButton(Dictionary.get("CDM.TranslationManager.Add"), Dictionary.get("CDM.TranslationManager.Add_Tooltip")); add_button.setEnabled(false); replace_button = new GLIButton(Dictionary.get("CDM.TranslationManager.Replace"), Dictionary.get("CDM.TranslationManager.Replace_Tooltip")); replace_button.setEnabled(false); remove_button = new GLIButton(Dictionary.get("CDM.TranslationManager.Remove"), Dictionary.get("CDM.TranslationManager.Remove_Tooltip")); remove_button.setEnabled(false); // Connection add_button.addActionListener(new AddListener()); remove_button.addActionListener(new RemoveListener()); replace_button.addActionListener(new ReplaceListener()); language_combobox.addActionListener(new LanguageActionListener()); translation_area.getDocument().addDocumentListener(new TranslationDocumentListener()); features_list.addListSelectionListener(new FeaturesListSelectionListener()); fragment_table.getSelectionModel().addListSelectionListener(new FragmentListSelectionListener()); // Layout component_selection_panel.setBorder(BorderFactory.createEmptyBorder(5,0,0,0)); component_selection_panel.setLayout(new BorderLayout()); component_selection_panel.add(component_label, BorderLayout.NORTH); component_selection_panel.add(new JScrollPane(features_list), BorderLayout.CENTER); fragment_selection_panel.setLayout(new BorderLayout()); fragment_selection_panel.add(fragment_label, BorderLayout.NORTH); fragment_selection_panel.add(table_scroll, BorderLayout.CENTER); selection_panel.setLayout(new GridLayout(2,1,0,5)); selection_panel.add(component_selection_panel); selection_panel.add(fragment_selection_panel); default_text_panel.setLayout(new BorderLayout()); default_text_panel.add(default_label, BorderLayout.NORTH); default_text_panel.add(new JScrollPane(default_area), BorderLayout.CENTER); translated_text_panel.setLayout(new BorderLayout()); translated_text_panel.add(translation_label, BorderLayout.NORTH); translated_text_panel.add(new JScrollPane(translation_area), BorderLayout.CENTER); text_panel.setLayout(new GridLayout(1,2,5,0)); text_panel.add(default_text_panel); text_panel.add(translated_text_panel); language_panel.setLayout(new BorderLayout(5,0)); language_panel.add(language_label, BorderLayout.LINE_START); language_panel.add(language_combobox, BorderLayout.CENTER); south_panel.setLayout(new BorderLayout()); south_panel.add(language_panel, BorderLayout.NORTH); south_panel.add(text_panel, BorderLayout.CENTER); center_panel.setLayout(new GridLayout(2,1,0,5)); center_panel.add(selection_panel); center_panel.add(south_panel); button_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); button_pane.setLayout(new GridLayout(1,3)); button_pane.add(add_button); button_pane.add(replace_button); button_pane.add(remove_button); setBorder(BorderFactory.createEmptyBorder(0,5,0,0)); setLayout(new BorderLayout()); add(header_panel, BorderLayout.NORTH); add(center_panel, BorderLayout.CENTER); add(button_pane, BorderLayout.SOUTH); } public void destroy() { } public void gainFocus() { // Rebuild the features model, just incase features have been added or removed. if(features_list != null) { Object selected_feature = features_list.getSelectedValue(); features_list.setListData(getFeaturesList()); if(selected_feature != null) { features_list.setSelectedValue(selected_feature, true); } } } public void loseFocus() { } private class AddListener implements ActionListener { public void actionPerformed(ActionEvent event) { ignore_event = true; String translation_text = translation_area.getText(); if(translation_text.length() > 0) { // If there is no current fragment table selection, but translation text is not an empty string, then this is a new fragment. Create new collection metadata, refresh the fragment table, then ensure that the new metadata is selected. Remember that it is the selected metadata to which changes will be applied. BobTheMagicalComparableWrapper selected_feature = (BobTheMagicalComparableWrapper)features_list.getSelectedValue(); String language = (String) language_combobox.getSelectedItem(); CollectionMeta metadatum = new CollectionMeta(selected_feature.getName(), language); metadatum.setValue(translation_text); CollectionDesignManager.collectionmeta_manager.addMetadatum(metadatum); fragment_table.clearSelection(); ArrayList metadata = CollectionDesignManager.collectionmeta_manager.getMetadata(selected_feature.getName()); fragment_table_model.setData(selected_feature.getName(), CollectionDesignManager.collectionmeta_manager.getLanguages(), metadata); int index = fragment_table_model.getMetadataIndexByLanguage(language); ///ystem.err.println("I want to select the row " + index + " out of " + metadata.size()); fragment_table.setRowSelectionInterval(index, index); metadatum = null; language = null; selected_feature = null; translation_text = null; remove_button.setEnabled(true); } add_button.setEnabled(false); replace_button.setEnabled(false); ignore_event = false; } } private class RemoveListener implements ActionListener { public void actionPerformed(ActionEvent event) { ignore_event = true; int index = -1; if((index = fragment_table.getSelectedRow()) != -1) { CollectionMeta metadata = fragment_table_model.getMetadata(index); CollectionDesignManager.collectionmeta_manager.removeMetadata(metadata); fragment_table_model.remove(index); // If a remove occured then enable add add_button.setEnabled(true); } // Either way disable replace and remove replace_button.setEnabled(false); remove_button.setEnabled(false); ignore_event = false; } } private class ReplaceListener implements ActionListener { public void actionPerformed(ActionEvent event) { ignore_event = true; int index = -1; if((index = fragment_table.getSelectedRow()) != -1) { String translation_text = translation_area.getText(); // Get the selected collection metadata CollectionMeta metadata = fragment_table_model.getMetadata(index); // Update the fragment metadata and ask the table to repaint the appropriate row metadata.setValue(translation_text); fragment_table_model.fireTableRowsUpdated(index, index); metadata = null; remove_button.setEnabled(true); } // Either way disable replace add_button.setEnabled(false); replace_button.setEnabled(false); ignore_event = false; } } private class FeaturesListSelectionListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent event) { if(!event.getValueIsAdjusting() && !ignore_event) { ///ystem.err.println("FeaturesListSelectionListener"); if(!features_list.isSelectionEmpty()) { // Determine the features name. Remember to remove our helpful source prefix (delimited by ':'). BobTheMagicalComparableWrapper selected_feature = (BobTheMagicalComparableWrapper)features_list.getSelectedValue(); // Retrieve the default language translation, or otherwise the first match, to be default text. CollectionMeta default_metadata = CollectionDesignManager.collectionmeta_manager.getMetadatum(selected_feature.getName()); if(default_metadata != null) { default_area.setText(default_metadata.getValue(CollectionMeta.TEXT)); } // Update the text fragment table. fragment_table.clearSelection(); fragment_table_model.setData(selected_feature.getName(), CollectionDesignManager.collectionmeta_manager.getLanguages(), CollectionDesignManager.collectionmeta_manager.getMetadata(selected_feature.getName())); selected_feature = null; // Now we check whatever value is currently selected in the combobox to see if it is valid to add. String language_name = (String) language_combobox.getSelectedItem(); int index = fragment_table_model.getMetadataIndexByLanguage(language_name); if(index != -1) { CollectionMeta metadata = fragment_table_model.getMetadata(index); fragment_table.setRowSelectionInterval(index, index); if(metadata != null) { translation_area.setText(metadata.getValue(CollectionMeta.TEXT)); } else { translation_area.setText(""); } metadata = null; } else { translation_area.setText(""); } // Update and enable the text area translation_area.setEnabled(true); translation_area.setBackground(Configuration.getColor("coloring.editable_background", false)); } else { default_area.setText(""); fragment_table.clearSelection(); fragment_table_model.setData("", new TreeSet(), new ArrayList()); translation_area.setText(""); // Update and disable the text area translation_area.setText(""); translation_area.setEnabled(false); translation_area.setBackground(Configuration.getColor("coloring.disabled", false)); } } } } private class FragmentListSelectionListener implements ListSelectionListener { public void valueChanged(ListSelectionEvent event) { if(!event.getValueIsAdjusting() && !ignore_event) { ignore_event = true; ///ystem.err.println("FragmentListSelectionListener"); int index = -1; if((index = fragment_table.getSelectedRow()) != -1) { // A row has been selected. Retrieve the collection metadata. CollectionMeta metadatum = fragment_table_model.getMetadata(index); if(metadatum != null) { // Update the combobox to show the current language. String language = metadatum.getLanguage(); ///ystem.err.println("Searching for the language: " + language_name); for(int i = 0; i < language_combobox.getItemCount(); i++) { if(language_combobox.getItemAt(i).toString().equals(language)) { language_combobox.setSelectedIndex(i); } } translation_area.setText(metadatum.getValue(CollectionMeta.TEXT)); remove_button.setEnabled(!metadatum.isDummy()); } else { remove_button.setEnabled(false); } } else { translation_area.setText(""); remove_button.setEnabled(false); } ignore_event = false; } } } private class FragmentTableModel extends AbstractTableModel { private ArrayList metadata; private String feature_name; private TreeSet languages; FragmentTableModel(String feature_name, TreeSet languages, ArrayList metadata) { this.feature_name = feature_name; this.languages = languages; this.metadata = metadata; } public int getRowCount() { // The row count is equal to the maximum number of languages currently assigned in the collection. return languages.size(); } public int getColumnCount() { return 2; } public String getColumnName(int column) { if (column == 0) { return Dictionary.get("CDM.TranslationManager.Language_Column"); } if (column == 1) { return Dictionary.get("CDM.TranslationManager.Fragment_Column"); } return ""; } /** Attempt to retrieve the metadata associated with a certain row - however not all rows actually have metadata with them (say the French row where no metadata has been set). * @param row the row number as an int * @return the CollectionMeta requested, which may be a dummy metadata pair */ public CollectionMeta getMetadata(int row) { // Determine what language we are talking about String language = null; Iterator language_iterator = languages.iterator(); int current_row = 0; while(current_row != row && language_iterator.hasNext()) { language_iterator.next(); current_row++; } if(current_row == row) { language = (String) language_iterator.next(); return getMetadata(language); } language_iterator = null; return null; } public CollectionMeta getMetadata(String language) { // Attempt to retrieve metadata with that language for(int i = 0; i < metadata.size(); i++) { CollectionMeta metadatum = (CollectionMeta) metadata.get(i); if(metadatum.getLanguage().equals(language)) { return metadatum; } } return new CollectionMeta(feature_name, language, true); } public int getMetadataIndexByLanguage(String language) { ///ystem.err.println("Find the index for the language " + language + " (out of " + languages.size() + ")"); // First we have to iterate to the correct place Iterator language_iterator = languages.iterator(); int current_row = 0; while(language_iterator.hasNext()) { String target = (String)language_iterator.next(); if(language.equals(target)) { ///ystem.err.println("Matches " + target); return current_row; } else { ///ystem.err.println("Doesn't match " + target); current_row++; } } ///ystem.err.println("No such language in model: -1"); return -1; } public Object getValueAt(int row, int column) { if(0 <= row && row < languages.size()) { // First we have to iterate to the correct place Iterator language_iterator = languages.iterator(); int current_row = 0; while(current_row != row && language_iterator.hasNext()) { language_iterator.next(); current_row++; } if(current_row == row) { String language = (String)language_iterator.next(); if(column == 0) { return CollectionDesignManager.language_manager.getLanguageName(language); } else { CollectionMeta metadatum = getMetadata(language); return metadatum.getValue(CollectionMeta.TEXT); } } language_iterator = null; } return "#Error"; } public void remove(int index) { metadata.remove(index); fireTableRowsDeleted(index, index); } public void setData(String feature_name, TreeSet languages, ArrayList metadata) { this.feature_name = feature_name; this.languages = languages; this.metadata = metadata; fireTableDataChanged(); } public int size() { return metadata.size(); } } private class FragmentTableRenderer extends DefaultTableCellRenderer { /** Retrieve the component used to rubberstamp the table based on the given parameters. * @param table The JTable to rendered. * @param value The Object whose representation will be shown in the table row. * @param isSelected true iff the column/row is selected. * @param hasFocus true iff the column/row is in focus. * @param row An int specifying the target row. * @param column An int specifying the target column. * @see org.greenstone.gatherer.cdm.Language */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JComponent component = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if(isSelected) { component.setBackground(Configuration.getColor("coloring.workspace_heading_background", false)); } else { component.setBackground(Configuration.getColor("coloring.collection_tree_background", false)); } return component; } } private class LanguageActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { if(!ignore_event && features_list.getSelectedIndex()>-1) { ignore_event = true; ///ystem.err.println("LanguageActionListener"); // If the newly selected language value already exists in the fragment table, and that row isn't the one selected, then select that row the text area. String language_name = language_combobox.getSelectedItem().toString(); int index = fragment_table_model.getMetadataIndexByLanguage(language_name); if(index != -1) { CollectionMeta metadata = fragment_table_model.getMetadata(index); fragment_table.setRowSelectionInterval(index, index); translation_area.setText(metadata.getValue(CollectionMeta.TEXT)); } else { fragment_table.clearSelection(); translation_area.setText(""); } // Ready the text area translation_area.setEnabled(true); translation_area.setBackground(Configuration.getColor("coloring.editable_background", false)); ignore_event = false; } } } private class TranslationDocumentListener implements DocumentListener { /** Gives notification that an attribute or set of attributes changed. */ public void changedUpdate(DocumentEvent e) { update(); } /** Gives notification that there was an insert into the document. */ public void insertUpdate(DocumentEvent e) { update(); } /** Gives notification that a portion of the document has been removed. */ public void removeUpdate(DocumentEvent e) { update(); } /** The text area has changed in some way. Given that this can only happed when we are editing or adding a text fragment we better respond appropriately. */ private void update() { String translation_text = translation_area.getText(); // Determine if add should be enabled. You can only add if the current text fragment doesn't already exist in the fragment table for the given language String language = (String) language_combobox.getSelectedItem(); CollectionMeta metadatum = fragment_table_model.getMetadata(language); add_button.setEnabled(translation_text.length() > 0 && (metadatum.isDummy() || fragment_table_model.getMetadataIndexByLanguage(language) == -1)); language = null; // Determine if replace should be enabled. Replace is only enabled it the text fragment is different from the one in the current fragment table selection. if(fragment_table.getSelectedRowCount() > 0) { replace_button.setEnabled(!metadatum.isDummy() && !translation_text.equals(fragment_table.getValueAt(fragment_table.getSelectedRow(), 1))); } // Nothing selected, nothing to replace else { replace_button.setEnabled(false); } translation_text = null; } } } }