diff options
author | Arthur van Dorp | 2020-08-06 08:33:01 +0000 |
---|---|---|
committer | Arthur van Dorp | 2020-08-06 09:23:16 +0000 |
commit | 153d50fcbfc95c6aa2ec3dd87f4f6e7a936d6b42 (patch) | |
tree | 345001b94c504e95b3965bb27298e9472ff85659 | |
parent | 1a3324828d6443df9355479af5f4c4c8b2df7d76 (diff) | |
download | org.eclipse.scout.rt-releases/4.2.x.tar.gz org.eclipse.scout.rt-releases/4.2.x.tar.xz org.eclipse.scout.rt-releases/4.2.x.zip |
Bug 453504 - SWT: Nullpointer when toggling a checkable4.2.0_R37releases/4.2.x
Manual cherry pick from 83ab7b7c921bd9531411c5cd6e2f6ad5e541e73e Mon Sep
17 00:00:00 2001 Daniel Wiehl <daniel.wiehl@bsiag.com> and Frank Ammeter
Change-Id: If67a59d2ef4d767e543c3a82174960f145ff24ef
Signed-off-by: Arthur van Dorp <Arthur.vanDorp@bsi-software.com>
Reviewed-on: https://git.eclipse.org/r/c/scout/org.eclipse.scout.rt/+/167343
5 files changed, 606 insertions, 719 deletions
diff --git a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/SwtScoutTable.java b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/SwtScoutTable.java index d4452f1105..d49c4380eb 100644 --- a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/SwtScoutTable.java +++ b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/SwtScoutTable.java @@ -23,8 +23,6 @@ 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.CellEditor; -import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; @@ -59,7 +57,7 @@ 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.SwtScoutTableCellEditor; +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; @@ -113,7 +111,6 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou private Listener m_columnListener = new P_TableColumnListener(); private SelectionListener m_columnSortListener = new P_ColumnSortListener(); private TableColumnManager m_columnManager = new TableColumnManager(); - private SwtScoutTableCellEditor m_cellEditorComposite; private int[] m_uiColumnOrder; private Menu m_contextMenu; private Menu m_headerMenu; @@ -158,9 +155,6 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou setSwtTableViewer(viewer); setSwtField(table); - //cell editing support - m_cellEditorComposite = new SwtScoutTableCellEditor(this); - // header menu m_headerMenu = new Menu(viewer.getTable().getShell(), SWT.POP_UP); table.addMenuDetectListener(new P_SwtHeaderMenuDetectListener()); @@ -238,10 +232,8 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou for (TableColumn col : getSwtField().getColumns()) { col.dispose(); } - /* - * bug: swt table first column can not be aligned nor an image can be set. - * see also SwtScoutTableCellEditor - */ + + // 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); @@ -260,35 +252,54 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou 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()); - TableColumn swtCol = new TableColumn(getSwtField(), style); - swtCol.setData(KEY_SCOUT_COLUMN, scoutColumn); + final TableViewerColumn viewerColumn = new TableViewerColumn(getSwtTableViewer(), style); + TableColumn swtColumn = viewerColumn.getColumn(); + swtColumn.setData(KEY_SCOUT_COLUMN, scoutColumn); - TableViewerColumn viewerColumn = new TableViewerColumn(getSwtTableViewer(), swtCol); + // Install the column's LabelProvider. viewerColumn.setLabelProvider(new SwtScoutCellLabelProvider(scoutColumn, getEnvironment())); - swtCol.setMoveable(true); - swtCol.setToolTipText(headerCell.getTooltipText()); - updateHeaderText(swtCol, scoutColumn); - swtCol.setWidth(scoutColumn.getWidth()); + // 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()) { - swtCol.setResizable(false); + swtColumn.setResizable(false); } if (headerCell.isSortActive()) { - getSwtField().setSortColumn(swtCol); + getSwtField().setSortColumn(swtColumn); getSwtField().setSortDirection(headerCell.isSortAscending() ? SWT.UP : SWT.DOWN); } if (sortEnabled) { - swtCol.addSelectionListener(m_columnSortListener); + swtColumn.addSelectionListener(m_columnSortListener); } - swtCol.addListener(SWT.Move, m_columnListener); - swtCol.addListener(SWT.Resize, m_columnListener); + 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(); - //update cell editors - m_cellEditorComposite.initialize(); } finally { m_redrawHandler.popControlChanging(); @@ -512,7 +523,7 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou break; } case TableEvent.TYPE_REQUEST_FOCUS_IN_CELL: { - //start editing + // 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++) { @@ -1006,18 +1017,7 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou } @Override - protected void triggerEditorActivationEvent(ColumnViewerEditorActivationEvent event) { - //Make sure editor is closed when clicking on another cell. Mainly necessary when using the second mouse button to open the context menu - for (CellEditor editor : getCellEditors()) { - if (editor != null && editor.isActivated()) { - applyEditorValue(); - } - } - super.triggerEditorActivationEvent(event); - } - - @Override - public void applyEditorValue() { + public void applyEditorValue() { // public visibility to close editor in case of an empty row click. super.applyEditorValue(); } } @@ -1092,8 +1092,8 @@ public class SwtScoutTable extends SwtScoutComposite<ITable> implements ISwtScou TableViewer swtTableViewer = getSwtTableViewer(); switch (event.type) { case SWT.MouseDown: { - //Close cell editor on empty space click - if (swtTableViewer.getTable().getItem(new Point(event.y, event.y)) == null && swtTableViewer instanceof P_TableViewerEx) { + // Close cell editor on empty space click. + if (swtTableViewer.isCellEditorActive() && swtTableViewer.getTable().getItem(new Point(event.y, event.y)) == null) { ((P_TableViewerEx) swtTableViewer).applyEditorValue(); } diff --git a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutFormFieldPopup.java b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutFormFieldPopup.java index e62ec8a2bd..eb61a2a51c 100644 --- a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutFormFieldPopup.java +++ b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutFormFieldPopup.java @@ -27,7 +27,6 @@ import org.eclipse.scout.rt.client.ui.form.fields.IFormField; import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox; import org.eclipse.scout.rt.ui.swt.basic.SwtScoutComposite; import org.eclipse.scout.rt.ui.swt.form.fields.ISwtScoutFormField; -import org.eclipse.scout.rt.ui.swt.util.SwtUtility; import org.eclipse.scout.rt.ui.swt.window.popup.SwtScoutDropDownPopup; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; @@ -38,9 +37,18 @@ import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; /** + * <p> * Wraps a {@link IFormField} to be displayed as popup cell editor. + * </p> + * Thereby, {@link SwtScoutComposite} is constructed as following: + * <ul> + * <li>{@link SwtScoutComposite#getSwtField()}: references the the UI representation of the FormField that is displayed + * within the popup {@link Shell}; is lazy bound the time the {@link Shell} is painted for the first time</li> + * <li>{@link SwtScoutComposite#getSwtContainer()}: references the owner control the Shell is bound to</li> + * </ul> */ public class SwtScoutFormFieldPopup extends SwtScoutComposite<IFormField> { @@ -55,8 +63,6 @@ public class SwtScoutFormFieldPopup extends SwtScoutComposite<IFormField> { private int m_prefHeight; private int m_style; - private Control m_swtFormField; - private final Set<IFormFieldPopupListener> m_listeners = Collections.synchronizedSet(new HashSet<IFormFieldPopupListener>()); public SwtScoutFormFieldPopup(Composite owner) { @@ -127,10 +133,12 @@ public class SwtScoutFormFieldPopup extends SwtScoutComposite<IFormField> { final IForm form = createForm(); if (form != null) { m_swtScoutPopup.showForm(form); - m_swtFormField = findSwtFormField(m_swtScoutPopup.getUiForm().getSwtField(), getScoutObject()); - if (m_swtFormField == null) { + + final Control swtFormField = findSwtFormField(m_swtScoutPopup.getUiForm().getSwtField(), getScoutObject()); + if (swtFormField == null) { LOG.warn("UI-FormField could not be found in UI-Form"); } + setSwtField(swtFormField); // Install listener to be notified about traversal events. installTraverseListener(m_swtScoutPopup.getShell(), traverseListener); @@ -167,15 +175,6 @@ public class SwtScoutFormFieldPopup extends SwtScoutComposite<IFormField> { m_swtScoutPopup.closePart(); } - /** - * Touches the field to write its UI value back to the model. - */ - public void touch() { - if (m_swtFormField != null && !m_swtFormField.isDisposed()) { - SwtUtility.runSwtInputVerifier(m_swtFormField); - } - } - public void addListener(IFormFieldPopupListener listener) { m_listeners.add(listener); } diff --git a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutTableCellEditor.java b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutTableCellEditor.java deleted file mode 100644 index e9cf44603c..0000000000 --- a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/SwtScoutTableCellEditor.java +++ /dev/null @@ -1,665 +0,0 @@ -/******************************************************************************* - * 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.celleditor; - -import org.eclipse.jface.viewers.CellEditor; -import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; -import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent; -import org.eclipse.jface.viewers.ICellModifier; -import org.eclipse.jface.viewers.TableViewer; -import org.eclipse.jface.viewers.ViewerCell; -import org.eclipse.scout.commons.BooleanUtility; -import org.eclipse.scout.commons.CompareUtility; -import org.eclipse.scout.commons.holders.BooleanHolder; -import org.eclipse.scout.commons.holders.Holder; -import org.eclipse.scout.commons.logger.IScoutLogger; -import org.eclipse.scout.commons.logger.ScoutLogManager; -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.TableUtility; -import org.eclipse.scout.rt.client.ui.basic.table.columns.IBooleanColumn; -import org.eclipse.scout.rt.client.ui.basic.table.columns.IColumn; -import org.eclipse.scout.rt.client.ui.form.fields.GridData; -import org.eclipse.scout.rt.client.ui.form.fields.IFormField; -import org.eclipse.scout.rt.client.ui.form.fields.stringfield.IStringField; -import org.eclipse.scout.rt.ui.swt.basic.ISwtScoutComposite; -import org.eclipse.scout.rt.ui.swt.basic.table.ISwtScoutTable; -import org.eclipse.scout.rt.ui.swt.basic.table.SwtScoutTable; -import org.eclipse.scout.rt.ui.swt.extension.UiDecorationExtensionPoint; -import org.eclipse.scout.rt.ui.swt.keystroke.SwtKeyStroke; -import org.eclipse.scout.rt.ui.swt.util.SwtUtility; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.TraverseEvent; -import org.eclipse.swt.events.TraverseListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.FillLayout; -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.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.Widget; - -/** - * Editable support for {@link ITable} in SWT-UI. - */ -public class SwtScoutTableCellEditor { - - private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwtScoutTableCellEditor.class); - - private static final String DUMMY_VALUE = "Dummy"; - - private final ISwtScoutTable m_tableComposite; - private final Listener m_rowHeightListener; - - private P_FocusLostListener m_focusLostListener; - - public SwtScoutTableCellEditor(final ISwtScoutTable tableComposite) { - m_focusLostListener = new P_FocusLostListener(); - m_tableComposite = tableComposite; - m_rowHeightListener = new Listener() { - @Override - public void handleEvent(Event event) { - event.height = Math.max(event.height, UiDecorationExtensionPoint.getLookAndFeel().getLogicalGridLayoutRowHeight()); - } - }; - } - - //(re)install cell editors - public void initialize() { - TableViewer viewer = m_tableComposite.getSwtTableViewer(); - String[] columnPropertyNames = new String[viewer.getTable().getColumnCount()]; - CellEditor[] oldEditors = viewer.getCellEditors(); - CellEditor[] newEditors = new CellEditor[columnPropertyNames.length]; - boolean hasEditors = false; - for (int i = 0; i < columnPropertyNames.length; i++) { - TableColumn swtCol = viewer.getTable().getColumn(i); - IColumn<?> scoutCol = (IColumn<?>) swtCol.getData(SwtScoutTable.KEY_SCOUT_COLUMN); - if (scoutCol != null) { - columnPropertyNames[i] = "" + scoutCol.getColumnIndex(); - if (scoutCol.isEditable()) { - hasEditors = true; - newEditors[i] = new P_SwtCellEditor(viewer.getTable(), scoutCol); - } - } - else { - columnPropertyNames[i] = ""; - } - } - viewer.setCellModifier(new P_SwtCellModifier()); - viewer.setColumnProperties(columnPropertyNames); - viewer.setCellEditors(newEditors); - if (oldEditors != null && oldEditors.length > 0) { - for (CellEditor editor : oldEditors) { - if (editor != null) { - editor.dispose(); - } - } - } - //increase row height when editors are present - if (hasEditors) { - viewer.getTable().addListener(SWT.MeasureItem, m_rowHeightListener); - } - else { - viewer.getTable().removeListener(SWT.MeasureItem, m_rowHeightListener); - } - } - - @SuppressWarnings("unchecked") - protected Control createEditorControl(Composite parent, final ITableRow scoutRow, final IColumn<?> scoutCol) { - IFormField formField = createFormField(scoutRow, scoutCol); - if (formField == null) { - return null; - } - - ISwtScoutComposite swtScoutFormField; - if (formField instanceof IStringField && ((IStringField) formField).isMultilineText()) { - // open a separate Shell to edit the content. - swtScoutFormField = createEditorCompositePopup(parent, formField, scoutRow, scoutCol); - } - else { - swtScoutFormField = m_tableComposite.getEnvironment().createFormField(parent, formField); - } - - if (swtScoutFormField != null) { - decorateEditorComposite(swtScoutFormField, scoutRow, scoutCol); - return swtScoutFormField.getSwtContainer(); - } - else { - return null; - } - } - - protected ISwtScoutComposite<? extends IFormField> createEditorCompositePopup(final Composite parent, IFormField formField, final ITableRow scoutRow, final IColumn<?> scoutCol) { - // overwrite layout properties - GridData gd = formField.getGridData(); - gd.h = 1; - gd.w = IFormField.FULL_WIDTH; - gd.weightY = 1; - gd.weightX = 1; - formField.setGridDataInternal(gd); - - TableColumn swtCol = getSwtColumn(scoutCol); - final P_SwtCellEditor cellEditor = (P_SwtCellEditor) m_tableComposite.getSwtTableViewer().getCellEditors()[getSwtColumnIndex(swtCol)]; - - int prefWidth = gd.widthInPixel; - int minWidth = swtCol.getWidth(); - int prefHeight = gd.heightInPixel; - int minHeight = Math.max(105, m_tableComposite.getSwtTableViewer().getTable().getItemHeight()); - - prefHeight = Math.max(prefHeight, minHeight); - prefWidth = Math.max(prefWidth, minWidth); - - // Create placeholder field to represent the cell editor - final Composite cellEditorComposite = new Composite(parent, SWT.NONE); - - // Create popup dialog to wrap the form field - final SwtScoutFormFieldPopup formFieldDialog = new SwtScoutFormFieldPopup(cellEditorComposite); - formFieldDialog.setPrefHeight(prefHeight); - formFieldDialog.setPrefWidth(prefWidth); - formFieldDialog.setMinHeight(minHeight); - formFieldDialog.setMinWidth(minWidth); - - // == ICellModifier == - // Replace default cell-modifier strategy to touch form-field to be written back to the model. - final ICellModifier defaultCellModifier = m_tableComposite.getSwtTableViewer().getCellModifier(); - m_tableComposite.getSwtTableViewer().setCellModifier(new P_SwtCellModifier() { - - @Override - public void modify(Object element, String property, Object value) { - formFieldDialog.touch(); - super.modify(element, property, value); - } - }); - - // == IFocusDelegate == - // Replace default focus handling strategy. - final IFocusDelegate defaultFocusDelegate = cellEditor.getFocusDelegate(); - cellEditor.setFocusDelegate(new IFocusDelegate() { - - @Override - public void doSetFocus() { - // NOOP: Focus is set the time the Shell is opened. - } - }); - - // == IFormFieldPopupListener == - // To receive events about the popup's state. The popup is not closed yet but the cell-editor closed. - final IFormFieldPopupListener formFieldPopupListener = new IFormFieldPopupListener() { - - @Override - public void handleEvent(int event) { - if ((event & IFormFieldPopupListener.TYPE_OK) > 0) { - cellEditor.stopCellEditing(); // save cell editor - } - else if ((event & IFormFieldPopupListener.TYPE_CANCEL) > 0) { - cellEditor.cancelCellEditing(); // cancel cell editor - } - - // traversal control - if ((event & IFormFieldPopupListener.TYPE_FOCUS_BACK) > 0) { - enqueueEditNextTableCell(scoutRow, scoutCol, false); - } - else if ((event & IFormFieldPopupListener.TYPE_FOCUS_NEXT) > 0) { - enqueueEditNextTableCell(scoutRow, scoutCol, true); - } - } - }; - formFieldDialog.addListener(formFieldPopupListener); - - // == DisposeListener == - // To close the Shell if the cell-editor is disposed. - cellEditorComposite.addDisposeListener(new DisposeListener() { - - @Override - public void widgetDisposed(DisposeEvent e) { - formFieldDialog.removeListener(formFieldPopupListener); // ignore resulting popup events. - - // Restore default focus delegate and cell modifier. - cellEditor.setFocusDelegate(defaultFocusDelegate); - m_tableComposite.getSwtTableViewer().setCellModifier(defaultCellModifier); - - // Close the popup Shell. - // The asyncExec is a workaround so that other cell-editors can be activated immediately. - // Note: If being dirty, 'Viewer#refresh()' in TableEx prevents the cell from being activated immediately. - e.display.asyncExec(new Runnable() { - - @Override - public void run() { - formFieldDialog.closePopup(); - } - }); - } - }); - - // Open the popup for the form field. - formFieldDialog.createField(parent, formField, m_tableComposite.getEnvironment()); - - return formFieldDialog; - } - - protected IFormField createFormField(final ITableRow scoutRow, final IColumn<?> scoutCol) { - if (scoutRow == null || scoutCol == null) { - return null; - } - - final Holder<IFormField> result = new Holder<IFormField>(); - Runnable t = new Runnable() { - @Override - public void run() { - result.setValue(m_tableComposite.getScoutObject().getUIFacade().prepareCellEditFromUI(scoutRow, scoutCol)); - } - }; - try { - m_tableComposite.getEnvironment().invokeScoutLater(t, 2345).join(2345); - } - catch (InterruptedException e) { - LOG.warn("Interrupted while waiting for the Form-Field to be created.", e); - } - return result.getValue(); - } - - /** - * Callback to be overwritten to customize the {@link IFormField}. - */ - protected void decorateEditorComposite(ISwtScoutComposite<? extends IFormField> editorComposite, final ITableRow scoutRow, final IColumn<?> scoutCol) { - } - - protected void saveEditorFromSwt() { - Runnable t = new Runnable() { - @Override - public void run() { - m_tableComposite.getScoutObject().getUIFacade().completeCellEditFromUI(); - } - }; - m_tableComposite.getEnvironment().invokeScoutLater(t, 0); - } - - protected void cancelEditorFromSwt() { - Runnable t = new Runnable() { - @Override - public void run() { - m_tableComposite.getScoutObject().getUIFacade().cancelCellEditFromUI(); - } - }; - m_tableComposite.getEnvironment().invokeScoutLater(t, 0); - } - - protected void enqueueEditNextTableCell(final ITableRow row, final IColumn<?> col, final boolean forward) { - if (row == null || col == null) { - return; - } - m_tableComposite.getEnvironment().invokeScoutLater(new Runnable() { - @Override - public void run() { - if (m_tableComposite.getEnvironment() == null) { - return; - } - ITable table = m_tableComposite.getScoutObject(); - TableUtility.editNextTableCell(table, row, col, forward, new TableUtility.ITableCellEditorFilter() { - @Override - public boolean accept(ITableRow rowx, IColumn<?> colx) { - return true; - } - }); - } - }, 0L); - } - - protected IColumn<?> getScoutColumn(String property) { - if (property != null && property.matches("[0-9]+")) { - int colIndex = Integer.parseInt(property); - return m_tableComposite.getScoutObject().getColumnSet().getColumn(colIndex); - } - return null; - } - - private TableColumn getSwtColumn(IColumn<?> scoutCol) { - for (TableColumn swtCol : m_tableComposite.getSwtTableViewer().getTable().getColumns()) { - IColumn<?> candidate = (IColumn<?>) swtCol.getData(SwtScoutTable.KEY_SCOUT_COLUMN); - if (candidate != null && CompareUtility.equals(candidate.getColumnId(), scoutCol.getColumnId())) { - return swtCol; - } - } - return null; - } - - private int getSwtColumnIndex(TableColumn swtCol) { - Table table = m_tableComposite.getSwtTableViewer().getTable(); - for (int i = 0; i < table.getColumnCount(); i++) { - if (table.getColumn(i) == swtCol) { - return i; - } - } - return -1; - } - - private class P_SwtCellModifier implements ICellModifier { - - @Override - public void modify(Object element, String property, Object value) { - saveEditorFromSwt(); - } - - @Override - public Object getValue(Object element, String property) { - //not used - return DUMMY_VALUE; - } - - @Override - public boolean canModify(Object element, String property) { - final ITable table = m_tableComposite.getScoutObject(); - final ITableRow row = (ITableRow) element; - final IColumn<?> column = getScoutColumn(property); - - final BooleanHolder result = new BooleanHolder(); - Runnable r = new Runnable() { - @Override - public void run() { - if (table != null && row != null && column != null) { - result.setValue(table.isCellEditable(row, column)); - } - } - }; - try { - m_tableComposite.getEnvironment().invokeScoutLater(r, 2345).join(2345); - } - catch (InterruptedException e) { - LOG.warn("Interrupted while waiting for the model to determine the cell's editability.", e); - } - return BooleanUtility.nvl(result.getValue(), false); - } - } - - /** - * Statefull per-column cell-editor which is used for all cells of a column. - */ - private class P_SwtCellEditor extends CellEditor { - private Composite m_container; - private Object m_value; - private ITableRow m_editScoutRow; - private IFocusDelegate m_focusDelegate; - private IColumn<?> m_scoutCol; - private ViewerCell m_cell; - private Image m_image; - - /** - * @param parent - * the table. - * @param scoutCol - * the scout column this cell editor is used for. - */ - protected P_SwtCellEditor(Composite parent, IColumn<?> scoutCol) { - super(parent); - m_scoutCol = scoutCol; - m_focusDelegate = new P_FocusDelegate(); - } - - @Override - protected Control createControl(Composite parent) { - m_container = new Composite(parent, SWT.NONE) { - /* - * disable inner components preferred sizes - */ - @Override - public Point computeSize(int wHint, int hHint, boolean changed) { - return new Point(wHint, hHint); - } - }; - m_container.setLayout(new FillLayout()); - m_tableComposite.getEnvironment().addKeyStroke(m_container, new SwtKeyStroke(SWT.ESC) { - @Override - public void handleSwtAction(Event e) { - e.doit = false; - fireCancelEditor(); - } - }); - m_tableComposite.getEnvironment().addKeyStroke(m_container, new SwtKeyStroke(SWT.CR) { - @Override - public void handleSwtAction(Event e) { - e.doit = false; - fireApplyEditorValue(); - deactivate(); - } - }); - m_tableComposite.getEnvironment().addKeyStroke(m_container, new SwtKeyStroke(SWT.KEYPAD_CR) { - @Override - public void handleSwtAction(Event e) { - e.doit = false; - fireApplyEditorValue(); - deactivate(); - } - }); - return m_container; - } - - @Override - protected void doSetFocus() { - m_focusDelegate.doSetFocus(); - } - - @Override - protected Object doGetValue() { - return m_value; - } - - @Override - protected void doSetValue(Object value) { - m_value = value; - } - - @Override - public void activate(ColumnViewerEditorActivationEvent e) { - // Install a focus-lost listener on the table widget to close an active cell-editor when the table looses the focus. - m_focusLostListener.install(); - - if (!(e.getSource() instanceof ViewerCell)) { - return; - } - - m_cell = (ViewerCell) e.getSource(); - m_editScoutRow = (ITableRow) m_cell.getElement(); - - if (m_scoutCol instanceof IBooleanColumn) { - if (e.sourceEvent instanceof MouseEvent) { - return; // no edit-mode when a boolean cell was clicked by mouse. - } - else { - // hide the checkbox image when editing a boolean value in traversal-mode. - m_image = m_cell.getImage(); - m_cell.setImage(null); - } - } - - // create the cell editor widget. - if (m_editScoutRow != null) { - createEditorControl(m_container, m_editScoutRow, m_scoutCol); - } - - m_container.layout(true, true); - m_container.setVisible(true); - } - - @Override - protected void deactivate(ColumnViewerEditorDeactivationEvent e) { - // restore the cell's image if being unset in CellEditor#activate. - if (m_cell != null && m_image != null) { - m_cell.setImage(m_image); - } - - m_cell = null; - m_image = null; - m_editScoutRow = null; - - // Dispose the cell-editor; in turn, any Shell opened by the editor is closed as well. - for (Control c : m_container.getChildren()) { - c.dispose(); - } - if (e.eventType == ColumnViewerEditorDeactivationEvent.EDITOR_CANCELED) { - cancelEditorFromSwt(); - } - - super.deactivate(e); - - m_focusLostListener.uninstall(); - } - - @Override - protected boolean dependsOnExternalFocusListener() { - return false; - } - - public void stopCellEditing() { - fireApplyEditorValue(); - deactivate(); - } - - public void cancelCellEditing() { - fireCancelEditor(); - deactivate(); - } - - public IFocusDelegate getFocusDelegate() { - return m_focusDelegate; - } - - public void setFocusDelegate(IFocusDelegate focusDelegate) { - m_focusDelegate = focusDelegate; - } - - private class P_FocusDelegate implements IFocusDelegate { - - @Override - public void doSetFocus() { - // traverse the focus to the cell editor's control so that the user can start editing immediately without having to click into the widget first. - m_container.traverse(SWT.TRAVERSE_TAB_NEXT); - - Control focusControl = m_container.getDisplay().getFocusControl(); - if (focusControl != null && SwtUtility.isAncestorOf(m_container, focusControl)) { - focusControl.addTraverseListener(new TraverseListener() { - @Override - public void keyTraversed(TraverseEvent e) { - switch (e.detail) { - case SWT.TRAVERSE_ESCAPE: - case SWT.TRAVERSE_RETURN: { - e.doit = false; - break; - } - case SWT.TRAVERSE_TAB_NEXT: { - e.doit = false; - ITableRow currentScoutRow = m_editScoutRow; // memorize the current row because being set to null when the cell editor is deactivated. - fireApplyEditorValue(); - deactivate(); - enqueueEditNextTableCell(currentScoutRow, m_scoutCol, true); // traverse the focus to the next editable cell. - break; - } - case SWT.TRAVERSE_TAB_PREVIOUS: { - e.doit = false; - ITableRow currentScoutRow = m_editScoutRow; // memorize the current row because being set to null when the cell editor is deactivated. - fireApplyEditorValue(); - deactivate(); - enqueueEditNextTableCell(currentScoutRow, m_scoutCol, false); // traverse the focus to the next editable cell. - break; - } - } - } - }); - } - } - } - } - - /** - * Hysteresis listener that commits the cell editor when the table has first received focus and then lost it. That is - * because cell editors in SWT are not closed automatically if the table looses the focus. - */ - private class P_FocusLostListener implements Listener { - - /** - * Installs listening for focus-lost events on the table widget. - */ - public void install() { - m_tableComposite.getEnvironment().getDisplay().addFilter(SWT.FocusIn, this); - } - - /** - * Uninstalls listening for focus-lost events on the table widget. - */ - public void uninstall() { - m_tableComposite.getEnvironment().getDisplay().removeFilter(SWT.FocusIn, this); - } - - @Override - public void handleEvent(Event event) { - Widget w = event.widget; - if (w == null || !(w instanceof Control) || w.isDisposed()) { - return; - } - - // Sanity check whether a cell-editor is active. - TableViewer viewer = m_tableComposite.getSwtTableViewer(); - if (!viewer.isCellEditorActive()) { - return; - } - - Control focusOwner = (Control) w; - Table table = m_tableComposite.getSwtTableViewer().getTable(); - - // Check if the table is the focus owner. - if (SwtUtility.isAncestorOf(table, focusOwner)) { - return; - } - - // Check if a Shell opened by the cell-editor is the focus owner. - if (focusOwner.getShell() != table.getShell()) { - Composite parentFocusOwner = focusOwner.getShell().getParent(); - while (parentFocusOwner != null) { - if (parentFocusOwner.getShell() == table.getShell()) { - return; // focus owner is a derrived Shell. - } - else { - parentFocusOwner = parentFocusOwner.getShell().getParent(); - } - } - } - - // Close the cell-editor because a control other than the table is focus owner. - for (CellEditor editor : viewer.getCellEditors()) { - if (editor != null && editor.isActivated() && editor instanceof P_SwtCellEditor) { - ((P_SwtCellEditor) editor).stopCellEditing(); - break; - } - } - } - } - - /** - * Delegate to process focus events on cell editor. - */ - private interface IFocusDelegate { - void doSetFocus(); - } - - /** - * Listener to get notified about deactivation events. - */ - private interface IDeactivateListener { - void canceled(ColumnViewerEditorDeactivationEvent event); - - void saved(ColumnViewerEditorDeactivationEvent event); - } -} diff --git a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/TableCellEditor.java b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/TableCellEditor.java new file mode 100644 index 0000000000..dcab3676a7 --- /dev/null +++ b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/TableCellEditor.java @@ -0,0 +1,427 @@ +/******************************************************************************* + * Copyright (c) 2014 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.celleditor; + +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; +import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.scout.commons.holders.Holder; +import org.eclipse.scout.commons.logger.IScoutLogger; +import org.eclipse.scout.commons.logger.ScoutLogManager; +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.TableUtility; +import org.eclipse.scout.rt.client.ui.basic.table.columns.IBooleanColumn; +import org.eclipse.scout.rt.client.ui.basic.table.columns.IColumn; +import org.eclipse.scout.rt.client.ui.form.fields.GridData; +import org.eclipse.scout.rt.client.ui.form.fields.IFormField; +import org.eclipse.scout.rt.client.ui.form.fields.stringfield.IStringField; +import org.eclipse.scout.rt.ui.swt.ISwtEnvironment; +import org.eclipse.scout.rt.ui.swt.basic.ISwtScoutComposite; +import org.eclipse.scout.rt.ui.swt.basic.table.ISwtScoutTable; +import org.eclipse.scout.rt.ui.swt.basic.table.SwtScoutTable; +import org.eclipse.scout.rt.ui.swt.keystroke.SwtKeyStroke; +import org.eclipse.scout.rt.ui.swt.util.SwtUtility; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +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.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Widget; + +/** + * <p> + * {@link CellEditor} for {@link SwtScoutTable}. + * </p> + * Each editable cell has its own {@link CellEditor} instance. + */ +public class TableCellEditor extends CellEditor { + + private static final IScoutLogger LOG = ScoutLogManager.getLogger(TableCellEditor.class); + + private ISwtEnvironment m_environment; + private P_FocusLostListener m_focusLostListener; + + private Composite m_container; + private Image m_image; + + private IColumn<?> m_scoutColumn; + private ITableRow m_scoutRow; + private ITable m_scoutTable; + private TableColumn m_swtColumn; + private Table m_swtTable; + + private TableViewer m_tableViewer; + + private boolean m_requestFocus; + + public TableCellEditor(TableViewer tableViewer, TableColumn swtColumn, ITableRow scoutRow, ISwtEnvironment environment) { + super(tableViewer.getTable()); + m_scoutRow = scoutRow; + m_scoutColumn = (IColumn<?>) swtColumn.getData(ISwtScoutTable.KEY_SCOUT_COLUMN); + m_scoutTable = m_scoutColumn.getTable(); + m_swtColumn = swtColumn; + m_tableViewer = tableViewer; + m_swtTable = tableViewer.getTable(); + + m_environment = environment; + m_focusLostListener = new P_FocusLostListener(); + } + + @Override + protected Control createControl(Composite parent) { + m_container = new Composite(parent, SWT.NONE) { + @Override + // disable inner components preferred sizes. + public Point computeSize(int wHint, int hHint, boolean changed) { + return new Point(wHint, hHint); + } + }; + m_container.setLayout(new FillLayout()); + + return m_container; + } + + @Override + protected Object doGetValue() { + // NOOP: The value is written back to the model by the widget's verify event. + return null; + } + + @Override + protected void doSetValue(Object value) { + // NOOP: The value is set into the cell-editor when it is created. + } + + @Override + public void activate(ColumnViewerEditorActivationEvent e) { + m_requestFocus = true; + + // Install a focus-lost listener on the table widget to close an active cell-editor when the table looses the focus. + m_focusLostListener.install(); + + // Install keystrokes to exit editing mode. + m_environment.addKeyStroke(m_container, new SwtKeyStroke(SWT.ESC) { + @Override + public void handleSwtAction(Event event) { + event.doit = false; + fireCancelEditor(); + } + }); + m_environment.addKeyStroke(m_container, new SwtKeyStroke(SWT.CR) { + @Override + public void handleSwtAction(Event event) { + event.doit = false; + fireApplyEditorValue(); + } + }); + m_environment.addKeyStroke(m_container, new SwtKeyStroke(SWT.KEYPAD_CR) { + @Override + public void handleSwtAction(Event event) { + event.doit = false; + fireApplyEditorValue(); + } + }); + + // Specific cell-editor for boolean values. + if (m_scoutColumn instanceof IBooleanColumn) { + if (e.sourceEvent instanceof MouseEvent) { + // no edit-mode when a boolean cell was clicked by mouse because being inverted and the editing mode closed in AbstractTable#interceptRowClickSingleObserver. + m_requestFocus = false; + return; + } + else { + // hide the checkbox image when editing a boolean value in traversal-mode. + ViewerCell cell = (ViewerCell) e.getSource(); + m_image = cell.getImage(); + cell.setImage(null); + } + } + + // create the Scout model field. + IFormField formField = createFormField(); + if (formField == null) { + LOG.warn("Failed to create FormField for cell-editor; editing mode canceled."); + m_requestFocus = false; + fireCancelEditor(); + return; + } + + // create the UI field. + ISwtScoutComposite swtScoutFormField; + if (formField instanceof IStringField && ((IStringField) formField).isMultilineText()) { + // open a separate Shell to edit the content. + swtScoutFormField = createPopupEditorControl(m_container, formField); + } + else { + swtScoutFormField = m_environment.createFormField(m_container, formField); + } + // hook to customize the form field. + decorateEditorComposite(swtScoutFormField, m_scoutRow, m_scoutColumn); + + m_container.layout(true, true); + m_container.setVisible(true); + } + + @Override + protected void doSetFocus() { + if (!m_requestFocus) { + return; + } + + // traverse the focus to the cell editor's control so that the user can start editing immediately without having to click into the widget first. + m_container.traverse(SWT.TRAVERSE_TAB_NEXT); + + Control focusControl = m_container.getDisplay().getFocusControl(); + if (focusControl != null && SwtUtility.isAncestorOf(m_container, focusControl)) { + focusControl.addTraverseListener(new TraverseListener() { + @Override + public void keyTraversed(TraverseEvent e) { + switch (e.detail) { + case SWT.TRAVERSE_ESCAPE: + case SWT.TRAVERSE_RETURN: { + e.doit = false; + break; + } + case SWT.TRAVERSE_TAB_NEXT: { + e.doit = false; + fireApplyEditorValue(); + enqueueEditNextTableCell(true); // traverse the focus to the next editable cell. + break; + } + case SWT.TRAVERSE_TAB_PREVIOUS: { + e.doit = false; + fireApplyEditorValue(); + enqueueEditNextTableCell(false); // traverse the focus to the next editable cell. + break; + } + } + } + }); + } + } + + @Override + protected void deactivate(ColumnViewerEditorDeactivationEvent e) { + // restore the cell's image if being unset in CellEditor#activate. + ViewerCell cell = (ViewerCell) e.getSource(); + if (m_image != null) { + cell.setImage(m_image); + } + + m_image = null; + + // Dispose the cell-editor; in turn, any Shell opened by the editor is closed as well. + for (Control c : m_container.getChildren()) { + c.dispose(); + } + + super.deactivate(e); + + m_focusLostListener.uninstall(); + } + + @Override + protected boolean dependsOnExternalFocusListener() { + return false; + } + + protected void enqueueEditNextTableCell(final boolean forward) { + m_environment.invokeScoutLater(new Runnable() { + @Override + public void run() { + ITable table = m_scoutColumn.getTable(); + TableUtility.editNextTableCell(table, m_scoutRow, m_scoutColumn, forward, new TableUtility.ITableCellEditorFilter() { + @Override + public boolean accept(ITableRow rowx, IColumn<?> colx) { + return true; + } + }); + } + }, 0L); + } + + protected ISwtScoutComposite<? extends IFormField> createPopupEditorControl(final Composite parent, IFormField formField) { + // overwrite layout properties + GridData gd = formField.getGridData(); + gd.h = 1; + gd.w = IFormField.FULL_WIDTH; + gd.weightY = 1; + gd.weightX = 1; + formField.setGridDataInternal(gd); + + int prefWidth = gd.widthInPixel; + int minWidth = m_swtColumn.getWidth(); + int prefHeight = gd.heightInPixel; + int minHeight = Math.max(105, m_swtTable.getItemHeight()); + + prefHeight = Math.max(prefHeight, minHeight); + prefWidth = Math.max(prefWidth, minWidth); + + // Create placeholder field to represent the cell editor + final Composite cellEditorComposite = new Composite(parent, SWT.NONE); + + // Create popup dialog to wrap the form field + final SwtScoutFormFieldPopup popup = new SwtScoutFormFieldPopup(cellEditorComposite); + popup.setPrefHeight(prefHeight); + popup.setPrefWidth(prefWidth); + popup.setMinHeight(minHeight); + popup.setMinWidth(minWidth); + + // Focus is set the time the Shell is opened. + m_requestFocus = false; + + // == IFormFieldPopupListener == + // To receive events about the popup's state. The popup is not closed yet but the cell-editor closed. + final IFormFieldPopupListener formFieldPopupListener = new IFormFieldPopupListener() { + + @Override + public void handleEvent(int event) { + if ((event & IFormFieldPopupListener.TYPE_OK) > 0) { + SwtUtility.runSwtInputVerifier(popup.getSwtField()); // write the value back into the model. + fireApplyEditorValue(); + } + else if ((event & IFormFieldPopupListener.TYPE_CANCEL) > 0) { + fireCancelEditor(); + } + + // traversal control + if ((event & IFormFieldPopupListener.TYPE_FOCUS_BACK) > 0) { + enqueueEditNextTableCell(false); + } + else if ((event & IFormFieldPopupListener.TYPE_FOCUS_NEXT) > 0) { + enqueueEditNextTableCell(true); + } + } + }; + popup.addListener(formFieldPopupListener); + + // == DisposeListener == + // To close the Shell if the cell-editor is disposed. + cellEditorComposite.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + popup.removeListener(formFieldPopupListener); // ignore resulting popup events. + + // Close the popup Shell. + // The asyncExec is a workaround so that other cell-editors can be activated immediately. + // Note: If being dirty, 'Viewer#refresh()' in TableEx prevents the cell from being activated immediately. + e.display.asyncExec(new Runnable() { + + @Override + public void run() { + popup.closePopup(); + } + }); + } + }); + + // Open the popup for the form field. + popup.createField(parent, formField, m_environment); + + return popup; + } + + protected IFormField createFormField() { + final Holder<IFormField> result = new Holder<IFormField>(); + Runnable t = new Runnable() { + @Override + public void run() { + result.setValue(m_scoutTable.getUIFacade().prepareCellEditFromUI(m_scoutRow, m_scoutColumn)); + } + }; + try { + m_environment.invokeScoutLater(t, 2345).join(2345); + } + catch (InterruptedException e) { + LOG.warn("Interrupted while waiting for the Form-Field to be created.", e); + } + return result.getValue(); + } + + /** + * Callback to be overwritten to customize the {@link IFormField}. + */ + protected void decorateEditorComposite(ISwtScoutComposite editorComposite, final ITableRow scoutRow, final IColumn<?> scoutCol) { + } + + /** + * Hysteresis listener that commits the cell editor when the table has first received focus and then lost it. That is + * because cell editors in SWT are not closed automatically if the table looses the focus. + */ + private class P_FocusLostListener implements Listener { + + /** + * Installs listening for focus-lost events on the table widget. + */ + public void install() { + m_environment.getDisplay().addFilter(SWT.FocusIn, this); + } + + /** + * Uninstalls listening for focus-lost events on the table widget. + */ + public void uninstall() { + m_environment.getDisplay().removeFilter(SWT.FocusIn, this); + } + + @Override + public void handleEvent(Event event) { + Widget w = event.widget; + if (w == null || !(w instanceof Control) || w.isDisposed()) { + return; + } + + // Sanity check whether a cell-editor is active. + TableViewer viewer = m_tableViewer; + if (!viewer.isCellEditorActive()) { + return; + } + + Control focusOwner = (Control) w; + Table table = m_tableViewer.getTable(); + + // Check if the table is the focus owner. + if (SwtUtility.isAncestorOf(table, focusOwner)) { + return; + } + + // Check if a Shell opened by the cell-editor is the focus owner. + if (focusOwner.getShell() != table.getShell()) { + Composite parentFocusOwner = focusOwner.getShell().getParent(); + while (parentFocusOwner != null) { + if (parentFocusOwner.getShell() == table.getShell()) { + return; // focus owner is a derrived Shell. + } + else { + parentFocusOwner = parentFocusOwner.getShell().getParent(); + } + } + } + + // Close the cell-editor because a control other than the table is focus owner. + fireApplyEditorValue(); + } + } +} diff --git a/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/TableEditingSupport.java b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/TableEditingSupport.java new file mode 100644 index 0000000000..f4dde5d68a --- /dev/null +++ b/org.eclipse.scout.rt.ui.swt/src/org/eclipse/scout/rt/ui/swt/basic/table/celleditor/TableEditingSupport.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2014 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.celleditor; + +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.ICellEditorListener; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.scout.commons.BooleanUtility; +import org.eclipse.scout.commons.holders.BooleanHolder; +import org.eclipse.scout.commons.logger.IScoutLogger; +import org.eclipse.scout.commons.logger.ScoutLogManager; +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.columns.IColumn; +import org.eclipse.scout.rt.ui.swt.ISwtEnvironment; +import org.eclipse.scout.rt.ui.swt.basic.table.ISwtScoutTable; +import org.eclipse.scout.rt.ui.swt.basic.table.SwtScoutTable; +import org.eclipse.swt.widgets.TableColumn; + +/** + * <p> + * Editing Support for {@link SwtScoutTable}. + * </p> + * Each editable column has its own {@link EditingSupport} instance. + */ +public class TableEditingSupport extends EditingSupport implements ICellEditorListener { + + private static final IScoutLogger LOG = ScoutLogManager.getLogger(TableEditingSupport.class); + + private ISwtEnvironment m_environment; + + private ITable m_scoutTable; + private IColumn<?> m_scoutColumn; + private TableColumn m_swtColumn; + + public TableEditingSupport(TableViewer viewer, TableColumn swtColumn, ISwtEnvironment environment) { + super(viewer); + + m_scoutColumn = (IColumn<?>) swtColumn.getData(ISwtScoutTable.KEY_SCOUT_COLUMN); + m_swtColumn = swtColumn; + m_scoutTable = m_scoutColumn.getTable(); + + m_environment = environment; + } + + @Override + public TableViewer getViewer() { + return (TableViewer) super.getViewer(); + } + + @Override + protected boolean canEdit(Object element) { + final ITableRow row = (ITableRow) element; + + final BooleanHolder editable = new BooleanHolder(); + try { + m_environment.invokeScoutLater(new Runnable() { + @Override + public void run() { + editable.setValue(m_scoutTable.isCellEditable(row, m_scoutColumn)); + } + }, 2345).join(2345); + } + catch (InterruptedException e) { + LOG.warn("Interrupted while waiting for the model to determine the cell's editability.", e); + } + return BooleanUtility.nvl(editable.getValue(), false); + } + + @Override + protected CellEditor getCellEditor(Object element) { + CellEditor cellEditor = new TableCellEditor(getViewer(), m_swtColumn, (ITableRow) element, m_environment); + cellEditor.addListener(this); + return cellEditor; + } + + @Override + protected Object getValue(Object element) { + // NOOP: The value is set into the cell-editor when it is created. + return null; + } + + @Override + protected void setValue(Object element, Object value) { + // NOOP: Only notify the model about completing the editing mode. + // The value itself is written back into the model by the widget's verify event. + m_environment.invokeScoutLater(new Runnable() { + @Override + public void run() { + m_scoutTable.getUIFacade().completeCellEditFromUI(); + } + }, 0); + } + + // == ICellEditorListener == + // To notify Scout about canceling editing. + + @Override + public void cancelEditor() { + m_environment.invokeScoutLater(new Runnable() { + @Override + public void run() { + m_scoutTable.getUIFacade().cancelCellEditFromUI(); + } + }, 0); + } + + @Override + public void applyEditorValue() { + // NOOP: is done in #setValue. + } + + @Override + public void editorValueChanged(boolean oldValidState, boolean newValidState) { + // NOOP + } +} |