/*
 * File:     InlineEditBox.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.gui;

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

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

import mpi.eudico.client.annotator.viewer.SubtitleViewer;

import mpi.eudico.client.im.ImUtil;

import mpi.eudico.server.corpora.clom.Annotation;
import mpi.eudico.server.corpora.clom.Transcription;

import mpi.eudico.server.corpora.clomimpl.abstr.TierImpl;
import mpi.eudico.server.corpora.clomimpl.abstr.TranscriptionImpl;
import mpi.eudico.server.corpora.clomimpl.type.CVEntry;
import mpi.eudico.server.corpora.clomimpl.type.ControlledVocabulary;

import mpi.library.util.LogUtil;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import java.util.Locale;
import java.util.logging.Logger;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;


/**
 * A class that provides and configures user interface components for  editing
 * annotation values. Elan Viewer components that offer the possibility of
 * editing annotation values can use this class to get a suitable editor
 * component. Depending on the language (locale) of the annotation it will
 * activate  special input method components.
 *
 * @author MPI
 * @version jun 2004 additions related to the use of controlled vocabularies
 * @version sep 2005 when an annotation's Locale is the system default and the
 * the edit box's is also the system default, the language isn't set anymore.
 * Tis way it is possible to use other system specific im's / keyboards on
 * MacOS as well.
 */
