blob: 1fdc9d6bf62ca75e5494e3797938b1e9983be4b3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 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.rap.form.fields.datefield;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
import org.eclipse.scout.commons.CompareUtility;
import org.eclipse.scout.commons.holders.Holder;
import org.eclipse.scout.commons.job.JobEx;
import org.eclipse.scout.rt.client.ui.form.fields.IFormField;
import org.eclipse.scout.rt.client.ui.form.fields.datefield.IDateField;
import org.eclipse.scout.rt.ui.rap.LogicalGridLayout;
import org.eclipse.scout.rt.ui.rap.ext.ButtonEx;
import org.eclipse.scout.rt.ui.rap.ext.StatusLabelEx;
import org.eclipse.scout.rt.ui.rap.ext.StyledTextEx;
import org.eclipse.scout.rt.ui.rap.ext.custom.StyledText;
import org.eclipse.scout.rt.ui.rap.form.fields.IPopupSupport;
import org.eclipse.scout.rt.ui.rap.form.fields.LogicalGridDataBuilder;
import org.eclipse.scout.rt.ui.rap.form.fields.RwtScoutValueFieldComposite;
import org.eclipse.scout.rt.ui.rap.form.fields.datefield.chooser.DateChooserDialog;
import org.eclipse.scout.rt.ui.rap.internal.TextFieldEditableSupport;
import org.eclipse.scout.rt.ui.rap.keystroke.RwtKeyStroke;
import org.eclipse.scout.rt.ui.rap.util.RwtLayoutUtility;
import org.eclipse.scout.rt.ui.rap.util.RwtUtility;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
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.Shell;
//TODO RAP 2.0 Migration
//check shell listener removal
public class RwtScoutDateField extends RwtScoutValueFieldComposite<IDateField> implements IRwtScoutDateField, IPopupSupport {
private Button m_dropDownButton;
private TextFieldEditableSupport m_editableSupport;
private Set<IPopupSupportListener> m_popupEventListeners;
private Object m_popupEventListenerLock;
private boolean m_ignoreLabel = false;
private Composite m_dateContainer;
private boolean m_dateTimeCompositeMember;
private String m_displayTextToVerify;
private DateChooserDialog m_dateChooserDialog = null;
private FocusAdapter m_textFieldFocusAdapter = null;
P_DateChooserDisposeListener m_disposeListener;
@Override
public void setIgnoreLabel(boolean ignoreLabel) {
m_ignoreLabel = ignoreLabel;
if (ignoreLabel) {
getUiLabel().setVisible(false);
}
else {
getUiLabel().setVisible(getScoutObject().isLabelVisible());
}
}
public boolean isIgnoreLabel() {
return m_ignoreLabel;
}
public boolean isDateTimeCompositeMember() {
return m_dateTimeCompositeMember;
}
@Override
public void setDateTimeCompositeMember(boolean dateTimeCompositeMember) {
m_dateTimeCompositeMember = dateTimeCompositeMember;
}
@Override
protected void initializeUi(Composite parent) {
m_popupEventListeners = new HashSet<IPopupSupportListener>();
m_popupEventListenerLock = new Object();
Composite container = getUiEnvironment().getFormToolkit().createComposite(parent);
StatusLabelEx label = getUiEnvironment().getFormToolkit().createStatusLabel(container, getScoutObject());
m_dateContainer = getUiEnvironment().getFormToolkit().createComposite(container, SWT.BORDER);
m_dateContainer.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_DATEFIELD);
StyledText textField = new StyledTextEx(m_dateContainer, SWT.SINGLE);
getUiEnvironment().getFormToolkit().adapt(textField, false, false);
textField.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_DATEFIELD);
ButtonEx dateChooserButton = getUiEnvironment().getFormToolkit().createButtonEx(m_dateContainer, SWT.PUSH | SWT.NO_FOCUS);
dateChooserButton.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_DATEFIELD);
m_dateContainer.setTabList(new Control[]{textField});
container.setTabList(new Control[]{m_dateContainer});
// key strokes on container
getUiEnvironment().addKeyStroke(container, new P_DateChooserOpenKeyStroke(), false);
// key strokes on field
getUiEnvironment().addKeyStroke(textField, new P_ShiftDayUpKeyStroke(), false);
getUiEnvironment().addKeyStroke(textField, new P_ShiftDayDownKeyStroke(), false);
getUiEnvironment().addKeyStroke(textField, new P_ShiftMonthUpKeyStroke(), false);
getUiEnvironment().addKeyStroke(textField, new P_ShiftMonthDownKeyStroke(), false);
getUiEnvironment().addKeyStroke(textField, new P_ShiftYearUpKeyStroke(), false);
getUiEnvironment().addKeyStroke(textField, new P_ShiftYearDownKeyStroke(), false);
// listener
dateChooserButton.addListener(ButtonEx.SELECTION_ACTION, new P_RwtBrowseButtonListener());
textField.addMouseListener(new MouseAdapter() {
private static final long serialVersionUID = 1L;
@Override
public void mouseUp(MouseEvent e) {
handleUiDateChooserAction();
}
});
//
setUiContainer(container);
setUiLabel(label);
setDropDownButton(dateChooserButton);
setUiField(textField);
// layout
container.setLayout(new LogicalGridLayout(1, 0));
m_dateContainer.setLayoutData(LogicalGridDataBuilder.createField(((IFormField) getScoutObject()).getGridData()));
m_dateContainer.setLayout(RwtLayoutUtility.createGridLayoutNoSpacing(2, false));
GridData textLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
textField.setLayoutData(textLayoutData);
GridData buttonLayoutData = new GridData(SWT.CENTER, SWT.CENTER, false, false);
buttonLayoutData.heightHint = 20;
buttonLayoutData.widthHint = 20;
dateChooserButton.setLayoutData(buttonLayoutData);
}
@Override
public Button getDropDownButton() {
return m_dropDownButton;
}
public void setDropDownButton(ButtonEx b) {
m_dropDownButton = b;
}
@Override
public StyledTextEx getUiField() {
return (StyledTextEx) super.getUiField();
}
public boolean isFocusInDatePicker() {
if (m_dateChooserDialog == null) {
return false;
}
Control focusControl = getUiEnvironment().getDisplay().getFocusControl();
boolean isFocusInDatePicker = RwtUtility.isAncestorOf(m_dateChooserDialog.getShell(), focusControl);
return isFocusInDatePicker;
}
private void installFocusListenerOnTextField() {
if (getUiField().isDisposed()) {
return;
}
getUiField().setFocus();
if (m_textFieldFocusAdapter == null) {
m_textFieldFocusAdapter = new FocusAdapter() {
private static final long serialVersionUID = 1L;
@Override
public void focusLost(FocusEvent e) {
handleUiFocusLostOnDatePickerPopup(e);
}
};
}
getUiField().addFocusListener(m_textFieldFocusAdapter);
}
/**
* The event is fired only if the datepicker popup is open.
* <p>
* The default sets the focus on the ui field if the new focus is inside the date picker. <br/>
* If the new focus is outside the date picker it makes sure the date picker popup will be closed.
* </p>
*/
protected void handleUiFocusLostOnDatePickerPopup(FocusEvent event) {
if (isFocusInDatePicker()) {
getUiEnvironment().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
getUiField().setFocus();
}
});
}
else {
getUiEnvironment().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
makeSureDateChooserIsClosed();
}
});
}
}
private void uninstallFocusListenerOnTextField() {
if (!getUiField().isDisposed() && m_textFieldFocusAdapter != null) {
getUiField().removeFocusListener(m_textFieldFocusAdapter);
}
}
@Override
protected void setEnabledFromScout(boolean b) {
super.setEnabledFromScout(b);
m_dropDownButton.setEnabled(b);
if (b) {
m_dateContainer.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_DATEFIELD);
}
else {
m_dateContainer.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_DATEFIELD_DISABLED);
}
}
@Override
protected void setLabelVisibleFromScout() {
if (!isIgnoreLabel()) {
super.setLabelVisibleFromScout();
}
}
@Override
protected void setFieldEnabled(Control rwtField, boolean enabled) {
if (m_editableSupport == null) {
m_editableSupport = new TextFieldEditableSupport(getUiField());
}
m_editableSupport.setEditable(enabled);
}
@Override
protected void setDisplayTextFromScout(String s) {
IDateField scoutField = getScoutObject();
if (s == null) {
s = "";
}
m_displayTextToVerify = s;
Date value = scoutField.getValue();
if (value != null) {
DateFormat format = scoutField.getIsolatedDateFormat();
if (format != null) {
m_displayTextToVerify = format.format(value);
}
}
getUiField().setText(m_displayTextToVerify);
getUiField().setCaretOffset(0);
}
@Override
protected void setBackgroundFromScout(String scoutColor) {
setBackgroundFromScout(scoutColor, m_dateContainer);
}
@Override
protected void handleUiInputVerifier(boolean doit) {
if (!doit) {
return;
}
final String text = getUiField().getText();
// only handle if text has changed
if (CompareUtility.equals(text, m_displayTextToVerify) && (isDateTimeCompositeMember() || getScoutObject().getErrorStatus() == null)) {
return;
}
m_displayTextToVerify = text;
final Holder<Boolean> result = new Holder<Boolean>(Boolean.class, false);
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
boolean b = getScoutObject().getUIFacade().setDateTextFromUI(text);
result.setValue(b);
}
};
JobEx job = getUiEnvironment().invokeScoutLater(t, 0);
try {
job.join(2345);
}
catch (InterruptedException e) {
//nop
}
getUiEnvironment().dispatchImmediateUiJobs();
// end notify
return;
}
@Override
protected void handleUiFocusGained() {
if (isSelectAllOnFocusEnabled()) {
getUiField().setSelection(0, getUiField().getText().length());
}
}
private void notifyPopupEventListeners(int eventType) {
IPopupSupportListener[] listeners;
synchronized (m_popupEventListenerLock) {
listeners = m_popupEventListeners.toArray(new IPopupSupportListener[m_popupEventListeners.size()]);
}
for (IPopupSupportListener listener : listeners) {
listener.handleEvent(eventType);
}
}
@Override
public void addPopupEventListener(IPopupSupportListener listener) {
synchronized (m_popupEventListenerLock) {
m_popupEventListeners.add(listener);
}
}
@Override
public void removePopupEventListener(IPopupSupportListener listener) {
synchronized (m_popupEventListenerLock) {
m_popupEventListeners.remove(listener);
}
}
protected void makeSureDateChooserIsClosed() {
if (m_dateChooserDialog != null
&& m_dateChooserDialog.getShell() != null
&& !m_dateChooserDialog.getShell().isDisposed()) {
m_dateChooserDialog.getShell().close();
}
uninstallFocusListenerOnTextField();
}
private void handleUiDateChooserAction() {
if (!getDropDownButton().isVisible() || !getDropDownButton().isEnabled()) {
return;
}
Date oldDate = getScoutObject().getValue();
if (oldDate == null) {
oldDate = new Date();
}
notifyPopupEventListeners(IPopupSupportListener.TYPE_OPENING);
makeSureDateChooserIsClosed();
m_dateChooserDialog = createDateChooserDialog(getUiField().getShell(), oldDate);
if (m_dateChooserDialog != null) {
m_disposeListener = new P_DateChooserDisposeListener();
m_dateChooserDialog.getShell().addDisposeListener(m_disposeListener);
m_dateChooserDialog.openDateChooser(getUiField());
installFocusListenerOnTextField();
}
}
protected DateChooserDialog createDateChooserDialog(Shell parentShell, Date currentDate) {
return new DateChooserDialog(parentShell, currentDate);
}
private void getDateFromClosedDateChooserDialog() {
Shell dateChooserShell = m_dateChooserDialog.getShell();
if (m_disposeListener != null) {
dateChooserShell.removeDisposeListener(m_disposeListener);
}
boolean setFocusToUiField = false;
try {
final Date newDate = m_dateChooserDialog.getReturnDate();
if (newDate != null) {
setFocusToUiField = true;
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().setDateFromUI(newDate);
}
};
getUiEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
finally {
notifyPopupEventListeners(IPopupSupportListener.TYPE_CLOSED);
uninstallFocusListenerOnTextField();
if (setFocusToUiField
&& !getUiField().isDisposed()) {
getUiField().setFocus();
}
}
}
private final class P_DateChooserDisposeListener implements DisposeListener {
private static final long serialVersionUID = 1L;
@Override
public void widgetDisposed(DisposeEvent event) {
getDateFromClosedDateChooserDialog();
}
}
private class P_RwtBrowseButtonListener implements Listener {
private static final long serialVersionUID = 1L;
public P_RwtBrowseButtonListener() {
}
@Override
public void handleEvent(Event event) {
switch (event.type) {
case ButtonEx.SELECTION_ACTION:
getUiField().forceFocus();
handleUiDateChooserAction();
break;
default:
break;
}
}
} // end class P_RwtBrowseButtonListener
private void shiftDate(final int level, final int value) {
if (getUiField().isDisposed()) {
return;
}
if (getUiField().isEnabled()
&& getUiField().getEditable()
&& getUiField().isVisible()) {
if (level >= 0) {
// notify Scout
Runnable t = new Runnable() {
@Override
public void run() {
getScoutObject().getUIFacade().fireDateShiftActionFromUI(level, value);
}
};
getUiEnvironment().invokeScoutLater(t, 0);
// end notify
}
}
}
private class P_ShiftDayUpKeyStroke extends RwtKeyStroke {
public P_ShiftDayUpKeyStroke() {
super(SWT.ARROW_UP);
}
@Override
public void handleUiAction(Event e) {
int level = 0;
int value = 1;
shiftDate(level, value);
}
}
private class P_ShiftDayDownKeyStroke extends RwtKeyStroke {
public P_ShiftDayDownKeyStroke() {
super(SWT.ARROW_DOWN);
}
@Override
public void handleUiAction(Event e) {
int level = 0;
int value = -1;
shiftDate(level, value);
}
}
private class P_ShiftMonthUpKeyStroke extends RwtKeyStroke {
public P_ShiftMonthUpKeyStroke() {
super(SWT.ARROW_UP, SWT.SHIFT);
}
@Override
public void handleUiAction(Event e) {
int level = 1;
int value = 1;
shiftDate(level, value);
}
}
private class P_ShiftMonthDownKeyStroke extends RwtKeyStroke {
public P_ShiftMonthDownKeyStroke() {
super(SWT.ARROW_DOWN, SWT.SHIFT);
}
@Override
public void handleUiAction(Event e) {
int level = 1;
int value = -1;
shiftDate(level, value);
}
}
private class P_ShiftYearUpKeyStroke extends RwtKeyStroke {
public P_ShiftYearUpKeyStroke() {
super(SWT.ARROW_UP, SWT.CONTROL);
}
@Override
public void handleUiAction(Event e) {
int level = 2;
int value = 1;
shiftDate(level, value);
}
}
private class P_ShiftYearDownKeyStroke extends RwtKeyStroke {
public P_ShiftYearDownKeyStroke() {
super(SWT.ARROW_DOWN, SWT.CONTROL);
}
@Override
public void handleUiAction(Event e) {
int level = 2;
int value = -1;
shiftDate(level, value);
}
}
private class P_DateChooserOpenKeyStroke extends RwtKeyStroke {
public P_DateChooserOpenKeyStroke() {
super(SWT.F2);
}
@Override
public void handleUiAction(Event e) {
handleUiDateChooserAction();
}
}
}