Bug 453504 - SWT: Nullpointer when toggling a checkable

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
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 d4452f1..d49c438 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.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.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 @@
   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 @@
     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 @@
       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 @@
         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);
 
-        TableViewerColumn viewerColumn = new TableViewerColumn(getSwtTableViewer(), swtCol);
+        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()));
 
-        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 @@
         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 @@
     }
 
     @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 @@
       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 e62ec8a..eb61a2a 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.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.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 @@
   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 @@
         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 @@
     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 e9cf446..0000000
--- 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 0000000..dcab367
--- /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 0000000..f4dde5d
--- /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
+  }
+}