/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* chris.gross@us.ibm.com - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.grid;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ControlEditor;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
/**
* <p>
* NOTE: THIS WIDGET AND ITS API ARE STILL UNDER DEVELOPMENT. THIS IS A PRE-RELEASE ALPHA
* VERSION. USERS SHOULD EXPECT API CHANGES IN FUTURE VERSIONS.
* </p>
*
* A GridEditor is a manager for a Control that appears above a cell in a Grid
* and tracks with the moving and resizing of that cell. It can be used to
* display a text widget above a cell in a Grid so that the user can edit the
* contents of that cell. It can also be used to display a button that can
* launch a dialog for modifying the contents of the associated cell.
* <p>
* @see org.eclipse.swt.custom.TableEditor
*/
public class GridEditor extends ControlEditor
{
Grid table;
GridItem item;
int column = -1;
ControlListener columnListener;
Listener resizeListener;
private Listener columnVisibleListener;
private Listener columnGroupListener;
private SelectionListener scrollListener;
private TreeListener treeListener;
/**
* Creates a TableEditor for the specified Table.
*
* @param table the Table Control above which this editor will be displayed
*/
public GridEditor(final Grid table)
{
super(table);
this.table = table;
treeListener = new TreeListener () {
final Runnable runnable = new Runnable() {
public void run() {
if (getEditor() == null || getEditor().isDisposed()) return;
if (table.isDisposed()) return;
layout();
getEditor().setVisible(true);
}
};
public void treeCollapsed(TreeEvent e) {
if (getEditor() == null || getEditor().isDisposed ()) return;
getEditor().setVisible(false);
e.display.asyncExec(runnable);
}
public void treeExpanded(TreeEvent e) {
if (getEditor() == null || getEditor().isDisposed ()) return;
getEditor().setVisible(false);
e.display.asyncExec(runnable);
}
};
table.addTreeListener(treeListener);
columnListener = new ControlListener()
{
public void controlMoved(ControlEvent e)
{
layout();
}
public void controlResized(ControlEvent e)
{
layout();
}
};
columnVisibleListener = new Listener()
{
public void handleEvent(Event event)
{
getEditor().setVisible(((GridColumn)event.widget).isVisible());
if (getEditor().isVisible()) layout();
}
};
resizeListener = new Listener()
{
public void handleEvent(Event event)
{
layout();
}
};
scrollListener = new SelectionListener()
{
public void widgetSelected(SelectionEvent e)
{
layout();
}
public void widgetDefaultSelected(SelectionEvent e)
{
}
};
columnGroupListener = new Listener()
{
public void handleEvent(Event event)
{
if (getEditor() == null || getEditor().isDisposed()) return;
getEditor().setVisible(table.getColumn(getColumn()).isVisible());
if (getEditor().isVisible()) layout();
}
};
// The following three listeners are workarounds for
// Eclipse bug 105764
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=105764
table.addListener(SWT.Resize, resizeListener);
if (table.getVerticalScrollBarProxy() != null)
{
table.getVerticalScrollBarProxy().addSelectionListener(scrollListener);
}
if (table.getHorizontalScrollBarProxy() != null)
{
table.getHorizontalScrollBarProxy().addSelectionListener(scrollListener);
}
// To be consistent with older versions of SWT, grabVertical defaults to
// true
grabVertical = true;
}
/**
* Returns the bounds of the editor.
*
* @return bounds of the editor.
*/
protected Rectangle computeBounds()
{
if (item == null || column == -1 || item.isDisposed())
return new Rectangle(0, 0, 0, 0);
Rectangle cell = item.getBounds(column);
Rectangle area = table.getClientArea();
if (cell.x < area.x + area.width)
{
if (cell.x + cell.width > area.x + area.width)
{
cell.width = area.x + area.width - cell.x;
}
}
Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, minimumHeight);
if (grabHorizontal)
{
editorRect.width = Math.max(cell.width, minimumWidth);
}
if (grabVertical)
{
editorRect.height = Math.max(cell.height, minimumHeight);
}
if (horizontalAlignment == SWT.RIGHT)
{
editorRect.x += cell.width - editorRect.width;
}
else if (horizontalAlignment == SWT.LEFT)
{
// do nothing - cell.x is the right answer
}
else
{ // default is CENTER
editorRect.x += (cell.width - editorRect.width) / 2;
}
if (verticalAlignment == SWT.BOTTOM)
{
editorRect.y += cell.height - editorRect.height;
}
else if (verticalAlignment == SWT.TOP)
{
// do nothing - cell.y is the right answer
}
else
{ // default is CENTER
editorRect.y += (cell.height - editorRect.height) / 2;
}
GridColumn c = table.getColumn(column);
if( c != null && c.isTree() ) {
int x = c.getCellRenderer().getTextBounds(item, false).x;
editorRect.x += x;
editorRect.width -= x;
}
return editorRect;
}
/**
* Removes all associations between the TableEditor and the cell in the
* table. The Table and the editor Control are <b>not</b> disposed.
*/
public void dispose()
{
if (!table.isDisposed() && this.column > -1 && this.column < table.getColumnCount())
{
GridColumn tableColumn = table.getColumn(this.column);
tableColumn.removeControlListener(columnListener);
if (tableColumn.getColumnGroup() != null){
tableColumn.getColumnGroup().removeListener(SWT.Expand, columnGroupListener);
tableColumn.getColumnGroup().removeListener(SWT.Collapse, columnGroupListener);
}
}
if (!table.isDisposed())
{
table.removeListener(SWT.Resize, resizeListener);
if (table.getVerticalScrollBarProxy() != null)
table.getVerticalScrollBarProxy().removeSelectionListener(scrollListener);
if (table.getHorizontalScrollBarProxy() != null)
table.getHorizontalScrollBarProxy().removeSelectionListener(scrollListener);
}
columnListener = null;
resizeListener = null;
table = null;
item = null;
column = -1;
super.dispose();
}
/**
* Returns the zero based index of the column of the cell being tracked by
* this editor.
*
* @return the zero based index of the column of the cell being tracked by
* this editor
*/
public int getColumn()
{
return column;
}
/**
* Returns the TableItem for the row of the cell being tracked by this
* editor.
*
* @return the TableItem for the row of the cell being tracked by this
* editor
*/
public GridItem getItem()
{
return item;
}
/**
* Sets the zero based index of the column of the cell being tracked by this
* editor.
*
* @param column the zero based index of the column of the cell being
* tracked by this editor
*/
public void setColumn(int column)
{
int columnCount = table.getColumnCount();
// Separately handle the case where the table has no TableColumns.
// In this situation, there is a single default column.
if (columnCount == 0)
{
this.column = (column == 0) ? 0 : -1;
layout();
return;
}
if (this.column > -1 && this.column < columnCount)
{
GridColumn tableColumn = table.getColumn(this.column);
tableColumn.removeControlListener(columnListener);
tableColumn.removeListener(SWT.Show, columnVisibleListener);
tableColumn.removeListener(SWT.Hide, columnVisibleListener);
this.column = -1;
}
if (column < 0 || column >= table.getColumnCount())
return;
this.column = column;
GridColumn tableColumn = table.getColumn(this.column);
tableColumn.addControlListener(columnListener);
tableColumn.addListener(SWT.Show, columnVisibleListener);
tableColumn.addListener(SWT.Hide, columnVisibleListener);
if (tableColumn.getColumnGroup() != null){
tableColumn.getColumnGroup().addListener(SWT.Expand, columnGroupListener);
tableColumn.getColumnGroup().addListener(SWT.Collapse, columnGroupListener);
}
layout();
}
/**
* Sets the item that this editor will function over.
*
* @param item editing item.
*/
public void setItem(GridItem item)
{
this.item = item;
layout();
}
/**
* Specify the Control that is to be displayed and the cell in the table
* that it is to be positioned above.
* <p>
* Note: The Control provided as the editor <b>must</b> be created with its
* parent being the Table control specified in the TableEditor constructor.
*
* @param editor the Control that is displayed above the cell being edited
* @param item the TableItem for the row of the cell being tracked by this
* editor
* @param column the zero based index of the column of the cell being
* tracked by this editor
*/
public void setEditor(Control editor, GridItem item, int column)
{
setItem(item);
setColumn(column);
setEditor(editor);
layout();
}
/**
* {@inheritDoc}
*/
public void layout()
{
if (table.isDisposed())
return;
if (item == null || item.isDisposed())
return;
int columnCount = table.getColumnCount();
if (columnCount == 0 && column != 0)
return;
if (columnCount > 0 && (column < 0 || column >= columnCount))
return;
boolean hadFocus = false;
if (getEditor() == null || getEditor().isDisposed())
return;
if (getEditor().getVisible())
{
hadFocus = getEditor().isFocusControl();
} // this doesn't work because
// resizing the column takes the focus away
// before we get here
getEditor().setBounds(computeBounds());
if (hadFocus)
{
if (getEditor() == null || getEditor().isDisposed())
return;
getEditor().setFocus();
}
}
}