
package uk.co.wingpath.gui;

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import uk.co.wingpath.event.*;

public class WCellEditor
    extends DefaultCellEditor
{
    private final TextEditor editor;
    private final JTextField field;
    private int row;
    private int modelColumn;
    private boolean editing;
    private String value;
    private TableVerifier tableVerifier;
    private MirrorField mirror;
    private final ValueEventSource listeners;
    private boolean hasFocus;
    private CellMirrored mirrored;

    private JTable table;

    private class CellMirrored
        implements MirroredField
    {
        private final JTable table;
        private final int row;
        private final int viewColumn;
        private final int modelColumn;
        private final Verifier verifier;

        CellMirrored (final JTable table, final int row, final int viewColumn)
        {
            this.table = table;
            this.row = row;
            this.viewColumn = viewColumn;
            modelColumn = table.convertColumnIndexToModel (viewColumn);

            verifier =
                new Verifier ()
                {
                    public String verify (String str, boolean isChanging)
                    {
                        if (tableVerifier == null)
                            return str;
                        return tableVerifier.verify (str, row, modelColumn,
                            isChanging);
                    }
                };
        }

        @Override
        public Verifier getVerifier ()
        {
            return verifier;
        }

        private void requestFocus ()
        {
            table.requestFocusInWindow ();
            table.editCellAt (row, viewColumn);
        }

        @Override
        public void restoreFocus ()
        {
            table.requestFocusInWindow ();
            table.editCellAt (row, viewColumn);
        }

        @Override
        public void saveValue (String val)
        {
            if (editing && this == mirrored)
            {
                // The user was editing in the mirror field and then clicked
                // on the table cell that was being mirrored.
                value = val;
                editor.setValue (val);
            }
            else
            {
                table.setValueAt (val, row, viewColumn);
            }
            if (listeners != null)
                listeners.fireValueChanged (this, false);
        }

        @Override
        public String resetValue ()
        {
            return table.getValueAt (row, viewColumn).toString ();
        }

        @Override
        public String getMirrorLabel ()
        {
            TableModel model = table.getModel ();
            if (model instanceof HasCellLabels)
            {
                HasCellLabels hcl = (HasCellLabels) model;
                return hcl.getCellLabel (row, modelColumn);
            }
            return "";
        }
    }

    public WCellEditor (ValueEventSource listeners)
    {
        super (new JTextField ());
        field = (JTextField) getComponent ();
        this.listeners = listeners;
        editor = new TextEditor (field, "", listeners);
        editing = false;
        table = null;
        row = -1;
        modelColumn = -1;
        value = null;
        tableVerifier = null;
        mirror = null;
        mirrored = null;
        hasFocus = false;

        editor.setVerifier (
            new Verifier ()
            {
                public String verify (String str, boolean isChanging)
                {
                    if (tableVerifier == null)
                        return str;
                    return tableVerifier.verify (str, row, modelColumn,
                        isChanging);
                }
            });

        InputMap inputMap = field.getInputMap (JComponent.WHEN_FOCUSED);
        inputMap.put (KeyStroke.getKeyStroke (KeyEvent.VK_F2, 0), "mirror");
        ActionMap actionMap = field.getActionMap ();
        actionMap.put ("mirror",
            new AbstractAction ()
            {
                public void actionPerformed (ActionEvent ev)
                {
                    if (mirror != null && mirror.isMirrored (mirrored))
                        mirror.requestFocus ();
                }
            });

        // The following line was copied from the default cell editor in
        // JTable.java. It's needed not only to make our cell editor look like
        // the standard one, but also to prevent the bottoms of characters
        // being clipped when using the Windows look & feel.
        field.setBorder (new LineBorder (Color.black));

        field.addFocusListener (new FocusListener ()
        {
            public void focusGained (FocusEvent e)
            {
                if (e.isTemporary () || hasFocus)
                    return;
                hasFocus = true;
                field.selectAll ();
                if (mirror != null && mirrored != null &&
                    mirrored.row < mirrored.table.getRowCount ())
                {
                    editor.setMirror (mirror, mirrored);
                    mirror.setMirrored (mirrored);
                    mirror.displayValue (field.getText ());
                }
            }

            public void focusLost (FocusEvent e)
            {
                if (e.isTemporary () || !hasFocus)
                    return;
                hasFocus = false;
                // Release association with the mirror if the focus is
                // moving anywhere other than the mirror.
                if (mirror != null && mirror.isMirrored (mirrored) &&
                    !mirror.isFocusTarget (e.getOppositeComponent ()))
                {
                    mirror.setMirrored (null);
                }
/*
                field.setCaretPosition (0);
*/
            }
        });

    }

    public void setVerifier (TableVerifier tableVerifier)
    {
        this.tableVerifier = tableVerifier;
    }

    public void setMirror (MirrorField mirror)
    {
        this.mirror = mirror;
    }

    @Override
    public void cancelCellEditing ()
    {
        editing = false;
        super.cancelCellEditing ();
    }

    @Override
    public boolean stopCellEditing ()
    {
        if (!editing)
            return super.stopCellEditing ();
        if (!editor.checkValue ())
            return false;
        editing = false;
        return super.stopCellEditing ();
    }

    @Override
    public Component getTableCellEditorComponent (JTable table,
          Object value, boolean isSelected, int row, int viewColumn)
    {
        this.value = value.toString ();
        editor.setValue (this.value);
        editing = true;
        this.table = table;
        this.row = row;
        modelColumn = table.convertColumnIndexToModel (viewColumn);
        Component comp = super.getTableCellEditorComponent (table,
            value, isSelected, row, viewColumn);
        if (mirrored == null ||
            mirrored.table != table ||
            mirrored.row != row ||
            mirrored.modelColumn != modelColumn)
        {
            mirrored = new CellMirrored (table, row, viewColumn);
        }
        else
        {
            // The user was editing in the mirror field and then clicked
            // on the table cell that was being mirrored.
        }
        return comp;
    }

    public boolean hasValueChanged ()
    {
        if (!editing)
            return false;
        return editor.hasValueChanged (value);
    }
}

