blob: 94f21318a9e321e008c356905754e2e5121b1007 [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 org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
/**
* A specialization of {@link TextCellEditor} that uses a multi line text editor
* as editor control. To support multi line editing correctly, the behaviour to
* commit on pressing the enter key is disabled.
* <p>
* A multi line editor usually needs some space. Therefore it might be a good
* decision to set the configuration attribute
* {@link EditConfigAttributes#OPEN_IN_DIALOG} to <code>true</code> for this
* editor, so the editor always opens in a subdialog.
* </p>
* <p>
* As some table layouts may support enough space for an inline cell editor,
* this editor does not specify
* {@link ICellEditor#openInline(org.eclipse.nebula.widgets.nattable.config.IConfigRegistry, java.util.List)}
* to always return <code>false</code>.
* </p>
*/
public class MultiLineTextCellEditor extends TextCellEditor {
/**
* Flag to configure whether the text control should enable automatic line
* wrap behaviour or not. By default this editor will support automatic line
* wrapping.
*/
private boolean lineWrap = true;
/**
* Create a new multi line text editor that ensures to not commit the editor
* value in case enter is typed. The text control will support automatic
* line wrapping.
*/
public MultiLineTextCellEditor() {
this(true);
}
/**
* Create a new multi line text editor that ensures to not commit the editor
* value in case enter is typed.
*
* @param lineWrap
* Flag to configure whether the text control should enable
* automatic line wrap behaviour or not.
*/
public MultiLineTextCellEditor(boolean lineWrap) {
this.commitOnEnter = false;
this.lineWrap = lineWrap;
}
@Override
public Text createEditorControl(Composite parent) {
boolean openInline = (this.editMode == EditModeEnum.INLINE);
int style = HorizontalAlignmentEnum.getSWTStyle(this.cellStyle) | SWT.MULTI | SWT.BORDER;
if (!openInline) {
// if the editor control is opened in a dialog, we add scrolling as
// the size of the control is dependent on the dialog size
style = style | SWT.V_SCROLL;
}
if (this.lineWrap) {
style = style | SWT.WRAP;
} else if (!openInline) {
// if the editor control is opened in a dialog, we add scrolling as
// the size of the control is dependent on the dialog size
style = style | SWT.H_SCROLL;
}
final Text textControl = super.createEditorControl(parent, style);
if (!openInline) {
// add the layout data directly so it will not be layouted by the
// CellEditDialog
GridDataFactory.fillDefaults().grab(true, true).hint(100, 50).applyTo(textControl);
}
// On inline editing there need to be a different handling of the return
// key as the Text control is performing a new line on return, it is not
// possible to commit a value by pressing enter. So for inline editing
// we catch enter to perform the commit, while pressing ALT + ENTER will
// add a new line. With setting commitWithCtrlKey to true the commit
// will only be performed if CTRL + ENTER is pressed.
if (openInline) {
this.commitOnEnter = true;
textControl.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent event) {
if (event.keyCode == SWT.CR
|| event.keyCode == SWT.KEYPAD_CR) {
if (event.stateMask == SWT.MOD3) {
textControl.insert(textControl.getLineDelimiter());
}
}
}
});
}
return textControl;
}
@Override
public Rectangle calculateControlBounds(final Rectangle cellBounds) {
int widthHintForCompute = this.lineWrap ? cellBounds.width : SWT.DEFAULT;
Point size = getEditorControl().computeSize(widthHintForCompute, SWT.DEFAULT);
int diff = 0;
if (this.lineWrap) {
// Because of computeTrim internally the computed width is bigger
// than the given width. We therefore need to calculate twice by
// removing the trim diff to get the correct size.
diff = size.x - cellBounds.width;
size = getEditorControl().computeSize(widthHintForCompute - diff, SWT.DEFAULT);
}
final int widthHint = widthHintForCompute - diff;
// add a listener that increases/decreases the size of the control if
// the text is modified as the calculateControlBounds method is only
// called in case of inline editing, this listener shouldn't hurt
// anybody else
getEditorControl().addModifyListener(e -> {
Point p = getEditorControl().computeSize(widthHint, SWT.DEFAULT, true);
Point loc = getEditorControl().getLocation();
getEditorControl().setBounds(
loc.x,
loc.y,
MultiLineTextCellEditor.this.lineWrap ? cellBounds.width : Math.max(p.x, cellBounds.width),
Math.max(p.y, cellBounds.height));
});
return new Rectangle(
cellBounds.x,
cellBounds.y,
this.lineWrap ? cellBounds.width : Math.max(size.x, cellBounds.width),
Math.max(size.y, cellBounds.height));
}
/**
* @param lineWrap
* <code>true</code> if the text control should enable automatic
* line wrap behaviour, <code>false</code> if not
*/
public void setLineWrap(boolean lineWrap) {
this.lineWrap = lineWrap;
}
}