| /******************************************************************************* |
| * 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.TimeChooserDialog; |
| 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 removel |
| |
| public class RwtScoutTimeField extends RwtScoutValueFieldComposite<IDateField> implements IRwtScoutTimeField, 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_timeContainer; |
| private boolean m_dateTimeCompositeMember; |
| private String m_displayTextToVerify; |
| private TimeChooserDialog m_timeChooserDialog = null; |
| private FocusAdapter m_textFieldFocusAdapter = null; |
| private P_TimeChooserDisposeListener 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_timeContainer = getUiEnvironment().getFormToolkit().createComposite(container, SWT.BORDER); |
| m_timeContainer.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_TIMEFIELD); |
| |
| StyledText textField = new StyledTextEx(m_timeContainer, SWT.SINGLE); |
| getUiEnvironment().getFormToolkit().adapt(textField, false, false); |
| textField.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_TIMEFIELD); |
| |
| ButtonEx timeChooserButton = getUiEnvironment().getFormToolkit().createButtonEx(m_timeContainer, SWT.PUSH | SWT.NO_FOCUS); |
| timeChooserButton.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_TIMEFIELD); |
| |
| m_timeContainer.setTabList(new Control[]{textField}); |
| container.setTabList(new Control[]{m_timeContainer}); |
| |
| // key strokes on container |
| getUiEnvironment().addKeyStroke(container, new P_TimeChooserOpenKeyStroke(), false); |
| |
| // key strokes on field |
| getUiEnvironment().addKeyStroke(textField, new P_ShiftNextQuarterHourKeyStroke(), false); |
| getUiEnvironment().addKeyStroke(textField, new P_ShiftPreviousQuarterHourKeyStroke(), false); |
| getUiEnvironment().addKeyStroke(textField, new P_ShiftNextHourKeyStroke(), false); |
| getUiEnvironment().addKeyStroke(textField, new P_ShiftPreviousHourKeyStroke(), false); |
| |
| // listener |
| timeChooserButton.addListener(ButtonEx.SELECTION_ACTION, new P_RwtBrowseButtonListener()); |
| textField.addMouseListener(new MouseAdapter() { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void mouseUp(MouseEvent e) { |
| handleUiTimeChooserAction(); |
| } |
| }); |
| // |
| setUiContainer(container); |
| setUiLabel(label); |
| setDropDownButton(timeChooserButton); |
| setUiField(textField); |
| |
| // layout |
| container.setLayout(new LogicalGridLayout(1, 0)); |
| |
| m_timeContainer.setLayoutData(LogicalGridDataBuilder.createField(((IFormField) getScoutObject()).getGridData())); |
| m_timeContainer.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; |
| timeChooserButton.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 isFocusInTimePicker() { |
| Control focusControl = getUiEnvironment().getDisplay().getFocusControl(); |
| boolean isFocusInDatePicker = RwtUtility.isAncestorOf(m_timeChooserDialog.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 time picker popup is open. |
| * <p> |
| * The default sets the focus on the ui field if the new focus is inside the time picker. <br/> |
| * If the new focus is outside the time picker it makes sure the time picker popup will be closed. |
| * </p> |
| */ |
| protected void handleUiFocusLostOnDatePickerPopup(FocusEvent event) { |
| if (isFocusInTimePicker()) { |
| getUiEnvironment().getDisplay().asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| getUiField().setFocus(); |
| } |
| |
| }); |
| } |
| else { |
| getUiEnvironment().getDisplay().asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| makeSureTimeChooserIsClosed(); |
| } |
| |
| }); |
| } |
| } |
| |
| 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); |
| getUiField().setEnabled(b); |
| if (b) { |
| m_timeContainer.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_TIMEFIELD); |
| } |
| else { |
| m_timeContainer.setData(WidgetUtil.CUSTOM_VARIANT, VARIANT_TIMEFIELD_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.getIsolatedTimeFormat(); |
| if (format != null) { |
| m_displayTextToVerify = format.format(value); |
| } |
| } |
| getUiField().setText(m_displayTextToVerify); |
| getUiField().setCaretOffset(0); |
| } |
| |
| @Override |
| protected void setBackgroundFromScout(String scoutColor) { |
| setBackgroundFromScout(scoutColor, m_timeContainer); |
| } |
| |
| @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().setTimeTextFromUI(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()); |
| } |
| } |
| |
| protected void makeSureTimeChooserIsClosed() { |
| if (m_timeChooserDialog != null |
| && m_timeChooserDialog.getShell() != null |
| && !m_timeChooserDialog.getShell().isDisposed()) { |
| m_timeChooserDialog.getShell().close(); |
| } |
| |
| uninstallFocusListenerOnTextField(); |
| } |
| |
| private void handleUiTimeChooserAction() { |
| if (!getDropDownButton().isVisible() || !getDropDownButton().isEnabled()) { |
| return; |
| } |
| |
| Date oldTime = getScoutObject().getValue(); |
| if (oldTime == null) { |
| oldTime = new Date(); |
| } |
| notifyPopupEventListeners(IPopupSupportListener.TYPE_OPENING); |
| |
| makeSureTimeChooserIsClosed(); |
| m_timeChooserDialog = createTimeChooserDialog(getUiField().getShell(), oldTime); |
| if (m_timeChooserDialog != null) { |
| m_disposeListener = new P_TimeChooserDisposeListener(); |
| m_timeChooserDialog.getShell().addDisposeListener(m_disposeListener); |
| |
| m_timeChooserDialog.openTimeChooser(getUiField()); |
| installFocusListenerOnTextField(); |
| } |
| } |
| |
| protected TimeChooserDialog createTimeChooserDialog(Shell parentShell, Date currentTime) { |
| return new TimeChooserDialog(parentShell, currentTime); |
| } |
| |
| private void getTimeFromClosedDateChooserDialog() { |
| if (m_disposeListener != null) { |
| m_timeChooserDialog.getShell().removeDisposeListener(m_disposeListener); |
| } |
| boolean setFocusToUiField = false; |
| try { |
| final Date newDate = m_timeChooserDialog.getReturnTime(); |
| if (newDate != null) { |
| setFocusToUiField = true; |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| getScoutObject().getUIFacade().setTimeFromUI(newDate); |
| } |
| }; |
| getUiEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| finally { |
| notifyPopupEventListeners(IPopupSupportListener.TYPE_CLOSED); |
| uninstallFocusListenerOnTextField(); |
| if (setFocusToUiField |
| && !getUiField().isDisposed()) { |
| getUiField().setFocus(); |
| } |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| /** |
| * |
| */ |
| private final class P_TimeChooserDisposeListener implements DisposeListener { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void widgetDisposed(DisposeEvent event) { |
| getTimeFromClosedDateChooserDialog(); |
| } |
| } |
| |
| 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(); |
| handleUiTimeChooserAction(); |
| break; |
| default: |
| break; |
| } |
| } |
| } // end class P_RwtBrowseButtonListener |
| |
| private void shiftTime(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().fireTimeShiftActionFromUI(level, value); |
| } |
| }; |
| getUiEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| } |
| |
| private class P_ShiftNextQuarterHourKeyStroke extends RwtKeyStroke { |
| public P_ShiftNextQuarterHourKeyStroke() { |
| super(SWT.ARROW_UP); |
| } |
| |
| @Override |
| public void handleUiAction(Event e) { |
| int level = 0; |
| int value = 1; |
| shiftTime(level, value); |
| } |
| } |
| |
| private class P_ShiftPreviousQuarterHourKeyStroke extends RwtKeyStroke { |
| public P_ShiftPreviousQuarterHourKeyStroke() { |
| super(SWT.ARROW_DOWN); |
| } |
| |
| @Override |
| public void handleUiAction(Event e) { |
| int level = 0; |
| int value = -1; |
| shiftTime(level, value); |
| } |
| } |
| |
| private class P_ShiftNextHourKeyStroke extends RwtKeyStroke { |
| public P_ShiftNextHourKeyStroke() { |
| super(SWT.ARROW_UP, SWT.SHIFT); |
| } |
| |
| @Override |
| public void handleUiAction(Event e) { |
| int level = 1; |
| int value = 1; |
| shiftTime(level, value); |
| } |
| } |
| |
| private class P_ShiftPreviousHourKeyStroke extends RwtKeyStroke { |
| public P_ShiftPreviousHourKeyStroke() { |
| super(SWT.ARROW_DOWN, SWT.SHIFT); |
| } |
| |
| @Override |
| public void handleUiAction(Event e) { |
| int level = 1; |
| int value = -1; |
| shiftTime(level, value); |
| } |
| } |
| |
| private class P_TimeChooserOpenKeyStroke extends RwtKeyStroke { |
| public P_TimeChooserOpenKeyStroke() { |
| super(SWT.F2); |
| } |
| |
| @Override |
| public void handleUiAction(Event e) { |
| handleUiTimeChooserAction(); |
| } |
| } |
| |
| } |