blob: 1efde854ff3abd16cec9b9dea946be3f4940941c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2020 Dirk Fauth and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.nattable.edit.editor;
import java.util.Arrays;
import java.util.Collection;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ICellEditorListener;
import org.eclipse.jface.viewers.ICellEditorValidator;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.nebula.widgets.nattable.Messages;
import org.eclipse.nebula.widgets.nattable.config.NatTableConfigAttributes;
import org.eclipse.nebula.widgets.nattable.data.convert.ConversionFailedException;
import org.eclipse.nebula.widgets.nattable.data.validate.ValidationFailedException;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.layer.IDpiConverter;
import org.eclipse.nebula.widgets.nattable.painter.cell.TableCellPainter;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.style.CellStyleProxy;
import org.eclipse.nebula.widgets.nattable.style.CellStyleUtil;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.style.IStyle;
import org.eclipse.nebula.widgets.nattable.style.Style;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
/**
* ICellEditor that uses a JFace TableViewer as editor control. In combination
* with the TableCellPainter it can be used to simulate a table within a cell to
* support editing of collections or arrays of values of an entity. This
* implementation is a workaround for the missing feature of nested tables in
* NatTable 1.x architecture. It is limited to one column editing. If a more
* complex nested table structure is needed, you need to extend or implement a
* new table cell editor.
* <p>
* Note: This editor is different to other default editors in NatTable in
* several facts:
* <ul>
* <li>The data value in the NatTable cell needs to be an array or a collection
* of values</li>
* <li><code>getCanonicalValue()</code> will directly update the underlying
* NatTable data model</li>
* <li>Committing the value in the NatTable framework code will simply replace
* the list reference with itself</li>
* <li>It does only support validation error styling for conversion and
* validation errors as well</li>
* </ul>
*
* @see TableCellPainter
*/
public class TableCellEditor extends AbstractCellEditor {
/**
* The internal control that is used for editing a table.
*/
private TableViewer viewer;
/**
* Flag to configure whether the selection should move after a value was
* committed by pressing enter.
*/
private final boolean moveSelectionOnEnter;
/**
* Flag to configure whether the adjacent editor on selection movements
* should always open the cell for editing instead of moving into the
* selection state of the table control.
*/
private final boolean alwaysOpenEditor;
/**
* The height of the sub cells to use. Setting a value >= 0 will result in
* using the specified fixed sub cell heights, a negative value will result
* in using the OS default height based on the font.
* <p>
* Because of limitations in the native table control for some OS, it is not
* possible to specify different row heights.
*/
private int fixedSubCellHeight;
/**
* Internal ColumnLabelProvider that is used to apply styling to the table
* viewer as well as for the editor controls in the table viewer.
*/
private InternalLabelProvider labelProvider;
/**
* Internal ICellEditorValidator that checks if a value is valid based on
* conversion and validation rules applied via NatTable IDisplayConverter
* and IDataValidator
*/
protected ICellEditorValidator cellEditorValidator = value -> {
// add validator to check conversion and validation configured in
// NatTable
if (TableCellEditor.this.displayConverter != null) {
Object cValue = null;
try {
// check if the information can be converted to the correct
// type
cValue = TableCellEditor.this.displayConverter.displayToCanonicalValue(
TableCellEditor.this.layerCell, TableCellEditor.this.configRegistry, value);
if (!validateCanonicalValue(cValue)) {
return Messages.getString("AbstractCellEditor.validationFailure"); //$NON-NLS-1$
}
} catch (ConversionFailedException | ValidationFailedException e) {
return e.getLocalizedMessage();
}
}
return null;
};
/**
* Creates a TableCellEditor with a default sub cell height of 20 (which is
* the same as the default for the TableCellPainter), that moves the
* selection on committing the value with enter and always opens the editor
* of the cell that is currently selected.
*/
public TableCellEditor() {
this(20, true, true);
}
/**
* Creates a TableCellEditor with the given configurations.
*
* @param fixedSubCellHeight
* The height of the sub cells to use. Setting a value &gt;= 0
* will result in using the specified fixed sub cell heights, a
* negative value will result in using the OS default height
* based on the font. Note that because of limitations in the
* native table control for some OS, it is not possible to
* specify different row heights.
* @param moveSelectionOnEnter
* configure whether the selection should move after a value was
* committed by pressing enter.
* @param alwaysOpenEditor
* configure whether the adjacent editor on selection movements
* should always open the cell for editing instead of moving into
* the selection state of the table control.
*/
public TableCellEditor(int fixedSubCellHeight, boolean moveSelectionOnEnter, boolean alwaysOpenEditor) {
this.fixedSubCellHeight = fixedSubCellHeight;
this.moveSelectionOnEnter = moveSelectionOnEnter;
this.alwaysOpenEditor = alwaysOpenEditor;
// Internal focus listener to handle committing and closing of this
// editor if the focus is lost out of the editor control AND the editing
// support editor control.
this.focusListener = new InternalFocusListener();
}
@Override
public Object getEditorValue() {
return this.viewer.getInput();
}
@Override
public void setEditorValue(Object value) {
if (value != null && value.getClass().isArray()) {
this.viewer.setInput(value);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Object getCanonicalValue() {
Object[] editorValues = getDataAsArray(getEditorValue());
// perform conversion
Object[] dataValues = new Object[editorValues.length];
Object canonicalValue;
for (int i = 0; i < editorValues.length; i++) {
Object value = editorValues[i];
if (this.displayConverter != null) {
canonicalValue = this.displayConverter.displayToCanonicalValue(this.layerCell, this.configRegistry, value);
} else {
canonicalValue = value;
}
dataValues[i] = canonicalValue;
}
if (this.layerCell.getDataValue().getClass().isArray()) {
Object[] cellDataArray = (Object[]) this.layerCell.getDataValue();
for (int i = 0; i < cellDataArray.length; i++) {
cellDataArray[i] = dataValues[i];
}
} else if (this.layerCell.getDataValue() instanceof Collection) {
// we don't create new collections, we operate on the existing
// this is because we don't know the exact collection implementation
// that we would need to create for type safety and performing an
// instanceof check for every possible collection implementation
// would be to complicated and could never be complete
Collection cellDataCollection = (Collection) this.layerCell.getDataValue();
cellDataCollection.clear();
cellDataCollection.addAll(Arrays.asList(dataValues));
}
return this.layerCell.getDataValue();
}
@Override
public void setCanonicalValue(Object canonicalValue) {
Object[] values = getDataAsArray(canonicalValue);
if (values != null) {
// transform the array of canonical data values to an array of
// display values
ValueWrapper[] editorValues = new ValueWrapper[values.length];
Object displayValue;
for (int i = 0; i < values.length; i++) {
Object value = values[i];
if (this.displayConverter != null) {
displayValue = this.displayConverter.canonicalToDisplayValue(this.layerCell, this.configRegistry, value);
} else {
displayValue = value;
}
editorValues[i] = new ValueWrapper("" + displayValue); //$NON-NLS-1$
}
setEditorValue(editorValues);
}
}
@Override
public Table getEditorControl() {
return this.viewer.getTable();
}
@Override
public Table createEditorControl(Composite parent) {
// need to directly set the member variable because a TableViewer is not
// a Control and therefore we can not return the TableViewer here
this.viewer = new TableViewer(parent, SWT.FULL_SELECTION);
this.viewer.setContentProvider(ArrayContentProvider.getInstance());
// this column is simply added because of the restriction that the first
// column in a table is always left aligned
TableViewerColumn emptyColumn = new TableViewerColumn(this.viewer, SWT.NONE);
emptyColumn.getColumn().setWidth(0);
emptyColumn.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
return ""; //$NON-NLS-1$
}
});
TableViewerColumn singleColumn = new TableViewerColumn(this.viewer, SWT.NONE);
singleColumn.getColumn().setAlignment(HorizontalAlignmentEnum.getSWTStyle(this.cellStyle));
singleColumn.setLabelProvider(this.labelProvider);
singleColumn.setEditingSupport(getEditingSupport());
// set style information configured in the associated cell style
final Table tableControl = this.viewer.getTable();
tableControl.setBackground(this.cellStyle.getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR));
tableControl.setLinesVisible(true);
if (getFixedSubCellHeight() >= 0) {
IDpiConverter dpiConverter = TableCellEditor.this.configRegistry.getConfigAttribute(
NatTableConfigAttributes.HORIZONTAL_DPI_CONVERTER,
DisplayMode.NORMAL);
int height = (dpiConverter != null)
? (int) (dpiConverter.getCurrentDpiFactor() * getFixedSubCellHeight())
: getFixedSubCellHeight();
tableControl.addListener(SWT.MeasureItem, event -> event.height = height + 1);
}
// add a key listener that will close the editor on pressing ESC
tableControl.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent event) {
if (event.keyCode == SWT.ESC && event.stateMask == 0) {
close();
}
if (event.keyCode == SWT.F2) {
Object element = ((StructuredSelection) TableCellEditor.this.viewer.getSelection()).getFirstElement();
if (element == null) {
tableControl.setSelection(tableControl.getTopIndex());
element = ((StructuredSelection) TableCellEditor.this.viewer.getSelection()).getFirstElement();
}
if (element != null) {
TableCellEditor.this.viewer.editElement(element, 1);
}
}
}
});
tableControl.addListener(SWT.Resize, event -> {
// it is the second column, because the first column is not
// visible as we want to support alignment in the cells
tableControl.getColumn(1).setWidth(tableControl.getClientArea().width);
// set focus on the table viewer and select the cell where the
// mouse is positioned this can only be done when the control is
// resized, as the bounds are set in the EditController
// dependent on the rendered cell
Point mouseLoc = Display.getCurrent().getCursorLocation();
Point tablePos = tableControl.toDisplay(0, 0);
int relativeX = mouseLoc.x - tablePos.x;
int relativeY = mouseLoc.y - tablePos.y;
TableItem item = tableControl.getItem(new Point(relativeX, relativeY));
if (item != null) {
tableControl.setSelection(item);
} else {
tableControl.setSelection(tableControl.getTopIndex());
}
// directly enable editing of the selected item
if (tableControl.getItemCount() > 0)
TableCellEditor.this.viewer.editElement(
((StructuredSelection) TableCellEditor.this.viewer.getSelection()).getFirstElement(), 1);
});
return tableControl;
}
@Override
protected Control activateCell(Composite parent, Object originalCanonicalValue) {
this.labelProvider = new InternalLabelProvider();
createEditorControl(parent);
setCanonicalValue(originalCanonicalValue);
getEditorControl().forceFocus();
return getEditorControl();
}
@Override
public void close() {
// this check is added to ensure that an open cell editor is also
// committed if the user clicks in another cell after editing within the
// table cell editor otherwise the framework performs a commit and close
// BEFORE the cell editor of the table viewer commits the value
this.viewer.applyEditorValue();
super.close();
}
@Override
public boolean commit(MoveDirectionEnum direction, boolean closeAfterCommit, boolean skipValidation) {
// Apply the value of an active cell editor if one is active. Otherwise
// the old value will be committed to the NatTable BEFORE the cell
// editor was able to apply.
this.viewer.applyEditorValue();
return super.commit(direction, closeAfterCommit, skipValidation);
}
/**
* Checks if the given data object is of type Collection or Array. Will
* return the Collection or Array as Object[] or <code>null</code> if the
* data object is not a Collection or Array.
*
* @param cellData
* The cellData that should be checked for its type.
* @return The Object[] representation of the data object if it is of type
* Collection or Array, or <code>null</code> if the data object is
* not a Collection or Array.
*/
protected Object[] getDataAsArray(Object cellData) {
Object[] cellDataArray = null;
if (cellData != null) {
if (cellData.getClass().isArray()) {
cellDataArray = (Object[]) cellData;
} else if (cellData instanceof Collection) {
Collection<?> cellDataCollection = (Collection<?>) cellData;
cellDataArray = cellDataCollection.toArray();
}
}
return cellDataArray;
}
/**
* Note that because of limitations to native tables of the OS, it is not
* possible to specify different row heights.
*
* @return The height of the sub cells to use. A value &gt;= 0 results in
* using the specified fixed sub cell heights, a negative value
* results in using the OS default height based on the font.
*/
public int getFixedSubCellHeight() {
return this.fixedSubCellHeight;
}
/**
* Setting a value &gt;= 0 will result in using a fixed height of the sub
* cells. Setting the value to a negative number will result in using the OS
* default height based on the font.
* <p>
* Note that because of limitations to native tables of the OS, it is not
* possible to specify different row heights.
*
* @param fixedSubCellHeight
* The height of the sub cells to use.
*/
public void setFixedSubCellHeight(int fixedSubCellHeight) {
this.fixedSubCellHeight = fixedSubCellHeight;
}
/**
* @return The EditingSupport to use to make the TableViewer editable.
*/
protected EditingSupport getEditingSupport() {
return new TableCellEditingSupport();
}
/**
* {@inheritDoc}
* <p>
* Note: We need to override this to not register the FocusListener. This is
* necessary because on editing the TableViewer, a Text control will be
* created that gains focus. This would mean to close the editor and break
* the whole use case.
*/
@Override
public void addEditorControlListeners() {
Control editorControl = getEditorControl();
if (editorControl != null && !editorControl.isDisposed()
&& this.editMode == EditModeEnum.INLINE) {
editorControl.addTraverseListener(this.traverseListener);
editorControl.addFocusListener(this.focusListener);
}
}
@Override
public void removeEditorControlListeners() {
Control editorControl = getEditorControl();
if (editorControl != null && !editorControl.isDisposed()) {
editorControl.removeTraverseListener(this.traverseListener);
editorControl.removeFocusListener(this.focusListener);
}
}
/**
* This class is needed to make editing work correctly within the
* TableViewer. If we only work with the values themselves and the
* collection contains the same values like for example the same Strings,
* calling editElement() will always jump to the first element in the table
* with that value instead of the selected one. With this wrapper we ensure
* that the selected value is edited because we do not override equals() and
* hashCode()
*/
protected class ValueWrapper {
private Object value;
private boolean valid = true;
protected ValueWrapper(Object value) {
this.setValue(value);
}
public Object getValue() {
return this.value;
}
public void setValue(Object value) {
this.value = value;
this.valid = TableCellEditor.this.cellEditorValidator.isValid(value) == null;
}
public boolean isValid() {
return this.valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
@Override
public String toString() {
return this.value != null ? this.value.toString() : ""; //$NON-NLS-1$
}
}
/**
* EditingSupport to make the TableViewer control of this cell editor
* editable. Will use a org.eclipse.jface.viewers.TextCellEditor for
* editing.
*/
protected class TableCellEditingSupport extends EditingSupport {
private CellEditor editor;
public TableCellEditingSupport() {
super(TableCellEditor.this.viewer);
int style = SWT.SINGLE | HorizontalAlignmentEnum.getSWTStyle(TableCellEditor.this.cellStyle);
this.editor = new org.eclipse.jface.viewers.TextCellEditor(TableCellEditor.this.viewer.getTable(), style);
}
@Override
protected CellEditor getCellEditor(final Object element) {
int style = SWT.SINGLE | HorizontalAlignmentEnum.getSWTStyle(TableCellEditor.this.cellStyle);
this.editor = new org.eclipse.jface.viewers.TextCellEditor(TableCellEditor.this.viewer.getTable(), style);
this.editor.setValidator(TableCellEditor.this.cellEditorValidator);
this.editor.addListener(new ICellEditorListener() {
@Override
public void editorValueChanged(boolean oldValidState, boolean newValidState) {
((ValueWrapper) element).setValid(TableCellEditingSupport.this.editor.isValueValid());
TableCellEditor.this.labelProvider.applyCellStyle(TableCellEditingSupport.this.editor.getControl(), element);
}
@Override
public void cancelEditor() {
close();
}
@Override
public void applyEditorValue() {
// no additional or adjusted action needed
}
});
this.editor.getControl().addTraverseListener(event -> {
if (event.keyCode == SWT.TAB) {
TableCellEditingSupport.this.setValue(element, ((Text) TableCellEditingSupport.this.editor.getControl()).getText());
boolean committed = false;
if (event.stateMask == SWT.MOD2) {
committed = commit(MoveDirectionEnum.LEFT);
} else if (event.stateMask == 0) {
committed = commit(MoveDirectionEnum.RIGHT);
}
if (!committed) {
event.doit = false;
}
}
});
this.editor.getControl().addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent event) {
if (event.keyCode == SWT.CR
|| event.keyCode == SWT.KEYPAD_CR) {
TableCellEditingSupport.this.setValue(element,
((Text) TableCellEditingSupport.this.editor.getControl()).getText());
if (TableCellEditingSupport.this.editor.isValueValid()) {
int selectionIndex = TableCellEditor.this.viewer.getTable()
.getSelectionIndex();
// if move selection and not last item -> move the
// selection one down
if (TableCellEditor.this.moveSelectionOnEnter
&& selectionIndex + 1 < TableCellEditor.this.viewer.getTable().getItemCount()) {
selectionIndex++;
TableCellEditor.this.viewer.getTable().setSelection(selectionIndex);
if (TableCellEditor.this.alwaysOpenEditor) {
TableCellEditor.this.viewer.editElement(
((StructuredSelection) TableCellEditor.this.viewer.getSelection()).getFirstElement(), 1);
}
} else {
commit(MoveDirectionEnum.NONE);
}
} else {
TableCellEditor.this.viewer.editElement(((StructuredSelection) TableCellEditor.this.viewer.getSelection()).getFirstElement(), 1);
}
} else if (event.keyCode == SWT.ARROW_DOWN) {
TableCellEditingSupport.this.setValue(element,
((Text) TableCellEditingSupport.this.editor.getControl()).getText());
int selectionIndex = TableCellEditor.this.viewer.getTable().getSelectionIndex();
if (selectionIndex + 1 < TableCellEditor.this.viewer.getTable().getItemCount()) {
selectionIndex++;
}
TableCellEditor.this.viewer.getTable().setSelection(selectionIndex);
if (TableCellEditor.this.alwaysOpenEditor) {
TableCellEditor.this.viewer.editElement(((StructuredSelection) TableCellEditor.this.viewer
.getSelection()).getFirstElement(), 1);
}
} else if (event.keyCode == SWT.ARROW_UP) {
TableCellEditingSupport.this.setValue(element,
((Text) TableCellEditingSupport.this.editor.getControl()).getText());
int selectionIndex = TableCellEditor.this.viewer.getTable().getSelectionIndex();
if (selectionIndex > 0) {
selectionIndex--;
}
TableCellEditor.this.viewer.getTable().setSelection(selectionIndex);
if (TableCellEditor.this.alwaysOpenEditor) {
TableCellEditor.this.viewer.editElement(((StructuredSelection) TableCellEditor.this.viewer
.getSelection()).getFirstElement(), 1);
}
}
}
});
this.editor.getControl().addFocusListener(TableCellEditor.this.focusListener);
TableCellEditor.this.labelProvider.applyCellStyle(this.editor.getControl(), element);
return this.editor;
}
@Override
protected boolean canEdit(Object element) {
return true;
}
@Override
protected Object getValue(Object element) {
return ((ValueWrapper) element).getValue();
}
@Override
protected void setValue(Object element, Object value) {
if (this.editor.isValueValid()) {
((ValueWrapper) element).setValue(value);
((ValueWrapper) element).setValid(true);
} else {
((ValueWrapper) element).setValid(false);
}
TableCellEditor.this.viewer.refresh();
}
}
/**
* ColumnLabelProvider that determines the styles to use in the internal
* editor control of the table viewer based on the registered styles in the
* ConfigRegistry for default style, conversion error style and validation
* error style.
* <p>
* If no explicit styles for conversion and validation errors are
* configured, the foreground is simply rendered in red.
*/
protected class InternalLabelProvider extends ColumnLabelProvider {
private IStyle normalStyle;
private IStyle conversionErrorStyle;
private IStyle validationErrorStyle;
public InternalLabelProvider() {
this.normalStyle = TableCellEditor.this.cellStyle;
this.conversionErrorStyle = TableCellEditor.this.configRegistry.getConfigAttribute(
EditConfigAttributes.CONVERSION_ERROR_STYLE,
DisplayMode.EDIT,
TableCellEditor.this.labelStack);
if (this.conversionErrorStyle == null) {
this.conversionErrorStyle = new Style();
this.conversionErrorStyle.setAttributeValue(
CellStyleAttributes.FOREGROUND_COLOR,
GUIHelper.COLOR_RED);
}
this.validationErrorStyle = TableCellEditor.this.configRegistry.getConfigAttribute(
EditConfigAttributes.VALIDATION_ERROR_STYLE,
DisplayMode.EDIT,
TableCellEditor.this.labelStack);
if (this.validationErrorStyle == null) {
this.validationErrorStyle = new Style();
this.validationErrorStyle.setAttributeValue(
CellStyleAttributes.FOREGROUND_COLOR,
GUIHelper.COLOR_RED);
}
}
/**
* Returns the IStyle based on the state of the given element
*
* @param element
* The element for which the style should be searched
* @return The IStyle for the current state of the given element
*/
public IStyle getActiveCellStyle(Object element) {
if (!((ValueWrapper) element).isValid()) {
return this.validationErrorStyle;
}
return this.normalStyle;
}
/**
* Applies style attributes to the internal cell editor of the table
* viewer based on the state of the value.
*
* @param editorControl
* The internal editor control of the table viewer to set the
* style to
* @param element
* The element that is shown in that cell
*/
public void applyCellStyle(Control editorControl, Object element) {
Color foreground = getForeground(element);
if (foreground != null) {
editorControl.setForeground(foreground);
}
Color background = getBackground(element);
if (background != null) {
editorControl.setBackground(getBackground(element));
}
Font font = getFont(element);
if (font != null) {
editorControl.setFont(font);
}
editorControl.setOrientation(HorizontalAlignmentEnum.getSWTStyle(getActiveCellStyle(element)));
}
@Override
public Color getForeground(Object element) {
return getActiveCellStyle(element).getAttributeValue(CellStyleAttributes.FOREGROUND_COLOR);
}
@Override
public Color getBackground(Object element) {
return getActiveCellStyle(element).getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR);
}
@Override
public Font getFont(Object element) {
if (getFixedSubCellHeight() >= 0) {
IStyle style = getActiveCellStyle(element);
if (style instanceof CellStyleProxy) {
return style.getAttributeValue(CellStyleAttributes.FONT);
}
// if the style is no CellStyleProxy we need to get the upscaled
// font ourself
return CellStyleUtil.getFont(style, TableCellEditor.this.configRegistry);
}
return null;
}
}
/**
* FocusListener that will fire the focus lost event only if the Text editor
* control of the editing support and the table viewer does not have the
* focus. This is because for NatTable use cases they share a focus. So
* closing the TableCellEditor should only happen if both loose focus. Using
* the default focus handling, opening an editor in the table via editing
* support would cause a focus lost event on the table, which immediately
* closes the editor again.
*/
protected class InternalFocusListener implements FocusListener {
boolean hasFocus = false;
@Override
public void focusLost(final FocusEvent e) {
this.hasFocus = false;
Display.getCurrent().timerExec(100, () -> {
if (!InternalFocusListener.this.hasFocus) {
if (!commit(MoveDirectionEnum.NONE, true)) {
if (e.widget instanceof Control
&& !e.widget.isDisposed()) {
((Control) e.widget).forceFocus();
}
}
}
});
}
@Override
public void focusGained(FocusEvent e) {
this.hasFocus = true;
}
}
}