blob: d49c4380ebbc803469e4589471cbbf6da3511661 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.ui.swt.basic.table;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeSet;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.scout.commons.CollectionUtility;
import org.eclipse.scout.commons.StringUtility;
import org.eclipse.scout.commons.beans.IPropertyObserver;
import org.eclipse.scout.commons.dnd.TransferObject;
import org.eclipse.scout.commons.holders.Holder;
import org.eclipse.scout.commons.job.JobEx;
import org.eclipse.scout.rt.client.ClientSyncJob;
import org.eclipse.scout.rt.client.ui.IDNDSupport;
import org.eclipse.scout.rt.client.ui.IEventHistory;
import org.eclipse.scout.rt.client.ui.action.ActionUtility;
import org.eclipse.scout.rt.client.ui.action.IActionFilter;
import org.eclipse.scout.rt.client.ui.action.keystroke.IKeyStroke;
import org.eclipse.scout.rt.client.ui.action.menu.TableMenuType;
import org.eclipse.scout.rt.client.ui.action.menu.root.IContextMenu;
import org.eclipse.scout.rt.client.ui.basic.table.IHeaderCell;
import org.eclipse.scout.rt.client.ui.basic.table.ITable;
import org.eclipse.scout.rt.client.ui.basic.table.ITableRow;
import org.eclipse.scout.rt.client.ui.basic.table.RowIndexComparator;
import org.eclipse.scout.rt.client.ui.basic.table.TableEvent;
import org.eclipse.scout.rt.client.ui.basic.table.TableListener;
import org.eclipse.scout.rt.client.ui.basic.table.columns.IColumn;
import org.eclipse.scout.rt.shared.security.CopyToClipboardPermission;
import org.eclipse.scout.rt.shared.services.common.security.ACCESS;
import org.eclipse.scout.rt.ui.swt.ISwtEnvironment;
import org.eclipse.scout.rt.ui.swt.SwtMenuUtility;
import org.eclipse.scout.rt.ui.swt.action.menu.SwtScoutContextMenu;
import org.eclipse.scout.rt.ui.swt.basic.SwtScoutComposite;
import org.eclipse.scout.rt.ui.swt.basic.table.celleditor.TableEditingSupport;
import org.eclipse.scout.rt.ui.swt.ext.table.TableEx;
import org.eclipse.scout.rt.ui.swt.ext.table.util.TableRolloverSupport;
import org.eclipse.scout.rt.ui.swt.extension.UiDecorationExtensionPoint;
import org.eclipse.scout.rt.ui.swt.form.fields.AbstractSwtScoutDndSupport;
import org.eclipse.scout.rt.ui.swt.form.fields.ISwtScoutDndSupport;
import org.eclipse.scout.rt.ui.swt.keystroke.ISwtKeyStroke;
import org.eclipse.scout.rt.ui.swt.util.SwtTransferObject;
import org.eclipse.scout.rt.ui.swt.util.SwtUtility;
import org.eclipse.scout.rt.ui.swt.util.UiRedrawHandler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
/**
* <h3>SwtScoutTable</h3> ...
* knownIssues - multi column sorting is not supported, unable to get any key
* mask in the selection event.
* <p>
* - multi line support in headers is not supported by swt.
* <p>
* - multi line support in row texts is not supported so far. Might probably be done by customized table rows.
*
* @since 1.0.0 28.03.2008
*/
public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScoutTable {
private P_ScoutTableListener m_scoutTableListener;
private UiRedrawHandler m_redrawHandler;
private Listener m_autoResizeColumnListener;
private Listener m_columnListener = new P_TableColumnListener();
private SelectionListener m_columnSortListener = new P_ColumnSortListener();
private TableColumnManager m_columnManager = new TableColumnManager();
private int[] m_uiColumnOrder;
private Menu m_contextMenu;
private Menu m_headerMenu;
private TableViewer m_viewer;
private ISwtKeyStroke[] m_keyStrokes;
private ClientSyncJob m_storeColumnWidthsJob;
private TableKeyboardNavigationSupport m_keyboardNavigationSupport;
private TableMouseMoveSelectionSupport m_mouseMoveSelectionSupport;
private ISwtScoutDndSupport m_dndSupport;
public SwtScoutTable() {
}
@Override
protected void initializeSwt(Composite parent) {
m_redrawHandler = new UiRedrawHandler(parent);
int style;
if (getScoutObject() != null && getScoutObject().isMultiSelect()) {
style = SWT.MULTI;
}
else {
style = SWT.SINGLE;
}
style |= SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.BORDER;
TableEx table = getEnvironment().getFormToolkit().createTable(parent, style, getScoutObject());
table.setLinesVisible(false);
table.setHeaderVisible(true);
new TableRolloverSupport(table);
table.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (m_headerMenu != null && !m_headerMenu.isDisposed()) {
m_headerMenu.dispose();
}
if (m_contextMenu != null && !m_contextMenu.isDisposed()) {
m_contextMenu.dispose();
}
}
});
TableViewer viewer = new P_TableViewerEx(table);
viewer.setUseHashlookup(true);
setSwtTableViewer(viewer);
setSwtField(table);
// header menu
m_headerMenu = new Menu(viewer.getTable().getShell(), SWT.POP_UP);
table.addMenuDetectListener(new P_SwtHeaderMenuDetectListener());
// ui listeners
viewer.addSelectionChangedListener(new P_SwtSelectionListener());
P_SwtTableListener swtTableListener = new P_SwtTableListener();
table.addListener(SWT.MouseDown, swtTableListener);
table.addListener(SWT.MouseUp, swtTableListener);
table.addListener(SWT.MouseDoubleClick, swtTableListener);
table.addListener(SWT.MenuDetect, swtTableListener);
table.addListener(SWT.KeyUp, swtTableListener);
// context menu
m_contextMenu = new SwtScoutContextMenu(getSwtField().getShell(), getScoutObject().getContextMenu(), getEnvironment()).getSwtMenu();
// CTRL-C listener to copy selected rows into clipboard)
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if ((e.stateMask & SWT.MOD1) == 0 || e.keyCode != ((int) 'c')) {
return;
}
// CTRL-C event
TransferObject scoutTransferable = handleSwtCopyRequest();
if (scoutTransferable == null) {
return;
}
SwtTransferObject[] swtTransferables = SwtUtility.createSwtTransferables(scoutTransferable);
if (swtTransferables.length == 0) {
return;
}
Clipboard clipboard = new Clipboard(getEnvironment().getDisplay());
try {
Transfer[] dataTypes = new Transfer[swtTransferables.length];
Object[] data = new Object[swtTransferables.length];
for (int i = 0; i < swtTransferables.length; i++) {
dataTypes[i] = swtTransferables[i].getTransfer();
data[i] = swtTransferables[i].getData();
}
clipboard.setContents(data, dataTypes);
}
finally {
clipboard.dispose();
}
}
});
// Install tooltip support for the table.
ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.RECREATE);
}
@Override
public boolean isDisposed() {
return getSwtField() == null || getSwtField().isDisposed();
}
private void initializeTableModel() {
getSwtTableViewer().setContentProvider(ArrayContentProvider.getInstance());
getSwtTableViewer().setInput(getScoutObject().getFilteredRows());
}
public TableColumnManager getColumnManager() {
return m_columnManager;
}
public void initializeColumns() {
m_redrawHandler.pushControlChanging();
try {
for (TableColumn col : getSwtField().getColumns()) {
col.dispose();
}
// SWT bug 43910: Create an invisible dummy column to omit SWT indention bug.
TableColumn dummyCol = new TableColumn(getSwtField(), SWT.LEFT);
dummyCol.setWidth(0);
dummyCol.setResizable(false);
dummyCol.setMoveable(false);
boolean sortEnabled = false;
List<IColumn<?>> scoutColumnsOrdered;
if (getScoutObject() != null) {
scoutColumnsOrdered = getScoutObject().getColumnSet().getVisibleColumns();
sortEnabled = getScoutObject().isSortEnabled();
}
else {
scoutColumnsOrdered = CollectionUtility.emptyArrayList();
}
if (m_columnManager == null) {
m_columnManager = new TableColumnManager();
}
m_columnManager.initialize(scoutColumnsOrdered);
boolean hasEditors = false;
for (IColumn<?> scoutColumn : scoutColumnsOrdered) {
IHeaderCell headerCell = scoutColumn.getHeaderCell();
int style = SwtUtility.getHorizontalAlignment(headerCell.getHorizontalAlignment());
final TableViewerColumn viewerColumn = new TableViewerColumn(getSwtTableViewer(), style);
TableColumn swtColumn = viewerColumn.getColumn();
swtColumn.setData(KEY_SCOUT_COLUMN, scoutColumn);
// Install the column's LabelProvider.
viewerColumn.setLabelProvider(new SwtScoutCellLabelProvider(scoutColumn, getEnvironment()));
// Install editable support.
if (scoutColumn.isEditable()) {
viewerColumn.setEditingSupport(new TableEditingSupport(getSwtTableViewer(), swtColumn, getEnvironment()));
hasEditors = true;
}
swtColumn.setMoveable(true);
swtColumn.setToolTipText(headerCell.getTooltipText());
updateHeaderText(swtColumn, scoutColumn);
swtColumn.setWidth(scoutColumn.getWidth());
if (scoutColumn.isFixedWidth()) {
swtColumn.setResizable(false);
}
if (headerCell.isSortActive()) {
getSwtField().setSortColumn(swtColumn);
getSwtField().setSortDirection(headerCell.isSortAscending() ? SWT.UP : SWT.DOWN);
}
if (sortEnabled) {
swtColumn.addSelectionListener(m_columnSortListener);
}
swtColumn.addListener(SWT.Move, m_columnListener);
swtColumn.addListener(SWT.Resize, m_columnListener);
}
// increase row height when editors are present.
if (hasEditors) {
getSwtTableViewer().getTable().addListener(SWT.MeasureItem, new Listener() {
@Override
public void handleEvent(Event event) {
event.height = Math.max(event.height, UiDecorationExtensionPoint.getLookAndFeel().getLogicalGridLayoutRowHeight());
}
});
}
m_uiColumnOrder = getSwtField().getColumnOrder();
}
finally {
m_redrawHandler.popControlChanging();
}
}
@Override
protected void attachScout() {
super.attachScout();
if (getScoutObject() == null) {
return;
}
//table listener
if (m_scoutTableListener == null) {
m_scoutTableListener = new P_ScoutTableListener();
getScoutObject().addUITableListener(m_scoutTableListener);
}
initializeColumns();
initializeTableModel();
setHeaderVisibleFromScout(getScoutObject().isHeaderVisible());
setSelectionFromScout(getScoutObject().getSelectedRows());
updateKeyStrokeFormScout();
updateKeyboardNavigationFromScout();
updateAutoResizeColumnsFromScout();
if (m_dndSupport == null) {
m_dndSupport = new P_DndSupport(getScoutObject(), getScoutObject(), getSwtField(), getEnvironment());
}
if (m_mouseMoveSelectionSupport == null && UiDecorationExtensionPoint.getLookAndFeel().isTableMouseMoveSelectionSupportEnabled()) {
m_mouseMoveSelectionSupport = new P_MouseMoveSelectionSupport(getSwtField(), m_dndSupport);
}
//handle events from recent history
final IEventHistory<TableEvent> h = getScoutObject().getEventHistory();
if (h != null) {
getEnvironment().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
for (TableEvent e : h.getRecentEvents()) {
handleScoutTableEventInSwt(e);
}
}
});
}
}
@Override
protected void detachScout() {
super.detachScout();
if (getScoutObject() != null) {
if (m_scoutTableListener != null) {
getScoutObject().removeTableListener(m_scoutTableListener);
m_scoutTableListener = null;
}
}
removeAutoResizeColumnListener();
}
@Override
public TableEx getSwtField() {
return (TableEx) super.getSwtField();
}
@Override
public TableViewer getSwtTableViewer() {
return m_viewer;
}
public void setSwtTableViewer(TableViewer viewer) {
m_viewer = viewer;
}
public ITableRow getSwtSelectedRow() {
return CollectionUtility.firstElement(getSwtSelectedRows());
}
public List<ITableRow> getSwtSelectedRows() {
TreeSet<ITableRow> sortedRows = new TreeSet<ITableRow>(new RowIndexComparator());
StructuredSelection uiSelection = (StructuredSelection) getSwtTableViewer().getSelection();
Iterator uiSelectionIt = uiSelection.iterator();
while (uiSelectionIt.hasNext()) {
sortedRows.add((ITableRow) uiSelectionIt.next());
}
return CollectionUtility.arrayList(sortedRows);
}
protected void updateKeyStrokeFormScout() {
// remove old
if (m_keyStrokes != null) {
for (ISwtKeyStroke swtKeyStroke : m_keyStrokes) {
getEnvironment().removeKeyStroke(getSwtField(), swtKeyStroke);
}
}
// add new
List<ISwtKeyStroke> newSwtKeyStrokes = new ArrayList<ISwtKeyStroke>();
for (IKeyStroke scoutKeyStroke : getScoutObject().getKeyStrokes()) {
ISwtKeyStroke[] swtStrokes = SwtUtility.getKeyStrokes(scoutKeyStroke, getEnvironment());
for (ISwtKeyStroke swtStroke : swtStrokes) {
getEnvironment().addKeyStroke(getSwtField(), swtStroke);
newSwtKeyStrokes.add(swtStroke);
}
}
m_keyStrokes = newSwtKeyStrokes.toArray(new ISwtKeyStroke[newSwtKeyStrokes.size()]);
}
protected void updateKeyboardNavigationFromScout() {
if (getScoutObject().hasKeyboardNavigation()) {
if (m_keyboardNavigationSupport == null) {
m_keyboardNavigationSupport = new P_KeyBoardNavigationSupport(getSwtField());
}
}
else {
if (m_keyboardNavigationSupport != null) {
m_keyboardNavigationSupport.dispose();
m_keyboardNavigationSupport = null;
}
}
}
private void updateAutoResizeColumnsFromScout() {
if (getSwtField() != null && !getSwtField().getParent().isDisposed()) {
Composite parent = getSwtField().getParent();
if (getScoutObject().isAutoResizeColumns()) {
if (m_autoResizeColumnListener == null) {
m_autoResizeColumnListener = new P_SwtResizeListener();
parent.addListener(SWT.Resize, m_autoResizeColumnListener);
//If the parent has already been resized no event will be fired anymore.
//So it is necessary to request an auto resizing of the columns manually. (Bugzilla 355855)
scheduleHandleAutoResizeColumn();
}
}
else {
removeAutoResizeColumnListener();
}
}
}
private void scheduleHandleAutoResizeColumn() {
getSwtField().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
handleAutoSizeColumns();
}
});
}
private void removeAutoResizeColumnListener() {
if (m_autoResizeColumnListener == null) {
return;
}
Composite parent = getSwtField().getParent();
parent.removeListener(SWT.Resize, m_autoResizeColumnListener);
m_autoResizeColumnListener = null;
}
/**
* scout property observer
*/
@Override
protected void handleScoutPropertyChange(String propName, Object newValue) {
if (propName.equals(ITable.PROP_HEADER_VISIBLE)) {
setHeaderVisibleFromScout(((Boolean) newValue).booleanValue());
}
else if (propName.equals(ITable.PROP_KEY_STROKES)) {
updateKeyStrokeFormScout();
}
else if (propName.equals(ITable.PROP_KEYBOARD_NAVIGATION)) {
updateKeyboardNavigationFromScout();
}
else if (propName.equals(ITable.PROP_AUTO_RESIZE_COLUMNS)) {
updateAutoResizeColumnsFromScout();
}
else if (propName.equals(ITable.PROP_SCROLL_TO_SELECTION)) {
updateScrollToSelectionFromScout();
}
}
/**
* scout table observer
*/
protected boolean isHandleScoutTableEvent(List<? extends TableEvent> events) {
for (TableEvent element : events) {
switch (element.getType()) {
case TableEvent.TYPE_REQUEST_FOCUS:
case TableEvent.TYPE_REQUEST_FOCUS_IN_CELL:
case TableEvent.TYPE_ROWS_INSERTED:
case TableEvent.TYPE_ROWS_UPDATED:
case TableEvent.TYPE_ROWS_DELETED:
case TableEvent.TYPE_ALL_ROWS_DELETED:
case TableEvent.TYPE_ROW_ORDER_CHANGED:
case TableEvent.TYPE_ROW_FILTER_CHANGED:
case TableEvent.TYPE_COLUMN_ORDER_CHANGED:
case TableEvent.TYPE_COLUMN_HEADERS_UPDATED:
case TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED:
case TableEvent.TYPE_ROWS_SELECTED:
case TableEvent.TYPE_SCROLL_TO_SELECTION: {
return true;
}
}
}
return false;
}
protected void handleScoutTableEventInSwt(TableEvent e) {
if (isDisposed()) {
return;
}
SwtScoutTableEvent swtTableEvent = null;
/*
* check the scout observer to filter all events that are used here
* @see isHandleScoutTableEvent()
*/
switch (e.getType()) {
case TableEvent.TYPE_REQUEST_FOCUS: {
getSwtField().setFocus();
break;
}
case TableEvent.TYPE_REQUEST_FOCUS_IN_CELL: {
// start edit-mode when entering editable cell by traversal key.
int swtCol = -1;
TableColumn[] swtColumns = getSwtField().getColumns();
for (int c = 0; c < swtColumns.length; c++) {
if (swtColumns[c].getData(KEY_SCOUT_COLUMN) == e.getFirstColumn()) {
swtCol = c;
break;
}
}
ITableRow scoutRow = e.getFirstRow();
if (scoutRow != null && swtCol >= 0) {
getSwtTableViewer().editElement(scoutRow, swtCol);
}
break;
}
case TableEvent.TYPE_SCROLL_TO_SELECTION: {
scrollToSelection();
break;
}
case TableEvent.TYPE_ROWS_DELETED:
getSwtTableViewer().remove(e.getRows().toArray());
swtTableEvent = new SwtScoutTableEvent();
break;
case TableEvent.TYPE_ALL_ROWS_DELETED:
getSwtTableViewer().getTable().removeAll();
swtTableEvent = new SwtScoutTableEvent();
break;
case TableEvent.TYPE_ROWS_INSERTED:
case TableEvent.TYPE_ROWS_UPDATED:
case TableEvent.TYPE_ROW_FILTER_CHANGED:
case TableEvent.TYPE_ROW_ORDER_CHANGED: {
swtTableEvent = new SwtScoutTableEvent();
break;
}
case TableEvent.TYPE_COLUMN_ORDER_CHANGED:
break;
case TableEvent.TYPE_COLUMN_HEADERS_UPDATED:
headerUpdateFromScout();
break;
case TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED: {
// re-install columns
initializeColumns();
if (getScoutObject().isAutoResizeColumns()) {
handleAutoSizeColumns();
}
swtTableEvent = new SwtScoutTableEvent();
break;
}
case TableEvent.TYPE_ROWS_SELECTED: {
setSelectionFromScout(e.getRows());
break;
}
}
//
if (swtTableEvent != null) {
getSwtTableViewer().setInput(getScoutObject().getFilteredRows());
}
// refresh selection, indexes might have changed
switch (e.getType()) {
case TableEvent.TYPE_ROW_FILTER_CHANGED:
// Update column title if filter changed (mark column as filtered)
for (TableColumn swtCol : getSwtField().getColumns()) {
updateHeaderText(swtCol);
}
setSelectionFromScout(e.getTable().getSelectedRows());
break;
}
}
private void updateHeaderText(TableColumn swtCol) {
if (swtCol == null) {
return;
}
Object data = swtCol.getData(KEY_SCOUT_COLUMN);
if (data instanceof IColumn<?>) {
updateHeaderText(swtCol, (IColumn<?>) data);
}
}
private void updateHeaderText(TableColumn swtCol, IColumn<?> scoutCol) {
IHeaderCell cell = scoutCol.getHeaderCell();
String text = cell.getText();
if (text == null) {
text = "";
}
if (scoutCol.isColumnFilterActive()) {
text = "((" + text + "))";
}
swtCol.setText(text);
}
private void updateScrollToSelectionFromScout() {
if (getScoutObject().isScrollToSelection()) {
scrollToSelection();
}
}
protected void scrollToSelection() {
getSwtField().showSelection();
}
protected void setHeaderVisibleFromScout(boolean headerVisible) {
getSwtField().setHeaderVisible(headerVisible);
}
@Override
public void setEnabledFromScout(boolean enabledFromScout) {
getSwtField().setReadOnly(!enabledFromScout);
}
protected void setSelectionFromScout(List<? extends ITableRow> modelSelection) {
if (getSwtField().isDisposed()) {
return;
}
List<ITableRow> uiSelection = getSwtSelectedRows();
if (CollectionUtility.equalsCollection(uiSelection, modelSelection)) {
// no change
return;
}
else {
if (modelSelection == null) {
modelSelection = Collections.emptyList();
}
getSwtTableViewer().setSelection(new StructuredSelection(modelSelection.toArray(new ITableRow[modelSelection.size()])), true);
if (CollectionUtility.hasElements(modelSelection)) {
getSwtTableViewer().reveal(CollectionUtility.firstElement(modelSelection));
}
}
//ticket 96051
if (getScoutObject().isScrollToSelection()) {
scrollToSelection();
}
}
protected void setContextColumnFromSwt(TableColumn swtColumn) {
if (getScoutObject() != null && swtColumn != null) {
//try to find correct location, since TableColumn has NO x,y and is not a Control!
Point pDisp = getSwtField().toDisplay(-getSwtField().getHorizontalBar().getSelection(), 0);
for (TableColumn c : getSwtField().getColumns()) {
if (c == swtColumn) {
break;
}
pDisp.x += c.getWidth();
}
getEnvironment().setPopupOwner(getSwtField(), new Rectangle(pDisp.x - 2, pDisp.y, 1, getSwtField().getHeaderHeight()));
// notify Scout
final IColumn finalCol = (IColumn<?>) swtColumn.getData(KEY_SCOUT_COLUMN);
Runnable t = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().setContextColumnFromUI(finalCol);
}
};
getEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
private int getVisualCellIndex(TableItem tableItem, int columnIndex) {
int visualCellIndex = columnIndex;
final int[] columnOrder = tableItem.getParent().getColumnOrder();
for (int element : columnOrder) {
if (element == columnIndex) {
visualCellIndex = columnIndex;
}
}
return visualCellIndex;
}
/**
* @param p
* is the location of the Table control (i.e. not scrollbar adjusted)
*/
private TableColumn getSwtColumnAt(Point p) {
Table table = getSwtTableViewer().getTable();
int x = p.x + getSwtField().getHorizontalBar().getSelection();
int[] order = table.getColumnOrder();
for (int index : order) {
// loop over columns according current display-order
TableColumn c = table.getColumn(index);
if (c != null) {
if (x >= 0 && x <= c.getWidth()) {
return c;
}
x = x - c.getWidth();
}
}
return null;
}
protected void setSelectionFromSwt(final StructuredSelection selection) {
if (getUpdateSwtFromScoutLock().isAcquired()) {
return;
}
//
if (getScoutObject() != null) {
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
try {
addIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_ROWS_SELECTED);
//
getScoutObject().getUIFacade().setSelectedRowsFromUI(SwtUtility.getItemsOfSelection(ITableRow.class, selection));
}
finally {
removeIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_ROWS_SELECTED);
}
}
};
getEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
protected void headerUpdateFromScout() {
getSwtField().setSortColumn(null);
for (TableColumn col : getSwtField().getColumns()) {
Object data = col.getData(KEY_SCOUT_COLUMN);
if (data instanceof IColumn<?>) {
IColumn<?> cell = (IColumn<?>) data;
if (cell.isSortExplicit()) {
getSwtField().setSortColumn(col);
getSwtField().setSortDirection(cell.isSortAscending() ? SWT.UP : SWT.DOWN);
}
updateHeaderText(col);
}
}
}
protected void handleSwtRowClick(final ITableRow row, final int swtMouseButton) {
if (getScoutObject() != null) {
if (row != null) {
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().fireRowClickFromUI(row, SwtUtility.swtToScoutMouseButton(swtMouseButton));
}
};
getEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
}
protected void handleSwtRowAction(final ITableRow row) {
if (getScoutObject() != null) {
if (!getScoutObject().isCheckable() && row != null) {
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().fireRowActionFromUI(row);
}
};
getEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
}
/**
* TODO: this method is currently not used: When swt table supports styled or html cells, add a hyperlink listener
* there
*/
protected void handleSwtHyperlinkAction(final ITableRow row, final IColumn col, final URL url) {
if (getScoutObject() != null && row != null) {
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().fireHyperlinkActionFromUI(row, col, url);
}
};
getEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
/**
* <p>
* Distributes the table width to the columns considering column weights of model. <br/>
* The initial widths of the columns are used as weights as well as the minimum widths. <br/>
* Empty space will be distributed weighted.
* </p>
*/
protected void handleAutoSizeColumns() {
if (getSwtField() == null || getSwtField().isDisposed()) {
return;
}
int totalWidth = getSwtField().getClientArea().width;
if (getSwtField().getVerticalBar() != null && getSwtField().getVerticalBar().getVisible()) {
// totalWidth -= getSwtField().getVerticalBar().getSize().x;
}
if (totalWidth < 32) {
//either not showing or not yet layouted
return;
}
int totalWeight = 0;
int actualWidth = 0;
HashMap<TableColumn, Integer> columnWeights = new HashMap<TableColumn, Integer>();
for (TableColumn col : getSwtField().getColumns()) {
if (col == null || col.isDisposed()) {
continue;
}
actualWidth += col.getWidth();
Object data = col.getData(SwtScoutTable.KEY_SCOUT_COLUMN);
if (data instanceof IColumn<?> && !((IColumn<?>) data).isFixedWidth()) {
int width = ((IColumn<?>) data).getInitialWidth();
columnWeights.put(col, width);
totalWeight += width;
}
else {
totalWidth -= col.getWidth();
}
}
//If the columns already have the correct size there is no need to recalculate it
if (actualWidth == totalWidth) {
return;
}
double factor = (double) totalWidth / (double) totalWeight;
if (factor < 1) {
factor = 1;
}
int i = 0;
for (Entry<TableColumn, Integer> entry : columnWeights.entrySet()) {
if (i < columnWeights.size() - 1) {
int width = (int) (factor * entry.getValue().intValue());
entry.getKey().setWidth(width);
totalWidth -= width;
i++;
}
else {
//Column must not get smaller than initial width.
int width = Math.max(totalWidth, entry.getValue());
entry.getKey().setWidth(width);
}
}
}
protected void handleSwtColumnResized(TableColumn column) {
if (column.isDisposed()) {
return;
}
if (!column.getParent().isVisible()) {
return;
}
if (getUpdateSwtFromScoutLock().isAcquired()) {
return;
}
//
final int width = column.getWidth();
final IColumn<?> scoutColumn = (IColumn<?>) column.getData(KEY_SCOUT_COLUMN);
if (scoutColumn != null) {
if (scoutColumn.getWidth() != width) {
//Cancel already scheduled resize job to protect the model from too many resize events.
if (m_storeColumnWidthsJob != null) {
m_storeColumnWidthsJob.cancel();
}
/*
* imo, 04.06.2009: added swt-side optimistic lock check Method
* autoSizeColumns indirectly calls this code by setting the width on a
* table column if this code calls scout using synchronizer it may
* invoke pending swt jobs while waiting, which can execute a dispose
* form job. After that job all columns are disposed. This results in
* WidgetDisposed exceptions on line with entry.getKey().setWidth(...)
*/
m_storeColumnWidthsJob = new ClientSyncJob("Store column widths", getEnvironment().getClientSession()) {
@Override
protected IStatus runStatus(IProgressMonitor monitor) {
try {
addIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED);
//
getScoutObject().getUIFacade().setColumnWidthFromUI(scoutColumn, width);
}
finally {
removeIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED);
}
return Status.OK_STATUS;
}
};
m_storeColumnWidthsJob.schedule(400);
}
}
}
protected TransferObject handleSwtCopyRequest() {
if (getUpdateSwtFromScoutLock().isAcquired()) {
return null;
}
final Holder<TransferObject> result = new Holder<TransferObject>(TransferObject.class, null);
if (getScoutObject() != null) {
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
if (!ACCESS.check(new CopyToClipboardPermission())) {
return;
}
TransferObject scoutTransferable = getScoutObject().getUIFacade().fireRowsCopyRequestFromUI();
result.setValue(scoutTransferable);
}
};
try {
getEnvironment().invokeScoutLater(t, 20000).join(20000);
}
catch (InterruptedException e) {
//nop
}
// end notify
}
return result.getValue();
}
protected void handleSwtColumnMoved() {
if (getUpdateSwtFromScoutLock().isAcquired()) {
return;
}
int[] uiColumnOrder = getSwtField().getColumnOrder();
// do not allow to reorder icon and dummy column
if (uiColumnOrder[0] != 0) {
getSwtField().setColumnOrder(m_uiColumnOrder);
return;
}
// if column with icon has changed position
if (uiColumnOrder[1] != m_uiColumnOrder[1] && getScoutObject().getRowCount() > 0 && StringUtility.hasText(getScoutObject().getRow(0).getIconId())) {
getSwtField().setRedraw(false);
try {
// Disable redraw during the time of refresh to omit flickering and boost performance.
getSwtTableViewer().refresh();
}
finally {
getSwtField().setRedraw(true);
}
}
int[] truncatedColOrder = new int[uiColumnOrder.length - 1];
for (int i = 0; i < truncatedColOrder.length; i++) {
truncatedColOrder[i] = uiColumnOrder[i + 1] - 1;
}
final List<IColumn<?>> newOrder = m_columnManager.getOrderedColumns(truncatedColOrder);
if (m_columnManager.applyNewOrder(newOrder)) {
m_uiColumnOrder = uiColumnOrder;
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
try {
addIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_COLUMN_ORDER_CHANGED);
addIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED);
//
getScoutObject().getUIFacade().fireVisibleColumnsChangedFromUI(newOrder);
}
finally {
removeIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_COLUMN_ORDER_CHANGED);
removeIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED);
}
}
};
getEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
protected void handleKeyboardNavigationFromSwt(TableItem item) {
if (getScoutObject().isCheckable()) {
//nop
return;
}
getSwtField().setSelection(item);
Event selectionEvent = new Event();
selectionEvent.type = SWT.DefaultSelection;
selectionEvent.widget = getSwtField();
for (Listener l : getSwtField().getListeners(SWT.DefaultSelection)) {
l.handleEvent(selectionEvent);
}
}
private class P_TableViewerEx extends TableViewer {
public P_TableViewerEx(Table table) {
super(table);
}
@Override
public void applyEditorValue() { // public visibility to close editor in case of an empty row click.
super.applyEditorValue();
}
}
private class P_ScoutTableListener implements TableListener {
@Override
public void tableChanged(final TableEvent event) {
if (isHandleScoutTableEvent(CollectionUtility.arrayList(event))) {
if (isIgnoredScoutEvent(TableEvent.class, "" + event.getType())) {
return;
}
Runnable t = new Runnable() {
@Override
public void run() {
try {
getUpdateSwtFromScoutLock().acquire();
//
handleScoutTableEventInSwt(event);
}
finally {
getUpdateSwtFromScoutLock().release();
}
}
};
getEnvironment().invokeSwtLater(t);
}
}
@Override
public void tableChangedBatch(final List<? extends TableEvent> a) {
if (isHandleScoutTableEvent(a)) {
final List<TableEvent> filteredList = new ArrayList<TableEvent>();
for (TableEvent event : a) {
if (!isIgnoredScoutEvent(TableEvent.class, "" + event.getType())) {
filteredList.add(event);
}
}
if (CollectionUtility.hasElements(filteredList)) {
Runnable t = new Runnable() {
@Override
public void run() {
if (isDisposed()) {
return;
}
m_redrawHandler.pushControlChanging();
try {
try {
getUpdateSwtFromScoutLock().acquire();
//
for (TableEvent element : filteredList) {
handleScoutTableEventInSwt(element);
}
}
finally {
getUpdateSwtFromScoutLock().release();
}
}
finally {
m_redrawHandler.popControlChanging();
}
}
};
getEnvironment().invokeSwtLater(t);
}
}
}
}// end P_ScoutTableListener
private class P_SwtTableListener implements Listener {
@Override
public void handleEvent(Event event) {
TableViewer swtTableViewer = getSwtTableViewer();
switch (event.type) {
case SWT.MouseDown: {
// Close cell editor on empty space click.
if (swtTableViewer.isCellEditorActive() && swtTableViewer.getTable().getItem(new Point(event.y, event.y)) == null) {
((P_TableViewerEx) swtTableViewer).applyEditorValue();
}
setContextColumnFromSwt(getSwtColumnAt(new Point(event.x, event.y)));
if (getSwtField().getItem(new Point(event.x, event.y)) == null) {
swtTableViewer.setSelection(null);
setSelectionFromSwt(new StructuredSelection());
}
break;
}
case SWT.MouseUp: {
if (event.count == 1) {
StructuredSelection selection = (StructuredSelection) swtTableViewer.getSelection();
if (selection.size() == 1) {
handleSwtRowClick((ITableRow) selection.getFirstElement(), event.button);
}
}
break;
}
case SWT.MouseDoubleClick: {
StructuredSelection selection = (StructuredSelection) swtTableViewer.getSelection();
if (selection.size() == 1) {
handleSwtRowAction((ITableRow) selection.getFirstElement());
}
break;
}
case SWT.MenuDetect: {
Point pt = getSwtField().getDisplay().map(null, getSwtField(), new Point(event.x, event.y));
Rectangle clientArea = getSwtField().getClientArea();
boolean header = clientArea.y <= pt.y && pt.y < clientArea.y + getSwtField().getHeaderHeight();
getSwtField().setMenu(header ? m_headerMenu : m_contextMenu);
break;
}
case SWT.KeyUp: {
if (event.doit && getScoutObject().isCheckable()) {
if (event.stateMask == 0) {
switch (event.keyCode) {
case ' ':
case SWT.CR:
List<ITableRow> selectedRows = SwtUtility.getItemsOfSelection(ITableRow.class, (StructuredSelection) swtTableViewer.getSelection());
if (CollectionUtility.hasElements(selectedRows)) {
handleSwtRowClick(CollectionUtility.firstElement(selectedRows), event.button);
}
event.doit = false;
break;
}
}
}
break;
}
}
}
}
private class P_SwtResizeListener implements Listener {
@Override
public void handleEvent(Event event) {
//lazy column auto-fit
if (getSwtField() != null && !getSwtField().isDisposed()) {
scheduleHandleAutoResizeColumn();
}
}
} // end class P_SwtResizeListener
public class P_SwtSelectionListener implements ISelectionChangedListener {
@Override
public void selectionChanged(SelectionChangedEvent event) {
setSelectionFromSwt((StructuredSelection) event.getSelection());
}
} // end class P_SwtSelectionListener
private class P_TableColumnListener implements Listener {
@Override
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Move:
handleSwtColumnMoved();
break;
case SWT.Resize:
if (event.widget instanceof TableColumn) {
handleSwtColumnResized((TableColumn) event.widget);
}
}
}
} // end class P_TableColumnMoveListener
private class P_ColumnSortListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
/*
* TODO multi column search does work with control mask. The selection
* event on a table column does not provide any key mask nor right mouse
* click.
*/
TableColumn col = (TableColumn) e.getSource();
setContextColumnFromSwt(col);
final IColumn<?> newColumn = (IColumn<?>) col.getData(KEY_SCOUT_COLUMN);
if (getScoutObject() != null) {
Runnable job = new Runnable() {
@Override
public void run() {
// use always memory sort since SWT does not provide a keymask on
// selection events
getScoutObject().getUIFacade().fireHeaderSortFromUI(newColumn, false);
}
};
getEnvironment().invokeScoutLater(job, 0);
}
}
} // end class P_ColumnSortListener
private class P_SwtHeaderMenuDetectListener implements MenuDetectListener {
@Override
public void menuDetected(MenuDetectEvent event) {
Table table = getSwtField();
Point pTable = table.getDisplay().map(null, table, new Point(event.x, event.y));
Rectangle clientArea = table.getClientArea();
boolean header = clientArea.y <= pTable.y && pTable.y < clientArea.y + table.getHeaderHeight();
if (!header) {
return;
}
// clear all previous
// Windows BUG: fires menu hide before the selection on the menu item is
// propagated.
if (m_headerMenu != null) {
for (MenuItem item : m_headerMenu.getItems()) {
disposeMenuItem(item);
}
}
setContextColumnFromSwt(getSwtColumnAt(pTable));
Runnable t = new Runnable() {
@Override
public void run() {
IActionFilter aboutToShowFilter = ActionUtility.createMenuFilterMenuTypes(CollectionUtility.hashSet(TableMenuType.EmptySpace, TableMenuType.Header), false);
IContextMenu contextMenu = getScoutObject().getContextMenu();
contextMenu.callAboutToShow(aboutToShowFilter);
}
};
JobEx job = getEnvironment().invokeScoutLater(t, 1200);
try {
job.join(1200);
}
catch (InterruptedException ex) {
//nop
}
// grab the actions out of the job, when the actions are providden
// within the scheduled time the popup will be handled.
IActionFilter displayFilter = ActionUtility.createMenuFilterMenuTypes(CollectionUtility.hashSet(TableMenuType.EmptySpace, TableMenuType.Header), true);
SwtMenuUtility.fillMenu(m_headerMenu, getScoutObject().getContextMenu().getChildActions(), displayFilter, getEnvironment());
}
private void disposeMenuItem(MenuItem item) {
Menu menu = item.getMenu();
if (menu != null) {
for (MenuItem childItem : menu.getItems()) {
disposeMenuItem(childItem);
}
menu.dispose();
}
item.dispose();
}
} // end class P_HeaderMenuListener
private class P_DndSupport extends AbstractSwtScoutDndSupport {
public P_DndSupport(IPropertyObserver scoutObject, IDNDSupport scoutDndSupportable, Control control, ISwtEnvironment environment) {
super(scoutObject, scoutDndSupportable, control, environment);
}
@Override
protected boolean acceptDrag() {
if (m_mouseMoveSelectionSupport != null) {
return m_mouseMoveSelectionSupport.acceptDrag();
}
return super.acceptDrag();
}
@Override
protected TransferObject handleSwtDragRequest() {
final Holder<TransferObject> result = new Holder<TransferObject>(TransferObject.class, null);
Runnable t = new Runnable() {
@Override
public void run() {
TransferObject scoutTransferable = getScoutObject().getUIFacade().fireRowsDragRequestFromUI();
result.setValue(scoutTransferable);
}
};
try {
getEnvironment().invokeScoutLater(t, 20000).join(20000);
}
catch (InterruptedException e) {
//nop
}
return result.getValue();
}
@Override
protected void handleSwtDropAction(DropTargetEvent event, final TransferObject scoutTransferObject) {
Object dropTarget = event.item != null ? event.item.getData() : null;
final ITableRow row = dropTarget instanceof ITableRow ? (ITableRow) dropTarget : null;
Runnable job = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().fireRowDropActionFromUI(row, scoutTransferObject);
}
};
getEnvironment().invokeScoutLater(job, 200);
}
}// end class P_DndSupport
private class P_KeyBoardNavigationSupport extends TableKeyboardNavigationSupport {
/**
* @param table
* @param delay
*/
public P_KeyBoardNavigationSupport(TableEx table) {
super(table);
}
@Override
void handleKeyboardNavigation(TableItem tableItem) {
handleKeyboardNavigationFromSwt(tableItem);
}
} // P_KeyBoardNavigationSupport
private class P_MouseMoveSelectionSupport extends TableMouseMoveSelectionSupport {
public P_MouseMoveSelectionSupport(Table table, ISwtScoutDndSupport dndSupport) {
super(table, dndSupport);
}
@Override
protected void selectingStopped() {
//Selection event seems not to be fired properly after selecting the items so it's necessary to manually call setSelectionFromSwt
StructuredSelection selection = (StructuredSelection) getSwtTableViewer().getSelection();
setSelectionFromSwt(selection);
}
}
}