/**
 * Copyright (c) 2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 * 
 * Contributors: 
 *   IBM - Initial API and implementation
 */
package org.eclipse.emf.common.ui.celleditor;


import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;


/**
 * An abstract base class for implementing table editing where only a single column is editable.
 * Clicking anywhere on a row or pressing enter when a row is selected edits the editable column.
 * <p><b>Provisional API.</b> Please do not use it for anything more than experimentation.
 * @since 2.5
 */
public abstract class SingleColumnTableEditor extends BasicTableEditor
  implements MouseListener, SelectionListener, TraverseListener
{
  protected int editableColumn;

  // These are used to avoid restarting editing immediately when the user ends editing
  // by clicking outside of an open editor but within the same item.
  //
  protected int editorFocusLostTime;
  protected TableItem editorFocusLostItem;

  public SingleColumnTableEditor(Table table)
  {
    this(table, table.getColumnCount() - 1);
  }

  public SingleColumnTableEditor(Table table, int editableColumn)
  {
    super(table);
    this.editableColumn = editableColumn;
    table.addMouseListener(this);
    table.addSelectionListener(this);
    table.addTraverseListener(this);
  }

  @Override
  public void dispose()
  {
    if (table != null)
    {
      table.removeMouseListener(this);
      table.removeSelectionListener(this);
      table.removeTraverseListener(this);
      table = null;
    }
    editorFocusLostItem = null;
  }

  public void widgetSelected(SelectionEvent e)
  {
    // Do nothing.
  }

  public void widgetDefaultSelected(SelectionEvent e)
  {
    TableItem item = getSelection();
    if (item != null)
    {
      edit(item, editableColumn);
    }
  }

  public void mouseDoubleClick(MouseEvent e)
  {
    // Do nothing.
  }

  public void mouseDown(MouseEvent e)
  {
    if (e.time != editorFocusLostTime)
    {
      editorFocusLostItem = null;
    }

    // If the table is not full selection style (SWT.FULL_SELECTION), the selection must be updated manually.
    //
    TableItem item = getItem(e.x, e.y);
    if (item != null && item != getSelection())
    {
      table.setSelection(item);
    }
  }

  public void mouseUp(MouseEvent e)
  {
    TableItem item = getItem(e.x, e.y);
    if (item != null && !item.equals(editorFocusLostItem))
    {
      edit(item, editableColumn);
    }
  }

  protected TableItem getItem(int x, int y)
  {
    int tableWidth = table.getClientArea().width;
    TableItem[] items = table.getItems();
    for (int row = table.getTopIndex(); row < items.length; row++)
    {
      TableItem item = items[row];
      Rectangle bounds = item.getBounds(0);
      bounds.width = tableWidth - bounds.x;
      
      if (bounds.contains(x, y))
      {
        return item;
      }
    }
    return null;
  }

  // Prevent return press from escaping to the table's container.
  // 
  public void keyTraversed(TraverseEvent e)
  {
    if (e.detail == SWT.TRAVERSE_RETURN)
    {
      e.doit = false;
    }
  }

  @Override
  protected boolean canEdit(TableItem item, int column)
  {
    return column == editableColumn;
  }

  @Override
  protected Text createTextEditor(final TableItem item, final int column)
  {
    final Text text = super.createTextEditor(item, column);
    text.addFocusListener(new FocusAdapter()
    {
      @Override
      public void focusLost(FocusEvent e)
      {
        editorFocusLostTime = e.time;
        editorFocusLostItem = item;
      }
    });

    return text;
  }
}
