/*
 * File:     InterlinearPreviewDlg.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package mpi.eudico.client.annotator.interlinear;

import mpi.eudico.client.annotator.Constants;
import mpi.eudico.client.annotator.ElanLocale;
import mpi.eudico.client.annotator.Preferences;

import mpi.eudico.client.annotator.commands.Command;
import mpi.eudico.client.annotator.commands.ELANCommandFactory;
import mpi.eudico.client.annotator.commands.PrintCommand;

import mpi.eudico.client.annotator.gui.TextExportFileChooser;

import mpi.eudico.client.annotator.tier.TierExportTableModel;

import mpi.eudico.client.annotator.util.ElanFileFilter;
import mpi.eudico.client.annotator.util.FileExtension;

import mpi.eudico.client.util.CheckBoxTableCellRenderer;
import mpi.eudico.client.util.TierSorter;

import mpi.eudico.server.corpora.clomimpl.abstr.TierImpl;

import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import java.io.File;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

//import javax.print.*;
//import javax.print.attribute.*;
//import javax.print.attribute.standard.*;
//import javax.print.event.*;
import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;


/**
 * A dialog that shows a preview for interlinear printing or text export.
 * Contains numerous ui elements for configuring the output parameters.
 *
 * @author mpi
 */
public class InterlinearPreviewDlg extends JDialog implements ActionListener,
    ListSelectionListener, ItemListener {
    private Interlinear interlinearizer;
    private int curSortMode;

    // ui
    private PreviewPanel previewPanel;
    private JPanel optionsPanel;
    private JPanel whatPanel;
    private JPanel tierSelectionPanel;
    private JPanel howPanel;
    private JPanel buttonPanel;
    private JTable tierTable;
    private TierExportTableModel model;

    // tier panel
    private JButton fontSizesButton;
    private JButton upButton;
    private JButton downButton;

    //private HashMap fontSizes;
    // whatPanel
    private JCheckBox selectionOnlyCheckBox;
    private JCheckBox showTierLabelCheckBox;
    private JCheckBox showTimeCodeCheckBox;
    private JComboBox timeCodeFormatComboBox;
    private JCheckBox showEmptySlotsCheckBox;
    private JRadioButton hideLinesRadioButton;
    private JRadioButton tierTemplateRadioButton;

    // howPanel
    private JLabel widthLabel;
    private JTextField widthTextField;
    private JLabel heightLabel;
    private JTextField heightTextField;
    private JLabel blockWrapLabel;
    private JComboBox blockWrapComboBox;
    private JLabel lineWrapLabel;

    //private JComboBox lineWrapComboBox;
    private JCheckBox lineWrapCheckBox;
    private JLabel sortingLabel;
    private JComboBox sortingComboBox;
    private JLabel lineSpacingLabel;
    private JTextField lineSpacingTextField;
    private JLabel blockSpacingLabel;
    private JTextField blockSpacingTextField;

    // button panel
    private JButton applyChangesButton;
    private JButton printButton;
    private JButton pageSetupButton;

    // localized option strings
    private String tcStyleHhMmSsMs;
    private String tcStyleSsMs;
    private String tcStyleMs;
    private String blockWrapNone;
    private String blockWrapWithinBlock;
    private String blockWrapBlockBoundary;
    private String blockWrapEachBlock;
    private String sortingAsFile;
    private String sortingTierHierarchy;
    private String sortingLinguisticType;
    private String sortingParticipant;

    /** the character encoding for the text file */
    private String charEncoding = "UTF-8";

    /** column id for the include in export checkbox column, invisible */
    private final String PRINT_COLUMN = "export";

    /** column id for the tier name column, invisible */
    private final String TIER_NAME_COLUMN = "tier";

    /** column id for the font size column, invisible */
    private final String FONT_SIZE_COLUMN = "fontsize";

    /**
     * Creates a new InterlinearPreviewDlg instance
     *
     * @param frame the parent frame
     * @param modal the modal property
     * @param interlinearizer the Interlinear object
     */
    public InterlinearPreviewDlg(Frame frame, boolean modal,
        Interlinear interlinearizer) {
        super(frame, modal);
        this.interlinearizer = interlinearizer;

        initComponents();
        setDefaultValues();
        extractTiers();
        doApplyChanges();
        postInit();
    }

    /**
     * Initializes UI elements.
     */
    protected void initComponents() {
        previewPanel = new PreviewPanel(interlinearizer);
        optionsPanel = new JPanel();
        tierSelectionPanel = new JPanel();
        whatPanel = new JPanel();
        howPanel = new JPanel();
        buttonPanel = new JPanel();
        fontSizesButton = new JButton();
        upButton = new JButton();
        downButton = new JButton();

        //fontSizes = new HashMap();
        //	then: other components
        selectionOnlyCheckBox = new JCheckBox();
        showTierLabelCheckBox = new JCheckBox();
        showTimeCodeCheckBox = new JCheckBox();
        timeCodeFormatComboBox = new JComboBox();
        showEmptySlotsCheckBox = new JCheckBox();
        showEmptySlotsCheckBox.setEnabled(false);
        hideLinesRadioButton = new JRadioButton();
        tierTemplateRadioButton = new JRadioButton();

        // components of howPanel
        widthLabel = new JLabel();
        widthTextField = new JTextField(4);
        heightLabel = new JLabel();
        heightTextField = new JTextField(4);
        blockWrapLabel = new JLabel();
        blockWrapComboBox = new JComboBox();
        lineWrapLabel = new JLabel();

        //lineWrapComboBox = new JComboBox();
        lineWrapCheckBox = new JCheckBox();
        sortingLabel = new JLabel();
        sortingComboBox = new JComboBox();
        lineSpacingLabel = new JLabel();
        lineSpacingTextField = new JTextField(2);
        blockSpacingLabel = new JLabel();
        blockSpacingTextField = new JTextField(2);

        // components of buttonPanel
        applyChangesButton = new JButton();
        printButton = new JButton();

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            pageSetupButton = new JButton();
        }

        // tier table and scrollpane, panel
        try {
            ImageIcon upIcon = new ImageIcon(this.getClass().getResource("/toolbarButtonGraphics/navigation/Up16.gif"));
            ImageIcon downIcon = new ImageIcon(this.getClass().getResource("/toolbarButtonGraphics/navigation/Down16.gif"));
            upButton.setIcon(upIcon);
            downButton.setIcon(downIcon);
        } catch (Exception ex) {
            upButton.setText("Up");
            downButton.setText("Down");
        }

        model = new TierExportTableModel();
        model.setColumnIdentifiers(new String[] {
                PRINT_COLUMN, TIER_NAME_COLUMN, FONT_SIZE_COLUMN
            });
        tierTable = new JTable(model);

        Dimension tableDim = new Dimension(50, 100);

        tierTable.getColumn(PRINT_COLUMN).setCellEditor(new DefaultCellEditor(
                new JCheckBox()));
        tierTable.getColumn(PRINT_COLUMN).setCellRenderer(new CheckBoxTableCellRenderer());
        tierTable.getColumn(PRINT_COLUMN).setMaxWidth(30);
        tierTable.getColumn(FONT_SIZE_COLUMN).setMaxWidth(30);
        tierTable.setShowVerticalLines(false);
        tierTable.setTableHeader(null);

        JScrollPane tierScrollPane = new JScrollPane(tierTable);
        tierScrollPane.setPreferredSize(tableDim);

        // layout
        getContentPane().setLayout(new GridBagLayout());
        optionsPanel.setLayout(new GridBagLayout());
        tierSelectionPanel.setLayout(new GridBagLayout());
        whatPanel.setLayout(new GridBagLayout());
        howPanel.setLayout(new GridBagLayout());
        buttonPanel.setLayout(new GridBagLayout());

        GridBagConstraints gbc;
        Insets insets = new Insets(2, 2, 2, 2);

        // add the preview panel
        gbc = new GridBagConstraints();
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        getContentPane().add(previewPanel, gbc);

        // fill and add the tier selection panel
        gbc = new GridBagConstraints();
        gbc.gridwidth = 3;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        tierSelectionPanel.add(tierScrollPane, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        tierSelectionPanel.add(upButton, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        tierSelectionPanel.add(downButton, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.EAST;
        tierSelectionPanel.add(fontSizesButton, gbc);

        gbc = new GridBagConstraints();
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        optionsPanel.add(tierSelectionPanel, gbc);

        // fill and add the "what" panel
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;

        //gbc.gridwidth = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        whatPanel.add(selectionOnlyCheckBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;

        //gbc.gridwidth = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        whatPanel.add(showTierLabelCheckBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;

        //gbc.gridwidth = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        whatPanel.add(showTimeCodeCheckBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 2;

        //gbc.gridwidth = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        whatPanel.add(timeCodeFormatComboBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;

        //gbc.gridwidth = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        whatPanel.add(showEmptySlotsCheckBox, gbc);

        ButtonGroup buttonGroup = new ButtonGroup();
        buttonGroup.add(hideLinesRadioButton);
        buttonGroup.add(tierTemplateRadioButton);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 4;

        //gbc.gridwidth = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        whatPanel.add(hideLinesRadioButton, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 5;

        //gbc.gridwidth = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.WEST;
        whatPanel.add(tierTemplateRadioButton, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        optionsPanel.add(whatPanel, gbc);

        // fill and add "how" panel
        gbc = new GridBagConstraints();
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(widthLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        howPanel.add(widthTextField, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(heightLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        howPanel.add(heightTextField, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(blockWrapLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        howPanel.add(blockWrapComboBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(lineWrapLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;

        //gbc.fill = GridBagConstraints.HORIZONTAL;
        //gbc.weightx = 1.0;	
        //howPanel.add(lineWrapComboBox, gbc);
        howPanel.add(lineWrapCheckBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 4;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(sortingLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 4;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        howPanel.add(sortingComboBox, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 5;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(lineSpacingLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 5;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        howPanel.add(lineSpacingTextField, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 6;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        howPanel.add(blockSpacingLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 6;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        howPanel.add(blockSpacingTextField, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 2;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;
        optionsPanel.add(howPanel, gbc);

        // button panel
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        buttonPanel.add(applyChangesButton, gbc);

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            gbc = new GridBagConstraints();
            gbc.gridx = 1;
            gbc.gridy = 0;
            gbc.insets = insets;
            gbc.anchor = GridBagConstraints.NORTHWEST;
            buttonPanel.add(pageSetupButton, gbc);
        }

        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        buttonPanel.add(printButton, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 3;
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        optionsPanel.add(buttonPanel, gbc);

        // add the options panel
        gbc = new GridBagConstraints();
        gbc.insets = insets;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.fill = GridBagConstraints.VERTICAL;
        gbc.weighty = 1.0;
        getContentPane().add(optionsPanel, gbc);

        // apply localized strings
        updateForLocale();

        // add listeners
        fontSizesButton.addActionListener(this);
        upButton.addActionListener(this);
        downButton.addActionListener(this);
        applyChangesButton.addActionListener(this);
        sortingComboBox.addItemListener(this);

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            printButton.addActionListener(this);
            pageSetupButton.addActionListener(this);
        } else {
            printButton.addActionListener(this);
            fontSizesButton.setEnabled(false);
            lineSpacingTextField.setEnabled(false);
        }

        tierTable.getSelectionModel().addListSelectionListener(this);
        addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent we) {
                    savePreferences();
                    interlinearizer.savePreferences();
                }
            });
    }

    /**
     * Pack, size and set location.
     */
    protected void postInit() {
        pack();

        int w = 550;
        int h = 400;
        setSize((getSize().width < w) ? w : getSize().width,
            (getSize().height < h) ? h : getSize().height);
        setLocationRelativeTo(getParent());

        //setResizable(false);
    }

    /**
     * Initialise some default parameter values.
     */
    private void setDefaultValues() {
        curSortMode = interlinearizer.getSortingStyle();
        selectionOnlyCheckBox.setSelected(interlinearizer.isSelectionOnly());

        showTierLabelCheckBox.setSelected(interlinearizer.isTierLabelsShown());
        showTimeCodeCheckBox.setSelected(interlinearizer.isTimeCodeShown());

        showEmptySlotsCheckBox.setSelected(false);
        showEmptySlotsCheckBox.setEnabled(false);

        if (interlinearizer.getEmptyLineStyle() == Interlinear.HIDE_EMPTY_LINES) {
            hideLinesRadioButton.setSelected(true);
        } else {
            tierTemplateRadioButton.setSelected(true);
        }

        if (interlinearizer.getBlockWrapStyle() == Interlinear.BLOCK_BOUNDARY) {
            blockWrapComboBox.setSelectedItem(blockWrapBlockBoundary);
        } else if (interlinearizer.getBlockWrapStyle() == Interlinear.EACH_BLOCK) {
            blockWrapComboBox.setSelectedItem(blockWrapEachBlock);
        } else if (interlinearizer.getBlockWrapStyle() == Interlinear.NO_WRAP) {
            blockWrapComboBox.setSelectedItem(blockWrapNone);
        } else if (interlinearizer.getBlockWrapStyle() == Interlinear.WITHIN_BLOCKS) {
            blockWrapComboBox.setSelectedItem(blockWrapWithinBlock);
        }

        int initialWidth = interlinearizer.getWidth();
        int initialHeigth = interlinearizer.getHeight();

        if (initialWidth > 0) {
            widthTextField.setText("" + initialWidth);
        } else {
            widthTextField.setText("" + 0);
        }

        if (initialHeigth > 0) {
            heightTextField.setText("" + initialHeigth);
        } else {
            heightTextField.setText("" + 0);
        }

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) { // width and height is determined by page setup, so fix it
            widthTextField.setEnabled(false);
            widthTextField.setBackground(Constants.DEFAULTBACKGROUNDCOLOR);
            heightTextField.setEnabled(false);
            heightTextField.setBackground(Constants.DEFAULTBACKGROUNDCOLOR);
        } else {
            heightTextField.setText("");
            heightTextField.setEnabled(false);
            heightTextField.setBackground(Constants.DEFAULTBACKGROUNDCOLOR);
            lineSpacingTextField.setText("");
            lineSpacingTextField.setBackground(Constants.DEFAULTBACKGROUNDCOLOR);
        }

        lineWrapCheckBox.setSelected(interlinearizer.getLineWrapStyle() == Interlinear.NEXT_LINE);
    }

    /**
     * Extract candidate tiers for export and add them to the table. Take saved
     * preferences into account.
     */
    protected void extractTiers() {
        if (model != null) {
            for (int i = model.getRowCount() - 1; i >= 0; i--) {
                model.removeRow(i);
            }

            if (interlinearizer.getTranscription() != null) {
                Vector v = interlinearizer.getTranscription().getTiers();
                ArrayList visTiers = interlinearizer.getVisibleTiers();
                ArrayList sortedTiers = null;
                ArrayList allTiers = new ArrayList();

                TierImpl t;
                String tierName;
                Integer fontSize;

                // read preferences
                Object sorted = Preferences.get(interlinearizer.prefTierOrder,
                        interlinearizer.getTranscription());

                if (sorted instanceof ArrayList) {
                    sortedTiers = (ArrayList) sorted;

                    for (int i = 0; i < sortedTiers.size(); i++) {
                        t = (TierImpl) interlinearizer.getTranscription()
                                                      .getTierWithId((String) sortedTiers.get(
                                    i));

                        if (t != null) {
                            allTiers.add(t);
                        }
                    }
                }

                for (int i = 0; i < v.size(); i++) {
                    t = (TierImpl) v.get(i);

                    if (!allTiers.contains(t)) {
                        allTiers.add(t);
                    }
                }

                for (int i = 0; i < allTiers.size(); i++) {
                    t = (TierImpl) allTiers.get(i);
                    tierName = t.getName();
                    fontSize = new Integer(interlinearizer.getFontSize(tierName));

                    //fontSizes.put(tierName, fontSize);
                    model.addRow(new Object[] {
                            new Boolean(visTiers.contains(t)), tierName,
                            fontSize
                        });
                }
            }

            if (model.getRowCount() > 1) {
                upButton.setEnabled(true);
                downButton.setEnabled(true);
            } else {
                upButton.setEnabled(false);
                downButton.setEnabled(false);
            }
        } else {
            upButton.setEnabled(false);
            downButton.setEnabled(false);
        }
    }

    /**
     * Save some user prefs to the preferences file.
     */
    public void savePreferences() {
        ArrayList allTiers = new ArrayList();

        if (model != null) {
            String name = null;
            int tierCol = model.findColumn(TIER_NAME_COLUMN);

            for (int i = 0; i < model.getRowCount(); i++) {
                name = (String) model.getValueAt(i, tierCol);
                allTiers.add(name);
            }

            Preferences.set(interlinearizer.prefTierOrder, allTiers,
                interlinearizer.getTranscription());
        }
    }

    /**
     * Update the UI elements according to the current Locale and the current
     * edit mode.
     */
    private void updateForLocale() {
        setTitle(ElanLocale.getString("InterlinearizerOptionsDlg.Title"));

        tierSelectionPanel.setBorder(new TitledBorder(ElanLocale.getString(
                    "InterlinearizerOptionsDlg.Tiers")));
        whatPanel.setBorder(new TitledBorder(ElanLocale.getString(
                    "InterlinearizerOptionsDlg.What")));
        howPanel.setBorder(new TitledBorder(ElanLocale.getString(
                    "InterlinearizerOptionsDlg.How")));

        fontSizesButton.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.FontSizes"));

        selectionOnlyCheckBox.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.SelectionOnly"));
        showTierLabelCheckBox.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.ShowTierLabels"));
        showTimeCodeCheckBox.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.ShowTimeCode"));
        showEmptySlotsCheckBox.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.ShowEmptySlots"));

        tcStyleHhMmSsMs = ElanLocale.getString("TimeCodeFormat.TimeCode");
        tcStyleSsMs = ElanLocale.getString("TimeCodeFormat.Seconds");
        tcStyleMs = ElanLocale.getString("TimeCodeFormat.MilliSec");
        timeCodeFormatComboBox.addItem(tcStyleHhMmSsMs);
        timeCodeFormatComboBox.addItem(tcStyleSsMs);
        timeCodeFormatComboBox.addItem(tcStyleMs);

        hideLinesRadioButton.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.HideLines"));
        tierTemplateRadioButton.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.TierTemplate"));

        // "how" panel
        widthLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.Width"));
        heightLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.Height"));
        blockWrapLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.BlockWrap"));

        blockWrapNone = ElanLocale.getString(
                "InterlinearizerOptionsDlg.BlockWrap.None");
        blockWrapWithinBlock = ElanLocale.getString(
                "InterlinearizerOptionsDlg.BlockWrap.WithinBlock");
        blockWrapBlockBoundary = ElanLocale.getString(
                "InterlinearizerOptionsDlg.BlockWrap.BlockBoundary");
        blockWrapEachBlock = ElanLocale.getString(
                "InterlinearizerOptionsDlg.BlockWrap.EachBlock");

        blockWrapComboBox.addItem(blockWrapNone);
        blockWrapComboBox.addItem(blockWrapWithinBlock);
        blockWrapComboBox.addItem(blockWrapBlockBoundary);
        blockWrapComboBox.addItem(blockWrapEachBlock);

        lineWrapLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.LineWrap"));

        sortingLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.Sorting"));
        sortingAsFile = ElanLocale.getString(
                "InterlinearizerOptionsDlg.Sorting.AsFile");
        sortingComboBox.addItem(sortingAsFile);

        sortingTierHierarchy = ElanLocale.getString(
                "InterlinearizerOptionsDlg.Sorting.TierHierarchy");
        sortingComboBox.addItem(sortingTierHierarchy);

        sortingLinguisticType = ElanLocale.getString(
                "InterlinearizerOptionsDlg.Sorting.ByType");
        sortingComboBox.addItem(sortingLinguisticType);

        sortingParticipant = ElanLocale.getString(
                "InterlinearizerOptionsDlg.Sorting.ByParticipant");
        sortingComboBox.addItem(sortingParticipant);
        lineSpacingLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.LineSpacing"));
        blockSpacingLabel.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.BlockSpacing"));

        // components of buttonPanel
        applyChangesButton.setText(ElanLocale.getString(
                "InterlinearizerOptionsDlg.ApplyChanges"));

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            pageSetupButton.setText(ElanLocale.getString("Menu.File.PageSetup"));
            printButton.setText(ElanLocale.getString("Menu.File.Print"));
        } else {
            printButton.setText(ElanLocale.getString("Menu.File.SaveAs"));
        }
    }

    /**
     * Moves selected tiers up in the list of tiers.
     */
    private void moveUp() {
        if ((tierTable == null) || (model == null) ||
                (model.getRowCount() < 2)) {
            return;
        }

        int[] selected = tierTable.getSelectedRows();

        for (int i = 0; i < selected.length; i++) {
            int row = selected[i];

            if ((row > 0) && !tierTable.isRowSelected(row - 1)) {
                model.moveRow(row, row, row - 1);
                tierTable.changeSelection(row, 0, true, false);
                tierTable.changeSelection(row - 1, 0, true, false);
            }
        }
    }

    /**
     * Moves selected tiers up in the list of tiers.
     */
    private void moveDown() {
        if ((tierTable == null) || (model == null) ||
                (model.getRowCount() < 2)) {
            return;
        }

        int[] selected = tierTable.getSelectedRows();

        for (int i = selected.length - 1; i >= 0; i--) {
            int row = selected[i];

            if ((row < (model.getRowCount() - 1)) &&
                    !tierTable.isRowSelected(row + 1)) {
                model.moveRow(row, row, row + 1);
                tierTable.changeSelection(row, 0, true, false);
                tierTable.changeSelection(row + 1, 0, true, false);
            }
        }
    }

    /**
     * Sorting of the tiers in the table.
     */
    private void sortTiersTable() {
        if ((tierTable == null) || (model == null) ||
                (model.getRowCount() < 2)) {
            return;
        }

        // first store the current order
        ArrayList curOrder = new ArrayList();
        String name = null;
        TierImpl t = null;
        int col = model.findColumn(TIER_NAME_COLUMN);

        for (int i = 0; i < model.getRowCount(); i++) {
            name = (String) model.getValueAt(i, col);

            t = (TierImpl) interlinearizer.getTranscription().getTierWithId(name);
            curOrder.add(t);
        }

        TierSorter sorter = new TierSorter(interlinearizer.getTranscription());
        ArrayList newSort = null;

        switch (curSortMode) {
        case Interlinear.EXTERNALLY_SPECIFIED:
            newSort = sorter.sortTiers(sorter.UNSORTED);

            break;

        case Interlinear.TIER_HIERARCHY:
            newSort = sorter.sortTiers(sorter.BY_HIERARCHY, curOrder);

            break;

        case Interlinear.BY_LINGUISTIC_TYPE:
            newSort = sorter.sortTiers(sorter.BY_LINGUISTIC_TYPE, curOrder);

            break;

        case Interlinear.BY_PARTICIPANT:
            newSort = sorter.sortTiers(sorter.BY_PARTICIPANT, curOrder);

            break;

        default:}

        // apply new order to the table
        String cellName;

        for (int i = 0; i < newSort.size(); i++) {
            t = (TierImpl) newSort.get(i);

            name = t.getName();

            for (int j = 0; j < model.getRowCount(); j++) {
                cellName = (String) model.getValueAt(j, col);

                if ((cellName != null) && cellName.equals(name)) {
                    model.moveRow(j, j, i);

                    break;
                }
            }
        }

        tierTable.clearSelection();
    }

    /**
     * Returns the tiers that are marked visible in the tier table.
     *
     * @return the current visible tiers in the table, a list of TierImpl
     *         objects
     */
    private ArrayList getVisibleTierList() {
        ArrayList vt = new ArrayList();

        if (model != null) {
            String name = null;
            Boolean vis = null;
            TierImpl t = null;
            int tierCol = model.findColumn(TIER_NAME_COLUMN);
            int visCol = model.findColumn(PRINT_COLUMN);

            for (int i = 0; i < model.getRowCount(); i++) {
                vis = (Boolean) model.getValueAt(i, visCol);

                if (vis.booleanValue()) {
                    name = (String) model.getValueAt(i, tierCol);

                    t = (TierImpl) interlinearizer.getTranscription()
                                                  .getTierWithId(name);
                    vt.add(t);
                }
            }
        }

        return vt;
    }

    /**
     * Update the Interlinear object with the current font sizes.
     */
    private void applyFontSizes() {
        if (model != null) {
            String name = null;
            Integer size = null;
            int tierCol = model.findColumn(TIER_NAME_COLUMN);
            int sizeCol = model.findColumn(FONT_SIZE_COLUMN);

            for (int i = 0; i < model.getRowCount(); i++) {
                name = (String) model.getValueAt(i, tierCol);
                size = (Integer) model.getValueAt(i, sizeCol);

                if ((name != null) && (size != null)) {
                    if (size.intValue() != interlinearizer.getFontSize(name)) {
                        interlinearizer.setFontSize(name, size.intValue());
                    }
                }
            }
        }
    }

    /**
     * Change the font sizes. Pass a HashMap with tier names and font sizes to
     * the dialog.
     */
    private void doSetFontSizes() {
        ArrayList fontNames = new ArrayList();
        HashMap fontMap = new HashMap();

        if (model != null) {
            int tierCol = model.findColumn(TIER_NAME_COLUMN);
            int fontCol = model.findColumn(FONT_SIZE_COLUMN);
            String name = null;
            Integer size = null;

            for (int i = 0; i < model.getRowCount(); i++) {
                name = (String) model.getValueAt(i, tierCol);
                size = (Integer) model.getValueAt(i, fontCol);
                fontNames.add(name);
                fontMap.put(name, size);
            }

            JDialog dlg = new TierFontSizeDlg(this, true, fontMap, fontNames);
            dlg.setLocationRelativeTo(tierTable);
            dlg.setVisible(true);

            // update the table
            Iterator tierIt = fontMap.keySet().iterator();
            name = null;
            size = null;

            while (tierIt.hasNext()) {
                name = (String) tierIt.next();

                if (name != null) {
                    size = (Integer) fontMap.get(name);

                    if (size != null) {
                        Object ttName;

                        for (int i = 0; i < model.getRowCount(); i++) {
                            ttName = model.getValueAt(i, tierCol);

                            if (name.equals(ttName)) {
                                model.setValueAt(size, i, fontCol);

                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Updates the interlinear layout and the preview after (possible) changes
     * in parameters.
     */
    private void doApplyChanges() {
        // set parameters	
        interlinearizer.setSelectionOnly(selectionOnlyCheckBox.isSelected());
        interlinearizer.setTierLabelsShown(showTierLabelCheckBox.isSelected());
        interlinearizer.setTimeCodeShown(showTimeCodeCheckBox.isSelected());

        String tcStyleString = (String) timeCodeFormatComboBox.getSelectedItem();

        if (tcStyleString.equals(tcStyleHhMmSsMs)) {
            interlinearizer.setTimeCodeType(Interlinear.HHMMSSMS);
        } else if (tcStyleString.equals(tcStyleSsMs)) {
            interlinearizer.setTimeCodeType(Interlinear.SSMS);
        } else if (tcStyleString.equals(tcStyleMs)) {
            interlinearizer.setTimeCodeType(Interlinear.MS);
        }

        if (hideLinesRadioButton.isSelected()) {
            interlinearizer.setEmptyLineStyle(Interlinear.HIDE_EMPTY_LINES);
        } else {
            interlinearizer.setEmptyLineStyle(Interlinear.TEMPLATE);
        }

        int imageWidth = 0;

        //int imageHeight = 0;
        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            int pageWidth = new Double(PrintCommand.pageFormat.getImageableWidth()).intValue(); //width of printer page	
            widthTextField.setText(new Integer(pageWidth).toString());
            interlinearizer.setWidth(pageWidth);

            int pageHeight = new Double(PrintCommand.pageFormat.getImageableHeight()).intValue();
            heightTextField.setText("" + pageHeight);
            interlinearizer.setPageHeight(pageHeight);
        } else if (interlinearizer.getOutputMode() == Interlinear.INTERLINEAR_TEXT) {
            String widthText = widthTextField.getText();

            try {
                imageWidth = Integer.parseInt(widthText);
                interlinearizer.setWidth(imageWidth);
            } catch (NumberFormatException nfe) {
                imageWidth = interlinearizer.getWidth();
                widthTextField.setText("" + imageWidth);
            }
        }

        if (lineWrapCheckBox.isSelected()) {
            interlinearizer.setLineWrapStyle(Interlinear.NEXT_LINE);
        } else {
            interlinearizer.setLineWrapStyle(Interlinear.NO_WRAP);
        }

        String blockWrapString = (String) blockWrapComboBox.getSelectedItem();

        if (blockWrapString.equals(blockWrapNone)) {
            interlinearizer.setBlockWrapStyle(Interlinear.NO_WRAP);
        } else if (blockWrapString.equals(blockWrapWithinBlock)) {
            interlinearizer.setBlockWrapStyle(Interlinear.WITHIN_BLOCKS);
        } else if (blockWrapString.equals(blockWrapBlockBoundary)) {
            interlinearizer.setBlockWrapStyle(Interlinear.BLOCK_BOUNDARY);
        } else if (blockWrapString.equals(blockWrapEachBlock)) {
            interlinearizer.setBlockWrapStyle(Interlinear.EACH_BLOCK);
        }

        String sortingString = (String) sortingComboBox.getSelectedItem();

        if (sortingString.equals(sortingAsFile)) {
            interlinearizer.setSortingStyle(Interlinear.EXTERNALLY_SPECIFIED);
        } else if (sortingString.equals(sortingTierHierarchy)) {
            interlinearizer.setSortingStyle(Interlinear.TIER_HIERARCHY);
        } else if (sortingString.equals(sortingLinguisticType)) {
            interlinearizer.setSortingStyle(Interlinear.BY_LINGUISTIC_TYPE);
        } else if (sortingString.equals(sortingParticipant)) {
            interlinearizer.setSortingStyle(Interlinear.BY_PARTICIPANT);
        }

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            int lineSpacing = 0;

            try {
                lineSpacing = Integer.parseInt(lineSpacingTextField.getText());
            } catch (NumberFormatException nfe) {
                lineSpacingTextField.setText("" + lineSpacing);
            }

            interlinearizer.setLineSpacing(lineSpacing);
        } else {
            lineSpacingTextField.setText("");
        }

        int blockSpacing = -1; // default: derived from line spacing

        try {
            blockSpacing = Integer.parseInt(blockSpacingTextField.getText());
        } catch (NumberFormatException nfe) {
            blockSpacingTextField.setText("" +
                interlinearizer.getBlockSpacing());
        }

        interlinearizer.setBlockSpacing(blockSpacing);

        // visible tiers
        interlinearizer.setVisibleTiers(getVisibleTierList());
        applyFontSizes();

        if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
            interlinearizer.renderView(previewPanel.getBufferedImage());

            // rendering (calculating) the view causes a new total height to be calculated 
            previewPanel.updateView();
        } else {
            interlinearizer.renderView();

            int pageWidth = 600;
            FontMetrics metrics = previewPanel.getBufferedImage().getGraphics()
                                              .getFontMetrics(Interlinear.MONOSPACED_FONT);

            if (metrics != null) {
                int cw = metrics.charWidth('w'); //any char
                pageWidth = cw * interlinearizer.getWidth();
            }

            previewPanel.setImageableSize(new Dimension(pageWidth,
                    interlinearizer.getHeight()));
            interlinearizer.drawViewOnImage(previewPanel.getBufferedImage(),
                previewPanel.getOffset());
            previewPanel.repaint();
        }
    }

    /**
     * Creates and executes a PrintCommand.
     */
    private void doPrint() {
        Command c = ELANCommandFactory.createCommand(null,
                ELANCommandFactory.PRINT);
        c.execute(interlinearizer.getTranscription(),
            new Object[] { interlinearizer });

        /*
           PrintService[] services = PrintServiceLookup.lookupPrintServices(
                                      DocFlavor.INPUT_STREAM.JPEG, null);
           PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
           PrintService service =  ServiceUI.printDialog(null, 50, 50,
                                                         services, null, null,
                                                         attributes);
         */
    }

    /**
     * Shows a save as dialog and starts rendering to the text file.
     */
    private void doSaveText() {
        String fileName = promptForFileName();

        if (fileName != null) {
            try {
                File exportFile = new File(fileName);
                CharacterRenderer render = new CharacterRenderer(interlinearizer,
                        exportFile, charEncoding);
                render.renderText();

                // success message?

                /*
                   JOptionPane.showMessageDialog(this,
                       ElanLocale.getString("ExportDialog.Message.Error"),
                       ElanLocale.getString("Message.Warning"),
                       JOptionPane.INFORMATION_MESSAGE);
                 */
            } catch (Exception e) {
                // FileNotFound, IO, Security, Null etc
                JOptionPane.showMessageDialog(this,
                    ElanLocale.getString(
                        "InterlinearizerOptionsDlg.Error.TextOut") + " \n" +
                    "(" + e.getMessage() + ")",
                    ElanLocale.getString("Message.Error"),
                    JOptionPane.WARNING_MESSAGE);
            }
        }
    }

    /**
     * Invokes a standard, platform specific page setup dialog
     */
    private void doPageSetup() {
        Command c = ELANCommandFactory.createCommand(null,
                ELANCommandFactory.PAGESETUP);
        c.execute(interlinearizer.getTranscription(), null);

        doApplyChanges(); // possibly width, height are changed
    }

    /**
     * Prompts the user for a file name and location.
     *
     * @return a file (unique) path
     */
    private String promptForFileName() {
        String exportDir = (String) Preferences.get("LastUsedExportDir", null);

        if (exportDir == null) {
            exportDir = System.getProperty("user.dir");
        }

        TextExportFileChooser chooser = new TextExportFileChooser();
        chooser.setCurrentDirectory(new File(exportDir));
        chooser.setDialogTitle(ElanLocale.getString(
                "ExportTradTranscript.Title"));

        File exportFile = null;
        FileFilter filter = ElanFileFilter.createFileFilter(ElanFileFilter.TEXT_TYPE);
        chooser.setFileFilter(filter);

        if (chooser.showSaveDialog(null, null) == JFileChooser.APPROVE_OPTION) {
            File curDir = chooser.getCurrentDirectory();

            if (curDir != null) {
                Preferences.set("LastUsedExportDir", curDir.getAbsolutePath(),
                    null);
            }

            exportFile = chooser.getSelectedFile();

            if (exportFile != null) {
                charEncoding = chooser.getSelectedEncoding();

                String name = exportFile.getAbsolutePath();
                String lowerPathName = name.toLowerCase();

                String[] exts = FileExtension.TEXT_EXT;
                boolean validExt = false;

                for (int i = 0; i < exts.length; i++) {
                    if (lowerPathName.endsWith("." + exts[i])) {
                        validExt = true;

                        break;
                    }
                }

                if (!validExt) {
                    name += ("." + exts[0]);
                    exportFile = new File(name);
                }

                if (exportFile.exists()) {
                    int answer = JOptionPane.showConfirmDialog(null,
                            ElanLocale.getString("Message.Overwrite"),
                            ElanLocale.getString("SaveDialog.Message.Title"),
                            JOptionPane.YES_NO_OPTION);

                    if (answer == JOptionPane.NO_OPTION) {
                        return promptForFileName();
                    } else {
                        return name;
                    }
                } else {
                    return name;
                }
            } else {
                return null;
            }
        } else {
            // save dialog canceled
            return null;
        }
    }

    /**
     * The action performed method.
     *
     * @param event the action event
     */
    public void actionPerformed(ActionEvent event) {
        if (event.getSource() == applyChangesButton) {
            doApplyChanges();
        } else if (event.getSource() == printButton) {
            if (interlinearizer.getOutputMode() == Interlinear.PRINT) {
                doPrint();
            } else {
                doSaveText();
            }
        } else if (event.getSource() == pageSetupButton) {
            doPageSetup();
        } else if (event.getSource() == fontSizesButton) {
            doSetFontSizes();
        } else if (event.getSource() == upButton) {
            moveUp();
        } else if (event.getSource() == downButton) {
            moveDown();
        }
    }

    /**
     * Updates the checked state of the export checkboxes.
     *
     * @param lse the list selection event
     */
    public void valueChanged(ListSelectionEvent lse) {
        if ((model != null) && lse.getValueIsAdjusting()) {
            int b = lse.getFirstIndex();
            int e = lse.getLastIndex();
            int col = model.findColumn(PRINT_COLUMN);

            for (int i = b; i <= e; i++) {
                if (tierTable.isRowSelected(i)) {
                    model.setValueAt(Boolean.TRUE, i, col);
                }
            }
        }
    }

    /**
     * The item state changed handling.
     *
     * @param ie the ItemEvent
     */
    public void itemStateChanged(ItemEvent ie) {
        if (ie.getSource() == sortingComboBox) {
            if (ie.getStateChange() == ItemEvent.SELECTED) {
                int nextSort = curSortMode;
                String sortMode = (String) sortingComboBox.getSelectedItem();

                if (sortMode == sortingAsFile) {
                    nextSort = Interlinear.EXTERNALLY_SPECIFIED;
                } else if (sortMode == sortingTierHierarchy) {
                    nextSort = Interlinear.TIER_HIERARCHY;
                } else if (sortMode == sortingParticipant) {
                    nextSort = Interlinear.BY_PARTICIPANT;
                } else {
                    nextSort = Interlinear.BY_LINGUISTIC_TYPE;
                }

                curSortMode = nextSort;
                sortTiersTable();
            }
        }
    }
}