public class InlineEditBox extends JPanel implements ActionListener,
    MouseListener, MenuListener, KeyListener, ElanLocaleListener {
    /** action command constant */
    private static final String EDIT_MENU_DET = "Detach Editor";

    /** action command constant */
    private static final String EDIT_MENU_ATT = "Attach Editor";

    /** action command constant */
    private static final String EDIT_MENU_CMT = "Commit Changes";

    /** action command constant */
    private static final String EDIT_MENU_CNL = "Cancel Changes";

    /** A logger to replace System.out calls. */
    private static final Logger LOG = Logger.getLogger(InlineEditBox.class.getName());

    /** the textarea in use when the editor is used in attached mode */
    final private JTextArea textArea = new JTextArea("", 2, 1);

    /** the scrollpane for the attached-mode textarea */
    final private JScrollPane textAreaScrollPane = new JScrollPane(textArea);

    /** the textarea in use when the editor is used in detached mode */
    final private JTextArea exttextArea = new JTextArea("", 2, 1);

    /** the scrollpane for the detached-mode textarea */
    final private JScrollPane exttextAreaScrollPane = new JScrollPane(exttextArea);

    /**
     * a focus listener that lets the attached-mode textarea request the
     * keyboard focus
     */
    final private FocusListener intFocusListener = new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                if (!isUsingControlledVocabulary) {
                    textArea.requestFocus();
                    textArea.getCaret().setVisible(true);
                } else {
                    if (cvEntryComp != null) {
                        cvEntryComp.grabFocus();
                    }
                }
            }

            public void focusLost(FocusEvent e) {
                if (!isEditing) {
                    transferFocusUpCycle();
                }
            }
        };

    /**
     * a focus listener that lets the detached-mode textarea request the
     * keyboard focus
     */
    final private FocusListener extFocusListener = new FocusAdapter() {
            public void focusGained(FocusEvent e) {
                if (!isUsingControlledVocabulary) {
                    exttextArea.requestFocus();
                    exttextArea.getCaret().setVisible(true);
                } else {
                    if (cvEntryComp != null) {
                        cvEntryComp.grabFocus();
                    }
                }
            }

            public void focusLost(FocusEvent e) {
                //transferFocusUpCycle();
            }
        };

    private JPopupMenu popupMenu = new JPopupMenu("Select Language");
    private JDialog externalDialog = null;
    private Rectangle dialogBounds;
    private Locale[] allLocales;
    private int numberOfLocales;
    private String oldText;
    private boolean attached = true;
    private Annotation annotation;
    private Point position;
    private JTable table = null;
    private int editingColumn = -1;
    private Locale annotationLocale;
    private boolean attachable;
    private boolean isUsingControlledVocabulary = false;
    private SubtitleViewer subtitleViewer;

    // fields for Locale changes
    private JMenu editMenu;
    private JMenu editorMenu;
    private JMenu selectLanguageMenu;
    private JMenuItem attachMI;
    private JMenuItem commitMI;
    private JMenuItem cancelMI;
    private JMenuItem detachPUMI;
    private JMenuItem commitPUMI;
    private JMenuItem cancelPUMI;
    private JMenuItem selectAllPUMI;
    private JMenuItem cutMI;
    private JMenuItem copyMI;
    private JMenuItem pasteMI;
    private JMenuItem cutPUMI;
    private JMenuItem copyPUMI;
    private JMenuItem pastePUMI;
    private JMenuItem selectAllMI;
    private JMenuBar menuBar;

    /** a JList in a scrollpane */
    private CVEntryComponent cvEntryComp;
    private int minCVWidth = 100;
    private int minCVHeight = 120;

    /**
     * this field can be either a JPanel (this), a JScrollPane, a JTextArea , a
     * JComboBox or any other component that can be added to the layout of a
     * viewer (component)
     */
    private JComponent editorComponent;

    //temp
    private Font uniFont = Constants.DEFAULTFONT;
    private boolean isEditing = false;

    /**
     * If InlineEditBox is invoked by a Table, it'll call on exiting the
     * editingStopped/Cancelled methods of the Table.
     *
     * @param table the JTable that will use the editor component as a
     *        tablecelleditor
     */
    public InlineEditBox(JTable table) {
        this.table = table;
        init();
    }

    /**
     * When this editor is not created by a viewer, it will always be created
     * as a  "detached" dialog.
     *
     * @param attachable whether or not this editor can be attached to a
     *        viewer component.
     */
    public InlineEditBox(boolean attachable) {
        init();

        this.attachable = attachable;
    }

    /**
     * Special case for a SubtitleViewer. <br>
     * The viewer receives a notification when editing has been stopped.
     * Pending: this should replaced by a more general type, an interface that
     * a viewer  can implement that needs notification of inline editing
     * events (stopped, attached, detached...).
     *
     * @param subtitleViewer the subtitleviewer
     */
    public InlineEditBox(SubtitleViewer subtitleViewer) {
        init();

        this.subtitleViewer = subtitleViewer;
    }

    /**
     * Creates a new InlineEditBox instance
     */
    public InlineEditBox() {
        init();
        attached = false;
    }

    /**
     * DOCUMENT ME!
     */
    public void init() {
        attachable = true;
        setLayout(new BorderLayout());

        try {
            allLocales = ImUtil.getLanguages();
            numberOfLocales = (allLocales == null) ? 0 : allLocales.length;
        } catch (java.lang.NoSuchMethodError nsme) {
            // The SPI extensions have not been present at startup.
            //String msg = "Setup incomplete: you won't be able to set languages for editing.";
            String msg = ElanLocale.getString("InlineEditBox.Message.SPI") +
                "\n" + ElanLocale.getString("InlineEditBox.Message.SPI2");
            JOptionPane.showMessageDialog(null, msg, null,
                JOptionPane.ERROR_MESSAGE);
        } catch (Exception exc) {
            LOG.warning("InlineEditBox::init::ParentIMBug::FIXME");
            LOG.warning(LogUtil.formatStackTrace(exc));
        }

        textArea.addMouseListener(this);
        textArea.setLineWrap(false);
        textAreaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        textAreaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        add(textAreaScrollPane, BorderLayout.CENTER);
        textArea.getCaret().setVisible(true);
        textArea.addKeyListener(this);
        textArea.addFocusListener(new FocusAdapter() {
                public void focusGained(FocusEvent e) {
                    if (!annotationLocale.equals(Locale.getDefault()) &&
                            !annotationLocale.equals(textArea.getLocale())) {
                        ImUtil.setLanguage(textArea, annotationLocale);
                        textArea.setFont(uniFont);
                    }
                }

                public void focusLost(FocusEvent e) {
                    if (!isEditing) {
                        transferFocusUpCycle();
                    }
                }
            });

        exttextArea.setLineWrap(true);
        exttextArea.setWrapStyleWord(true);
        exttextArea.addKeyListener(this);
        exttextArea.addFocusListener(new FocusAdapter() {
                public void focusGained(FocusEvent e) {
                    if (!annotationLocale.equals(Locale.getDefault()) &&
                            !annotationLocale.equals(exttextArea.getLocale())) {
                        ImUtil.setLanguage(exttextArea, annotationLocale);
                        exttextArea.setFont(uniFont.deriveFont(20.0f));
                    }
                }

                public void focusLost(FocusEvent e) {
                    if (!isEditing) {
                        transferFocusUpCycle();
                    }
                }
            });

        createPopupMenu();

        exttextAreaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        exttextAreaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

        textAreaScrollPane.addFocusListener(intFocusListener);
        addFocusListener(intFocusListener);
    }

    /**
     * Creates a modal JDialog when editing is done in detached mode.
     */
    public void createExternalDialog() {
        try {
            externalDialog = new JDialog(ELANCommandFactory.getRootFrame(
                        (Transcription) annotation.getTier().getParent()),
                    ElanLocale.getString("InlineEditBox.Title"), true);
        } catch (Exception ex) {
            LOG.warning(LogUtil.formatStackTrace(ex));
        }

        //exttextAreaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        //exttextAreaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        //externalDialog.getContentPane().add(exttextAreaScrollPane);
        if (menuBar == null) {
            createJMenuBar();
        }

        externalDialog.setJMenuBar(menuBar);
        externalDialog.addFocusListener(extFocusListener);
        externalDialog.setSize(300, 300);
    }

    /**
     * Creates a popup menu.
     */
    public void createPopupMenu() {
        detachPUMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Detach"));
        detachPUMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
                ActionEvent.SHIFT_MASK));
        detachPUMI.setActionCommand(EDIT_MENU_DET);
        detachPUMI.addActionListener(this);
        popupMenu.add(detachPUMI);
        commitPUMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Commit"));
        commitPUMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        commitPUMI.setActionCommand(EDIT_MENU_CMT);
        commitPUMI.addActionListener(this);
        popupMenu.add(commitPUMI);
        cancelPUMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Cancel"));
        cancelPUMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
        cancelPUMI.setActionCommand(EDIT_MENU_CNL);
        cancelPUMI.addActionListener(this);
        popupMenu.add(cancelPUMI);

        popupMenu.addSeparator();

        cutPUMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Edit.Cut"));
        cutPUMI.setActionCommand("cut");
        cutPUMI.addActionListener(this);
        popupMenu.add(cutPUMI);
        copyPUMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Edit.Copy"));
        copyPUMI.setActionCommand("copy");
        copyPUMI.addActionListener(this);
        popupMenu.add(copyPUMI);
        pastePUMI = new JMenuItem(ElanLocale.getString(
                    "InlineEditBox.Edit.Paste"));
        pastePUMI.setActionCommand("paste");
        pastePUMI.addActionListener(this);
        popupMenu.add(pastePUMI);
        selectAllPUMI = new JMenuItem(ElanLocale.getString(
                    "InlineEditBox.Edit.SelectAll"));
        selectAllPUMI.setActionCommand("selectAll");
        selectAllPUMI.addActionListener(this);
        popupMenu.add(selectAllPUMI);

        popupMenu.addSeparator();

        JMenuItem newItem;

        for (int i = 0; i < numberOfLocales; i++) {
            newItem = new JMenuItem(allLocales[i].getDisplayName());
            popupMenu.add(newItem);
            newItem.addActionListener(this);
        }
    }

    private JMenuBar createJMenuBar() {
        menuBar = new JMenuBar();
        editorMenu = new JMenu(ElanLocale.getString("InlineEditBox.Menu.Editor"));
        editMenu = new JMenu(ElanLocale.getString("Menu.Edit"));
        editMenu.addMenuListener(this);
        selectLanguageMenu = new JMenu(ElanLocale.getString(
                    "InlineEditBox.Menu.Select"));

        if (attachable == true) {
            attachMI = new JMenuItem(ElanLocale.getString(
                        "InlineEditBox.Attach"));
            attachMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
                    ActionEvent.SHIFT_MASK));
            attachMI.setActionCommand(EDIT_MENU_ATT);
            attachMI.addActionListener(this);
            editorMenu.add(attachMI);
        }

        commitMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Commit"));
        commitMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        commitMI.setActionCommand(EDIT_MENU_CMT);
        commitMI.addActionListener(this);
        editorMenu.add(commitMI);
        cancelMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Cancel"));
        cancelMI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
        cancelMI.setActionCommand(EDIT_MENU_CNL);
        cancelMI.addActionListener(this);
        editorMenu.add(cancelMI);

        cutMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Edit.Cut"));
        cutMI.setActionCommand("cut");
        cutMI.addActionListener(this);
        editMenu.add(cutMI);
        copyMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Edit.Copy"));
        copyMI.setActionCommand("copy");
        copyMI.addActionListener(this);
        editMenu.add(copyMI);
        pasteMI = new JMenuItem(ElanLocale.getString("InlineEditBox.Edit.Paste"));
        pasteMI.setActionCommand("paste");
        pasteMI.addActionListener(this);
        editMenu.add(pasteMI);

        selectAllMI = new JMenuItem(ElanLocale.getString(
                    "InlineEditBox.Edit.SelectAll"));
        selectAllMI.setActionCommand("selectAll");
        selectAllMI.addActionListener(this);
        editMenu.add(selectAllMI);

        JMenuItem newItem;

        for (int i = 0; i < numberOfLocales; i++) {
            newItem = new JMenuItem(allLocales[i].getDisplayName());
            selectLanguageMenu.add(newItem);
            newItem.addActionListener(this);
        }

        menuBar.add(editorMenu);
        menuBar.add(editMenu);
        menuBar.add(selectLanguageMenu);

        return menuBar;
    }

    /**
     * Returns whether or not the current annotation's value is restricted by a
     * ControlledVocabulary.
     *
     * @return true if a CV has to be used, false otherwise
     */
    public boolean isUsingControlledVocabulary() {
        return isUsingControlledVocabulary;
    }

    /**
     * Overrides setVisible(boolean) in JComponent.
     *
     * @param vis the visibility value
     */
    public void setVisible(boolean vis) {
        super.setVisible(vis);

        if (externalDialog != null) {
            externalDialog.setVisible(vis);
        }

        if (vis == false) {
            //closeIM();
            if (table == null) {
                setLocation(-10, -10);
            }
        }
    }

    /**
     * Overrides setSize(Dimension) in Component by performing a check to
     * guarantee a minimal  size.
     *
     * @param d the requested size
     */
    public void setSize(Dimension d) {
        // set useable sizes for edit region
        if (d.getWidth() < 60) {
            d = new Dimension(60, d.height);
        }

        if (d.getHeight() < 38) {
            d = new Dimension(d.width, 38);
        }

        //AK 16/08/2002 unfortunately all those setSizes are somewhere necessary
        super.setSize(d);
        setPreferredSize(d);
        textAreaScrollPane.setPreferredSize(d);
        textAreaScrollPane.setSize(d);
    }

    /**
     * Overrides setFont(Font) in Component by also setting the font for the
     * textareas.
     *
     * @param font the Font to use
     */
    public void setFont(Font font) {
        super.setFont(font);
        uniFont = font;

        // setFont() is used at intializing superclass - textarea not yet instantiated
        if (textArea != null) {
            textArea.setFont(font);
        }

        if (exttextArea != null) {
            exttextArea.setFont(font);
        }
    }

    /**
     * Sets the annotation that is to be edited.
     * When <code>forceOpenCV</code> is true an 'open' text edit box will be used
     * even if the linguistic type has an associated ControlledVocabulery.
     *
     * @param ann the annotation to be edited
     * @param forceOpenCV if true the associated CV will be ignored,
     * editing will be open
     */
    public void setAnnotation(Annotation ann, boolean forceOpenCV) {
        annotation = ann;
        oldText = ann.getValue();
        textArea.setText(oldText.trim());

        try {
            annotationLocale = ((TierImpl) annotation.getTier()).getDefaultLocale();

            if (forceOpenCV) {
                isUsingControlledVocabulary = false;
            } else {
                isUsingControlledVocabulary = ((TierImpl) annotation.getTier()).getLinguisticType()
                                               .isUsingControlledVocabulary();
            }
        } catch (Exception e) {
            LOG.warning(
                "Could not establish Default Language of Tier. Using System Default instead.");
            annotationLocale = Locale.getDefault();
            isUsingControlledVocabulary = false;
        }

        if (attached) {
            if (!isUsingControlledVocabulary) {
                textArea.setEditable(true);
                textArea.setCaretPosition(textArea.getText().length());

                //textArea.requestFocus();
            } else {
                textArea.setEditable(false);

                if (cvEntryComp == null) {
                    cvEntryComp = new CVEntryComponent(JScrollPane.class);
                }

                cvEntryComp.setAnnotation(annotation);
            }
        }
    }

    /**
     * Sets the annotation that is to be edited.
     *
     * @param ann the annotation to be edited
     */
    public void setAnnotation(Annotation ann) {
        setAnnotation(ann, false);
    }

    /**
     * Checks whether the annotation's value has been edited.
     *
     * @return true if the annotation's value has been edited, false otherwise
     */
    public boolean annotationModified() {
        return attached ? (!oldText.equals(textArea.getText()))
                        : (!oldText.equals(exttextArea.getText()));
    }

    /**
     * Returns true if the internal TextArea is open
     *
     * @return true if the component is attached to a viewer's layout
     */
    public boolean isAttached() {
        return attached;
    }

    /**
     * Sets internal TextArea (resp. "this") visible(false) and opens external
     * TextArea (resp. "externalDialog")
     */
    public void detachEditor() {
        if (attachable && !attached) {
            return;
        }

        attached = false;
        position = getLocation();
        createExternalDialog();

        if (dialogBounds != null) {
            externalDialog.setBounds(dialogBounds);
        } else {
            Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
            Rectangle frameDim = externalDialog.getBounds();
            externalDialog.setLocation((screenDim.width - frameDim.width) / 2,
                (screenDim.height - frameDim.height) / 2);
        }

        ImUtil.setLanguage(textArea, Locale.getDefault());
        setVisible(false);

        if (table != null) {
            editingColumn = table.getEditingColumn();
            table.editingCanceled(new ChangeEvent(this));
        }

        if (!isUsingControlledVocabulary) {
            externalDialog.getContentPane().removeAll();
            externalDialog.getContentPane().add(exttextAreaScrollPane);
            exttextArea.setEditable(true);
            exttextArea.setText(textArea.getText());
            exttextArea.setCaretPosition(exttextArea.getText().length());
            exttextArea.setFont(textArea.getFont().deriveFont(20.0f));
        } else {
            if (cvEntryComp == null) {
                cvEntryComp = new CVEntryComponent(JScrollPane.class);
                cvEntryComp.setAnnotation(annotation);
            } else {
                //cvEntryComp.setAnnotation(annotation);
                cvEntryComp.setDelegate(JScrollPane.class);
            }

            exttextArea.setEditable(false);
            cvEntryComp.removePopupListener();
            cvEntryComp.setFont(getFont());
            externalDialog.getContentPane().removeAll();
            externalDialog.getContentPane().add(cvEntryComp.getEditorComponent());
        }

        externalDialog.setVisible(true);
    }

    /**
     * Returns the editor the editor to the previous attached state and dispose
     * the external dialog.
     */
    protected void attachEditor() {
        attached = true;
        dialogBounds = externalDialog.getBounds();
        externalDialog.dispose();
        externalDialog = null;

        if (table == null) {
            setLocation(position);
            setVisible(true);
        } else {
            Locale tmpLocale = annotationLocale;

            //The TagCellEditor will call another time the setAnnotation method
            //which sets again the default Locale of the Tier
            //table.editCellAt(table.getRowOfAnnotation(annotation),1);
            int[] selrows = table.getSelectedRows();

            //int column = table.getSelectedColumn();
            table.editCellAt(selrows[0], editingColumn);
            annotationLocale = tmpLocale;
        }

        if (!isUsingControlledVocabulary) {
            textArea.setText(exttextArea.getText());
            textArea.requestFocus();
        } else {
            cvEntryComp.addPopupListener();

            if (editorComponent == this) {
                removeAll();
                add(cvEntryComp.getEditorComponent(), BorderLayout.CENTER);
            }

            startEdit();
        }
    }

    /**
     * Resets elements and cleans up without applying any changes in the  value
     * of the annotation.  HB, 11 oct 01, changed from protected to public
     */
    public void cancelEdit() {
        isEditing = false;
        closeIM();

        if (attached) {
            setVisible(false);
        } else {
            if (externalDialog != null) {
                dialogBounds = externalDialog.getBounds();
                setVisible(false);
                externalDialog.dispose();
                externalDialog = null;
            }

            attached = true;
        }

        if (table != null) {
            table.editingCanceled(new ChangeEvent(this));
        }

        //M_P
        handleSubtitleViewer();
    }

    /**
     * Checks for modifications, applies the modification if any, resets and
     * cleans  up a bit.
     */
    public void commitEdit() {
        isEditing = false;
        closeIM();

        if (isUsingControlledVocabulary && (cvEntryComp != null)) {
            if (attached) {
                textArea.setText(cvEntryComp.getSelectedEntryValue());
            } else {
                exttextArea.setText(cvEntryComp.getSelectedEntryValue());
            }
        }

        String newText = "";
        boolean modified = annotationModified();

        if (attached) {
            if (modified) {
                newText = textArea.getText();
            }

            setVisible(false);
        } else {
            if (modified) {
                newText = exttextArea.getText();
            }

            dialogBounds = externalDialog.getBounds();
            setVisible(false);
            externalDialog.dispose();
            externalDialog = null;
            attached = true;
        }

        if (modified) {
            Command c = ELANCommandFactory.createCommand(((Transcription) annotation.getTier()
                                                                                    .getParent()),
                    ELANCommandFactory.MODIFY_ANNOTATION);
            Object[] args = new Object[] { oldText, newText };
            c.execute(annotation, args);
        }

        if (table != null) {
            table.editingStopped(new ChangeEvent(this));
        }

        //M_P
        handleSubtitleViewer();
    }

    /**
     * Notification of a connected SubtitleViewer when editing has stopped.
     * Pending: candidate for change
     */
    private void handleSubtitleViewer() {
        if (subtitleViewer != null) {
            subtitleViewer.putOriginalComponentBack();
        }
    }

    /**
     * Restores the default locale (because of InputMethod stuff) of the
     * textareas.
     */
    private void closeIM() {
        if (attached) {
            if (!textArea.getLocale().equals(Locale.getDefault())) {
                ImUtil.setLanguage(textArea, Locale.getDefault());
            }
        } else {
            if (!exttextArea.getLocale().equals(Locale.getDefault())) {
                ImUtil.setLanguage(exttextArea, Locale.getDefault());
            }
        }
    }

    /**
     * Forwards the cut action to either the <code>textArea</code> or the
     * <code>exttextArea</code>, depending on the attached/detached state.
     */
    private void doCut() {
        if (attached) {
            textArea.cut();
        } else {
            exttextArea.cut();
        }
    }

    /**
     * Forwards the copy action to either the <code>textArea</code> or the
     * <code>exttextArea</code>, depending on the attached/detached state.
     */
    private void doCopy() {
        if (attached) {
            textArea.copy();
        } else {
            exttextArea.copy();
        }
    }

    /**
     * Forwards the paste action to either the <code>textArea</code> or the
     * <code>exttextArea</code>, depending on the attached/detached state.
     */
    private void doPaste() {
        if (attached) {
            textArea.paste();
        } else {
            exttextArea.paste();
        }
    }

    /**
     * Forwards the select all action to either the <code>textArea</code> or the
     * <code>exttextArea</code>, depending on the attached/detached state.
     */
    private void doSelectAll() {
        if (attached) {
            textArea.selectAll();
        } else {
            exttextArea.selectAll();
        }
    }

    /**
     * Enables/disables edit menu items in the popup menu.<br>
     * Check the contents of the <code>textArea</code> component and the
     * system clipboard.
     */
    private void updatePopup() {
        if ((textArea.getSelectedText() == null) ||
                (textArea.getSelectedText().length() == 0)) {
            cutPUMI.setEnabled(false);
            copyPUMI.setEnabled(false);
        } else {
            cutPUMI.setEnabled(true);
            copyPUMI.setEnabled(true);
        }

        if (isTextOnClipboard()) {
            pastePUMI.setEnabled(true);
        } else {
            pastePUMI.setEnabled(false);
        }

        if ((textArea.getText() == null) || (textArea.getText().length() == 0)) {
            selectAllPUMI.setEnabled(false);
        } else {
            selectAllPUMI.setEnabled(true);
        }
    }

    /**
     * Enables/disables edit menu items in the popup menu.<br>
     * Check the contents of the <code>textArea</code> component and the
     * system clipboard.
     */
    private void updateMenuBar() {
        if ((exttextArea.getSelectedText() == null) ||
                (exttextArea.getSelectedText().length() == 0)) {
            cutMI.setEnabled(false);
            copyMI.setEnabled(false);
        } else {
            cutMI.setEnabled(true);
            copyMI.setEnabled(true);
        }

        if (isTextOnClipboard()) {
            pasteMI.setEnabled(true);
        } else {
            pasteMI.setEnabled(false);
        }

        if ((exttextArea.getText() == null) ||
                (exttextArea.getText().length() == 0)) {
            selectAllMI.setEnabled(false);
        } else {
            selectAllMI.setEnabled(true);
        }
    }

    /**
     * Checks whether the contents of the system clipboard can be paste into  a
     * textcomponent.
     *
     * @return true if there is contents of type text, false otherwise
     */
    private boolean isTextOnClipboard() {
        Transferable contents = null;

        try {
            contents = Toolkit.getDefaultToolkit().getSystemClipboard()
                              .getContents(this);
        } catch (IllegalStateException ise) {
            LOG.warning("Could not access the system clipboard.");
        }

        if (contents != null) {
            DataFlavor[] flavors = contents.getTransferDataFlavors();
            DataFlavor best = DataFlavor.selectBestTextFlavor(flavors);

            if (best != null) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns the (configured) editor component, ready to be used in a
     * viewer's  layout.
     *
     * @return the editor component
     *
     * @see #configureEditor(Class, Font, Dimension)
     * @see #startEdit()
     */
    public JComponent getEditorComponent() {
        if (editorComponent == null) {
            return this;
        }

        return editorComponent;
    }

    /**
     * Sets up and configures a certain kind of editor component.<br>
     *
     * @param preferredComponent DOCUMENT ME!
     * @param font DOCUMENT ME!
     * @param size DOCUMENT ME!
     *
     * @see #getEditorComponent()
     * @see #startEdit()
     */
    public void configureEditor(Class preferredComponent, Font font,
        Dimension size) {
        if (preferredComponent == JPanel.class) {
            // configures "this"
            editorComponent = this;

            if (isUsingControlledVocabulary) {
                if (cvEntryComp == null) {
                    cvEntryComp = new CVEntryComponent(JScrollPane.class);
                    cvEntryComp.setAnnotation(annotation);
                } else {
                    if (!(cvEntryComp.getEditorComponent() instanceof JScrollPane)) {
                        cvEntryComp.setDelegate(preferredComponent);
                    }
                }

                if (font != null) {
                    cvEntryComp.setFont(font);
                }

                cvEntryComp.addPopupListener();

                int w;
                int h;

                if (size == null) {
                    w = minCVWidth;
                    h = minCVHeight;
                } else {
                    w = size.width;
                    h = size.height;

                    if (w < minCVWidth) {
                        w = minCVWidth;
                    }

                    if (h < minCVHeight) {
                        h = minCVHeight;
                    }
                }

                removeAll();
                add(cvEntryComp.getEditorComponent(), BorderLayout.CENTER);
                this.setSize(w, h);
                validate();
            } else {
                if (font != null) {
                    setFont(font);
                }

                removeAll();
                add(textAreaScrollPane, BorderLayout.CENTER);

                if (size != null) {
                    setSize(size);
                }

                validate();
            }
        } else if (preferredComponent == JScrollPane.class) {
            // configure the scrollpane of either the attached-mode textfield
            // or the CVEntry list
            if (isUsingControlledVocabulary) {
                if (cvEntryComp == null) {
                    cvEntryComp = new CVEntryComponent(preferredComponent);
                    cvEntryComp.setAnnotation(annotation);
                } else {
                    if (!(cvEntryComp.getEditorComponent() instanceof JScrollPane)) {
                        cvEntryComp.setDelegate(preferredComponent);
                    }
                }

                if (font != null) {
                    cvEntryComp.setFont(font);
                }

                cvEntryComp.addPopupListener();

                if (size != null) {
                    cvEntryComp.getEditorComponent().setSize(size);
                }

                editorComponent = cvEntryComp.getEditorComponent();
            } else {
                if (font != null) {
                    setFont(font);
                }

                if (size != null) {
                    setSize(size);
                }

                editorComponent = textAreaScrollPane;
            }
        } else if (preferredComponent == JComboBox.class) {
            if (isUsingControlledVocabulary) {
                if (cvEntryComp == null) {
                    cvEntryComp = new CVEntryComponent(preferredComponent);
                    cvEntryComp.setAnnotation(annotation);
                } else {
                    if (!(cvEntryComp.getEditorComponent() instanceof JComboBox)) {
                        cvEntryComp.setDelegate(preferredComponent);
                    }
                }

                if (font != null) {
                    cvEntryComp.setFont(font);
                }

                if (size != null) {
                    cvEntryComp.getEditorComponent().setSize(size);
                }

                editorComponent = cvEntryComp.getEditorComponent();
            }
        }
    }

    /**
     * Makes the editorComponent visible and tries to grabFocus.<br>
     * This should be called after configuring and getting the editor
     * component
     *
     * @see #configureEditor(Class, Font, Dimension)
     * @see #getEditorComponent()
     */
    public void startEdit() {
        isEditing = true;

        if (editorComponent == this) {
            setVisible(true);
            requestFocus();
        } else {
            if (isUsingControlledVocabulary) {
                cvEntryComp.grabFocus();
            } else {
                editorComponent.requestFocus();
            }
        }
    }

    /**
     * Menu items' ActionPerformed handling.
     *
     * @param e the action event
     */
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();

        if (command.equals(EDIT_MENU_DET)) {
            detachEditor();
        } else if (command.equals(EDIT_MENU_ATT)) {
            if (attachable == true) {
                attachEditor();
            }
        } else if (command.equals(EDIT_MENU_CNL)) {
            cancelEdit();
        } else if (command.equals(EDIT_MENU_CMT)) {
            commitEdit();
        } else if (command.equals("cut")) {
            doCut();
        } else if (command.equals("copy")) {
            doCopy();
        } else if (command.equals("paste")) {
            doPaste();
        } else if (command.equals("selectAll")) {
            doSelectAll();
        } else {
            for (int i = 0; i < numberOfLocales; i++) {
                if (command.equals(allLocales[i].getDisplayName())) {
                    annotationLocale = allLocales[i];

                    if (attached) {
                        ImUtil.setLanguage(textArea, annotationLocale);
                        textArea.setFont(uniFont);
                    } else {
                        ImUtil.setLanguage(exttextArea, annotationLocale);
                        exttextArea.setFont(uniFont.deriveFont(20.0f));
                    }

                    break;
                }
            }
        }
    }

    /**
     * Mouse event handling for popping up the popup menu.
     *
     * @param e the mouse event
     */
    public void mouseClicked(MouseEvent e) {
    }

    /**
     * Stub
     *
     * @param e the mouse event
     */
    public void mouseEntered(MouseEvent e) {
    }

    /**
     * Stub
     *
     * @param e the mouse event
     */
    public void mouseExited(MouseEvent e) {
    }

    /**
     * Stub
     *
     * @param e the mouse event
     */
    public void mousePressed(MouseEvent e) {
        if (javax.swing.SwingUtilities.isRightMouseButton(e) ||
                e.isPopupTrigger()) {
            updatePopup();
            popupMenu.show(textArea, e.getX(), e.getY());

            popupMenu.setVisible(true);
        }
    }

    /**
     * Stub
     *
     * @param e the mouse event
     */
    public void mouseReleased(MouseEvent e) {
    }

    /**
     * M_P all e.consume() calls outcommented because the events are needed to
     * deselect a newly  made / edited annotation in EudicoAnnotationFrame
     * with the Escape key M_P 25 june 2003 Just the first outcommented. The
     * last when has to be consumed.
     *
     * @param e the key event
     */
    public void keyPressed(KeyEvent e) {
        // KB Cancel Changes
        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
            //e.consume();
            cancelEdit();
        }
        // KB Detach
        else if ((e.getKeyCode() == KeyEvent.VK_ENTER) && e.isShiftDown()) {
            if (attachable == true) {
                e.consume();

                // thread is necessary to avoid the dialog blocking events still in the eventqueue!
                if (attached) {
                    SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                detachEditor();
                            }
                        });
                } else {
                    attachEditor();
                }
            }
        }
        // KB Confirm
        else if ((e.getKeyCode() == KeyEvent.VK_ENTER) &&
                ((e.getModifiers() &
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0)) {
            e.consume();
            commitEdit();
        }
         /*else if ((e.getKeyCode() == KeyEvent.VK_ENTER) && e.isMetaDown() &&
              System.getProperty("os.name").startsWith("Mac OS")) {
          commitEdit(); // hack for osx metake
        }*/
    }

    /**
     * Stub
     *
     * @param e the key event
     */
    public void keyReleased(KeyEvent e) {
    }

    /**
     * Stub
     *
     * @param e the key event
     */
    public void keyTyped(KeyEvent e) {
    }

    /**
     * Updates menu items of the menubar by checking the system clipboard.
     *
     * @param e the menu event
     */
    public void menuSelected(MenuEvent e) {
        updateMenuBar();
    }

    /**
     * Stub
     *
     * @param e the menu event
     */
    public void menuDeselected(MenuEvent e) {
    }

    /**
     * Stub
     *
     * @param e the menu event
     */
    public void menuCanceled(MenuEvent e) {
    }

    /**
     * Updates UI elements after a change in the selected Locale.
     */
    public void updateLocale() {
        detachPUMI.setText(ElanLocale.getString("InlineEditBox.Detach"));
        commitPUMI.setText(ElanLocale.getString("InlineEditBox.Commit"));
        cancelPUMI.setText(ElanLocale.getString("InlineEditBox.Cancel"));
        cutPUMI.setText(ElanLocale.getString("InlineEditBox.Edit.Cut"));
        copyPUMI.setText(ElanLocale.getString("InlineEditBox.Edit.Copy"));
        pastePUMI.setText(ElanLocale.getString("InlineEditBox.Edit.Paste"));

        if (menuBar != null) {
            editorMenu.setText(ElanLocale.getString("InlineEditBox.Menu.Editor"));
            editMenu.setText(ElanLocale.getString("Menu.Edit"));
            selectLanguageMenu.setText(ElanLocale.getString(
                    "InlineEditBox.Menu.Select"));
            attachMI.setText(ElanLocale.getString("InlineEditBox.Attach"));
            commitMI.setText(ElanLocale.getString("InlineEditBox.Commit"));
            cancelMI.setText(ElanLocale.getString("InlineEditBox.Cancel"));
            cutMI.setText(ElanLocale.getString("InlineEditBox.Edit.Cut"));
            copyMI.setText(ElanLocale.getString("InlineEditBox.Edit.Copy"));
            pasteMI.setText(ElanLocale.getString("InlineEditBox.Edit.Paste"));
        }
    }

    //////////////////////////
    // inner class: a component for selecting an entry from a list of entries of a cv
    //////////////////////////

    /**
     * A class that provides a component for the selection of an entry from  a ControlledVocabulary.<br>
     * The current possible delegate components are a JScrollPane containing
     * a JList or a JComboBox
     *
     * @author Han Sloetjes
     */
    class CVEntryComponent implements KeyListener, ActionListener {
        /** the list containing the cv entries */
        private JList entryList;

        /** the model for the list */
        private DefaultListModel entryListModel;

        /** the scrollpane for the list */
        private JScrollPane scrollPane;

        /** popup menu for detach, commit and cancel */
        private JPopupMenu popup;

        /** moudelistener for bringing up the popup menu */
        private MouseListener popupListener;

        /** mouse listener that handles a double click on a list entry */
        private MouseListener doubleClickListener;

        /** menu items for detaching, cancelling and committing */
        private JMenuItem detachMI;

        /** menu items for detaching, cancelling and committing */
        private JMenuItem cancelMI;

        /** menu items for detaching, cancelling and committing */
        private JMenuItem commitMI;

        /** a combo box editor component */
        private JComboBox box;

        /** a model for the combo box */
        private DefaultComboBoxModel entryBoxModel;

        /**
         * the component to use for editing, either a scrollpane containing a
         * JList  or a JComboBox
         */
        private JComponent delegate;

        /**
         * the array of CV entries from the ControlledVocabulary referenced by
         * the  LinguisticType in use by the Tier containing the current
         * annotation
         */
        private CVEntry[] entries;

        /** the annotation to edit */
        private Annotation annotation;

        /**
         * Creates a new entrylist and initializes components.<br>
         * Components are being initialized depending on the type of the
         * argument.
         *
         * @param componentClass the type of component to use for edit
         *        operations
         */
        public CVEntryComponent(Class componentClass) {
            initComponents(componentClass);
        }

        /**
         * Returns the current delegate component.
         *
         * @return the delegate component for editing actions
         */
        public JComponent getEditorComponent() {
            return delegate;
        }

        /**
         * Sets which type of component should be used for editing. Can depend
         * on the kind of viewer that created the InlineEditBox  and of the
         * attached / detached state.
         *
         * @param compClass the type of component to use for editing
         */
        void setDelegate(Class compClass) {
            if (delegate.getClass() == compClass) {
                return;
            }

            if (compClass == JComboBox.class) {
                if (box == null) {
                    initComponents(compClass);
                }

                delegate = box;

                // make sure it is filled with the current entries
                entryBoxModel.removeAllElements();
                fillModel();

                if (entryList != null) {
                    box.setSelectedItem(entryList.getSelectedValue());
                }
            } else if (compClass == JScrollPane.class) {
                if (entryList == null) {
                    initComponents(compClass);
                }

                delegate = scrollPane;
                entryListModel.clear();
                fillModel();

                if (box != null) {
                    entryList.setSelectedValue(box.getSelectedItem(), true);
                }
            }
        }

        /**
         * Tries to ensure that the selected item is visible in the
         * scrollpane's viewport. Applies only to the JList component.
         */
        public void ensureSelectionIsVisible() {
            if (delegate instanceof JScrollPane && (entryList != null)) {
                entryList.ensureIndexIsVisible(entryList.getSelectedIndex());
            }
        }

        /**
         * When this list is in a detached dialog it doesn't need the popup
         * because all options are in the menu bar. Applies only to the JList
         * component.
         */
        public void removePopupListener() {
            if (entryList != null) {
                entryList.removeMouseListener(popupListener);
            }
        }

        /**
         * When this list is not in a dialog menu items for detaching,
         * committing  and cancelling need to be provided.  Applies only to
         * the JList component.
         */
        public void addPopupListener() {
            if (entryList != null) {
                MouseListener[] listeners = entryList.getMouseListeners();

                for (int i = 0; i < listeners.length; i++) {
                    if (listeners[i] == popupListener) {
                        return;
                    }
                }

                entryList.addMouseListener(popupListener);
            }
        }

        /**
         * Initializes either a list in a scrollpane (with a popup menu etc) or
         * a combo box.  Adds listeners.
         *
         * @param component the type of component to use for editing
         */
        private void initComponents(Class component) {
            if (component == JScrollPane.class) {
                if (entryList == null) {
                    entryListModel = new DefaultListModel();
                    entryList = new JList(entryListModel);
                    entryList.setFont(InlineEditBox.this.getFont());
                    entryList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                    scrollPane = new JScrollPane(entryList);

                    popup = new JPopupMenu();
                    detachMI = new JMenuItem(ElanLocale.getString(
                                "InlineEditBox.Detach"));
                    detachMI.addActionListener(this);
                    detachMI.setAccelerator(KeyStroke.getKeyStroke(
                            KeyEvent.VK_ENTER, ActionEvent.SHIFT_MASK));
                    popup.add(detachMI);
                    commitMI = new JMenuItem(ElanLocale.getString(
                                "InlineEditBox.Commit"));
                    commitMI.addActionListener(this);
                    commitMI.setAccelerator(KeyStroke.getKeyStroke(
                            KeyEvent.VK_ENTER,
                            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
                    popup.add(commitMI);
                    cancelMI = new JMenuItem(ElanLocale.getString(
                                "InlineEditBox.Cancel"));
                    cancelMI.addActionListener(this);
                    cancelMI.setAccelerator(KeyStroke.getKeyStroke(
                            KeyEvent.VK_ESCAPE, 0));
                    popup.add(cancelMI);

                    popupListener = new MouseAdapter() {
                                public void mousePressed(MouseEvent e) {
                                    if (SwingUtilities.isRightMouseButton(e) ||
                                            e.isPopupTrigger()) {
                                        CVEntryComponent.this.popup.show(CVEntryComponent.this.entryList,
                                            e.getX(), e.getY());
                                        CVEntryComponent.this.popup.setVisible(true);
                                    }
                                }
                            };

                    doubleClickListener = new MouseAdapter() {
                                public void mouseClicked(MouseEvent e) {
                                    if (e.getClickCount() > 1) {
                                        InlineEditBox.this.commitEdit();
                                    }
                                }
                            };

                    entryList.addMouseListener(popupListener);
                    entryList.addMouseListener(doubleClickListener);

                    entryList.addKeyListener(this);
                    entryList.addListSelectionListener(new ListSelectionListener() {
                            public void valueChanged(ListSelectionEvent lse) {
                                CVEntryComponent.this.ensureSelectionIsVisible();
                            }
                        });
                    delegate = scrollPane;
                }
            } else if (component == JComboBox.class) {
                if (box == null) {
                    entryBoxModel = new DefaultComboBoxModel();
                    box = new JComboBox(entryBoxModel);

                    box.addActionListener(this);
                    box.addKeyListener(this);
                    delegate = box;
                }
            }
        }

        /**
         * Sets the font for the entry list component.
         *
         * @param f the font
         */
        public void setFont(Font f) {
            if (delegate == box) {
                box.setFont(f);
            } else if (delegate == scrollPane) {
                entryList.setFont(f);
            }
        }

        /**
         * Gets the entry array with the entries in the cv referenced by the
         * linguistic type of the tier.
         *
         * @param annotation the active annotation
         */
        public void setAnnotation(Annotation annotation) {
            this.annotation = annotation;

            if (entryListModel != null) {
                entryListModel.clear();
            }

            if (entryBoxModel != null) {
                entryBoxModel.removeAllElements();
            }

            if (annotation != null) {
                TierImpl tier = (TierImpl) annotation.getTier();
                TranscriptionImpl trans = (TranscriptionImpl) tier.getParent();
                ControlledVocabulary cv = trans.getControlledVocabulary(tier.getLinguisticType()
                                                                            .getControlledVocabylaryName());

                if (cv != null) {
                    entries = cv.getEntries();
                }
            }

            fillModel();
        }

        /**
         * Fills the model of either the combo box or the list with the entries
         * of the current Controlled Vocabulary.
         */
        private void fillModel() {
            String value = null;

            if (annotation != null) {
                value = annotation.getValue();
            }

            if (delegate == scrollPane) {
                for (int i = 0; i < entries.length; i++) {
                    entryListModel.addElement(entries[i]);

                    if ((value != null) && value.equals(entries[i].getValue())) {
                        entryList.setSelectedIndex(i);
                    }
                }

                if ((entries.length > 0) && (entryList.getSelectedIndex() < 0)) {
                    entryList.setSelectedIndex(0);
                }
            } else if (delegate == box) {
                for (int i = 0; i < entries.length; i++) {
                    entryBoxModel.addElement(entries[i]);

                    if ((value != null) && value.equals(entries[i].getValue())) {
                        entryBoxModel.setSelectedItem(entries[i]);
                    }
                }

                if ((entries.length > 0) && (box.getSelectedIndex() < 0)) {
                    box.setSelectedIndex(0);
                }
            }
        }

        /**
         * Tries to grant the focus to the delegate component.
         */
        public void grabFocus() {
            if (delegate == box) {
                box.requestFocus();
            } else if (delegate == scrollPane) {
                entryList.requestFocus();
                entryList.ensureIndexIsVisible(entryList.getSelectedIndex());
            }
        }

        /**
         * Returns the currently selected entry value.
         *
         * @return the currently selected entry value or null
         */
        public String getSelectedEntryValue() {
            String value = null;

            if (delegate == scrollPane) {
                if (entryList.getSelectedValue() != null) {
                    value = ((CVEntry) entryList.getSelectedValue()).getValue();
                }
            } else if (delegate == box) {
                if (box.getSelectedItem() != null) {
                    value = ((CVEntry) box.getSelectedItem()).getValue();
                } else {
                    value = annotation.getValue();
                }
            }

            return value;
        }

        /**
         * KeyPressed handling.
         *
         * @param e the key event
         */
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                if (e.isShiftDown()) {
                    InlineEditBox.this.detachEditor();
                } /*else if (
                  (e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0){
                  InlineEditBox.this.commitEdit();
                } */
                else {
                    // in all other cases commit when the Enter key is typed
                    InlineEditBox.this.commitEdit();
                }
            } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                InlineEditBox.this.cancelEdit();
            }
        }

        /**
         * Key released handling: do nothing.
         *
         * @param e the key event
         */
        public void keyReleased(KeyEvent e) {
        }

        /**
         * Key typed handling: do nothing.
         *
         * @param e the key event
         */
        public void keyTyped(KeyEvent e) {
        }

        /**
         * Action handling.
         *
         * @param ae the action event
         */
        public void actionPerformed(ActionEvent ae) {
            if (ae.getSource() == detachMI) {
                if (attachable) {
                    if (attached) {
                        InlineEditBox.this.detachEditor();
                    } else {
                        InlineEditBox.this.attachEditor();
                    }
                }
            } else if (ae.getSource() == commitMI) {
                InlineEditBox.this.commitEdit();
            } else if (ae.getSource() == cancelMI) {
                InlineEditBox.this.cancelEdit();
            } else if (ae.getSource() == box) {
                if ((ae.getID() == ActionEvent.ACTION_PERFORMED) &&
                        (ae.getModifiers() == InputEvent.BUTTON1_MASK)) {
                    // prevent that the first click / doubleclick on the combo box
                    // causes a commit
                    if (box.isPopupVisible()) {
                        InlineEditBox.this.commitEdit();
                    }
                }
            }
        }
    }

    // end of CVEntryComponent
}
