blob: c19743555270c12e62d7d3abb7f5eded93fb4a23 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2021 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.extension.nebula.cdatetime;
import java.util.Calendar;
import java.util.Date;
import org.eclipse.nebula.widgets.cdatetime.CDT;
import org.eclipse.nebula.widgets.cdatetime.CDateTime;
import org.eclipse.nebula.widgets.nattable.edit.editor.AbstractCellEditor;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
/**
* ICellEditor implementation that uses the Nebula {@link CDateTime} control for
* editing. It supports objects of type Date and Calendar aswell.
* <p>
* Introduces the contract that the editor control value is of type Calendar.
* Therefore the methods to deal with the canonical values need to be overriden
* too, to avoid conversion of the canonical value to display value by using the
* IDisplayConverter that is registered together with this editor.
*
* @since 1.1
*/
public class CDateTimeCellEditor extends AbstractCellEditor {
/**
* The DateTime control which is the editor wrapped by this DateCellEditor.
*/
private CDateTime dateTime;
/**
* Flag to configure whether the selection should move after a value was
* committed after pressing enter.
*/
private final boolean moveSelectionOnEnter;
/**
* The style bits that should be used to create the {@link CDateTime}
* control of the editor.
*/
private int style;
/**
* Flag to configure whether the editor should provide a {@link Calendar}
* object on editing or a {@link Date}. By default a {@link Date} is
* returned as {@link CDateTime} returns {@link Date} objects.
*/
private boolean provideCalendar = false;
/**
* Creates the default DateCellEditor that does not move the selection on
* committing the value by pressing enter.
*/
public CDateTimeCellEditor() {
this(false);
}
/**
* Creates a DateCellEditor.
*
* @param moveSelectionOnEnter
* Flag to configure whether the selection should move after a
* value was committed after pressing enter.
*/
public CDateTimeCellEditor(boolean moveSelectionOnEnter) {
this(moveSelectionOnEnter, CDT.DROP_DOWN | CDT.DATE_SHORT | CDT.TIME_SHORT);
}
/**
* Creates a DateCellEditor.
*
* @param moveSelectionOnEnter
* Flag to configure whether the selection should move after a
* value was committed after pressing enter.
* @param style
* The style bits that should be used to create the
* {@link CDateTime} control of the editor.
*/
public CDateTimeCellEditor(boolean moveSelectionOnEnter, int style) {
this.moveSelectionOnEnter = moveSelectionOnEnter;
this.style = style;
}
@Override
public Object getEditorValue() {
if (this.dateTime.getSelection() != null) {
return this.dateTime.getSelection();
}
return null;
}
@Override
public void setEditorValue(Object value) {
if (value instanceof Calendar) {
Calendar cal = (Calendar) value;
this.dateTime.setSelection(cal.getTime());
} else if (value instanceof Date) {
this.dateTime.setSelection((Date) value);
}
}
@Override
public Object getCanonicalValue() {
// there is no need for conversion because the CDateTime control
// already returns a Date
if (this.provideCalendar) {
Calendar cal = Calendar.getInstance();
cal.setTime((Date) getEditorValue());
return cal;
}
return getEditorValue();
}
@Override
public void setCanonicalValue(Object canonicalValue) {
Date editorValue = null;
if (canonicalValue instanceof Calendar) {
editorValue = ((Calendar) canonicalValue).getTime();
} else if (canonicalValue instanceof Date) {
editorValue = (Date) canonicalValue;
}
if (editorValue != null) {
setEditorValue(editorValue);
}
}
@Override
public CDateTime getEditorControl() {
return this.dateTime;
}
@Override
public CDateTime createEditorControl(final Composite parent) {
final CDateTime dateControl = new CDateTime(parent, this.style) {
@Override
protected Shell getContentShell() {
Shell shell = super.getContentShell();
shell.addShellListener(new ShellAdapter() {
@Override
public void shellActivated(ShellEvent e) {
if (CDateTimeCellEditor.this.focusListener instanceof InlineFocusListener) {
((InlineFocusListener) CDateTimeCellEditor.this.focusListener).handleFocusChanges = false;
}
}
@Override
public void shellClosed(ShellEvent e) {
if (CDateTimeCellEditor.this.focusListener instanceof InlineFocusListener) {
((InlineFocusListener) CDateTimeCellEditor.this.focusListener).handleFocusChanges = true;
}
}
});
return shell;
}
@Override
protected void postClose(Shell popup) {
if (CDateTimeCellEditor.this.focusListener instanceof InlineFocusListener) {
((InlineFocusListener) CDateTimeCellEditor.this.focusListener).handleFocusChanges = true;
}
super.postClose(popup);
}
@Override
public void setOpen(boolean open) {
if (CDateTimeCellEditor.this.focusListener instanceof InlineFocusListener) {
((InlineFocusListener) CDateTimeCellEditor.this.focusListener).handleFocusChanges = false;
}
super.setOpen(open);
}
@Override
public void setOpen(boolean open, Runnable callback) {
if (CDateTimeCellEditor.this.focusListener instanceof InlineFocusListener) {
((InlineFocusListener) CDateTimeCellEditor.this.focusListener).handleFocusChanges = false;
}
super.setOpen(open, callback);
}
@Override
protected void addTextListener() {
super.addTextListener();
this.text.getControl().addTraverseListener(event -> {
boolean committed = false;
if (event.keyCode == SWT.TAB && event.stateMask == SWT.MOD2) {
committed = commit(MoveDirectionEnum.LEFT);
} else if (event.keyCode == SWT.TAB && event.stateMask == 0) {
committed = commit(MoveDirectionEnum.RIGHT);
} else if (event.detail == SWT.TRAVERSE_ESCAPE) {
close();
}
if (!committed) {
event.doit = false;
}
});
}
};
// set style information configured in the associated cell style
dateControl.setBackground(this.cellStyle.getAttributeValue(CellStyleAttributes.BACKGROUND_COLOR));
dateControl.setForeground(this.cellStyle.getAttributeValue(CellStyleAttributes.FOREGROUND_COLOR));
// when trying to set a font in simple mode (embedded GUI) we will get a
// StackOverflowError
if ((this.style & CDT.SIMPLE) == 0) {
dateControl.setFont(this.cellStyle.getAttributeValue(CellStyleAttributes.FONT));
}
dateControl.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetDefaultSelected(SelectionEvent event) {
boolean commit = (event.stateMask != SWT.MOD3);
MoveDirectionEnum move = MoveDirectionEnum.NONE;
if (CDateTimeCellEditor.this.moveSelectionOnEnter
&& CDateTimeCellEditor.this.editMode == EditModeEnum.INLINE) {
if (event.stateMask == 0) {
move = MoveDirectionEnum.DOWN;
} else if (event.stateMask == SWT.MOD2) {
move = MoveDirectionEnum.UP;
}
}
if (commit) {
commit(move);
}
if (CDateTimeCellEditor.this.editMode == EditModeEnum.DIALOG) {
parent.forceFocus();
}
}
});
return dateControl;
}
@Override
protected Control activateCell(Composite parent, Object originalCanonicalValue) {
this.dateTime = createEditorControl(parent);
setCanonicalValue(originalCanonicalValue);
// this is necessary so the control gets the focus
// but this also causing some issues as focusing the DateTime control
// programmatically does some strange things with showing the editable
// data also it seems to be not possible to open the dropdown
// programmatically
this.dateTime.forceFocus();
return this.dateTime;
}
/**
*
* @param provideCalendar
* <code>true</code> if this editor should provide a
* {@link Calendar} object on editing, <code>false</code> if it
* should provide a {@link Date} object.
*/
public void setProvideCalendar(boolean provideCalendar) {
this.provideCalendar = provideCalendar;
}
}