Skip to content

Content of file DateTimeControlSWTRenderer.java

/*******************************************************************************
 * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * Eugen - initial API and implementation
 * Christian W. Damus - bug 527686
 ******************************************************************************/
package org.eclipse.emf.ecp.view.internal.core.swt.renderer;

import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import javax.inject.Inject;

import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.IObserving;
import org.eclipse.core.databinding.observable.value.DateAndTimeObservableValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecp.edit.spi.ViewLocaleService;
import org.eclipse.emf.ecp.view.internal.core.swt.MessageKeys; import org.eclipse.emf.ecp.view.spi.context.ViewModelContext; import org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTControlSWTRenderer; import org.eclipse.emf.ecp.view.spi.model.DateTimeDisplayType; import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener; import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification; import org.eclipse.emf.ecp.view.spi.model.VAttachment; import org.eclipse.emf.ecp.view.spi.model.VControl; import org.eclipse.emf.ecp.view.spi.model.VDateTimeDisplayAttachment; import org.eclipse.emf.ecp.view.spi.util.swt.ImageRegistryService; import org.eclipse.emf.ecp.view.template.model.VTViewTemplateProvider; import org.eclipse.emf.edit.command.SetCommand; import org.eclipse.emfforms.spi.common.report.AbstractReport; import org.eclipse.emfforms.spi.common.report.ReportService; import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException; import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedReport; import org.eclipse.emfforms.spi.core.services.databinding.EMFFormsDatabinding; import org.eclipse.emfforms.spi.core.services.label.EMFFormsLabelProvider; import org.eclipse.emfforms.spi.localization.EMFFormsLocalizationService; import org.eclipse.emfforms.spi.swt.core.SWTDataElementIdHelper; import org.eclipse.jface.databinding.swt.ISWTObservableValue; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.dialogs.IDialogLabelKeys; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DateTime; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.osgi.framework.FrameworkUtil; /** * A control which can handle {@link java.util.Date Date}. * * @author Eugen Neufeld * */ public class DateTimeControlSWTRenderer extends SimpleControlSWTControlSWTRenderer { /** * ModelToTarget Strategy that handles also null values of dates. * * @author Eugen Neufeld * */ private class DateModelToTargetUpdateStrategy extends UpdateValueStrategy { DateModelToTargetUpdateStrategy() { super(); } DateModelToTargetUpdateStrategy(int policy) { super(policy); } @Override protected IStatus doSet(IObservableValue observableValue, Object value) { if (value == null) { return Status.OK_STATUS; } return super.doSet(observableValue, value); } } private final EMFFormsLocalizationService localizationService; private final ImageRegistryService imageRegistryService; /** * Default constructor. * * @param vElement the view model element to be rendered * @param viewContext the view context * @param reportService The {@link ReportService} * @param emfFormsDatabinding The {@link EMFFormsDatabinding} * @param emfFormsLabelProvider The {@link EMFFormsLabelProvider} * @param vtViewTemplateProvider The {@link VTViewTemplateProvider} * @param localizationService The {@link EMFFormsLocalizationService} * @param imageRegistryService The {@link ImageRegistryService} */ @Inject public DateTimeControlSWTRenderer(VControl vElement, ViewModelContext viewContext, ReportService reportService, EMFFormsDatabinding emfFormsDatabinding, EMFFormsLabelProvider emfFormsLabelProvider, VTViewTemplateProvider vtViewTemplateProvider, EMFFormsLocalizationService localizationService, ImageRegistryService imageRegistryService) { super(vElement, viewContext, reportService, emfFormsDatabinding, emfFormsLabelProvider, vtViewTemplateProvider); this.localizationService = localizationService; this.imageRegistryService = imageRegistryService; } private Label unsetLabel; private StackLayout stackLayout; private Composite stackComposite, dateTimeComposite; private Composite composite; private Shell dialog; private ModelChangeListener domainModelChangeListener; private DateTime dateWidget; private DateTime timeWidget; private Button bUnset; private Button setBtn; @Override protected Binding[] createBindings(Control control) throws DatabindingFailedException { final ISWTObservableValue dateObserver = WidgetProperties.selection().observe(dateWidget); final ISWTObservableValue timeObserver = WidgetProperties.selection().observe(timeWidget); final IObservableValue target = new DateAndTimeObservableValue(dateObserver, timeObserver); final Binding binding = getDataBindingContext().bindValue(target, getModelValue(), withPreSetValidation(new UpdateValueStrategy()), new DateModelToTargetUpdateStrategy()); if (domainModelChangeListener == null) { domainModelChangeListener = new ModelChangeListener() { @Override public void notifyChange(ModelChangeNotification notification) { EStructuralFeature structuralFeature; try { structuralFeature = (EStructuralFeature) getModelValue().getValueType(); } catch (final DatabindingFailedException ex) { getReportService().report(new DatabindingFailedReport(ex)); return; } if (structuralFeature.equals(notification.getStructuralFeature())) { updateChangeListener(notification.getRawNotification().getNewValue()); } } }; getViewModelContext().registerDomainChangeListener(domainModelChangeListener); } return new Binding[] { binding }; } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#dispose() */ @Override protected void dispose() { if (dialog != null && !dialog.isDisposed()) { dialog.dispose(); } getViewModelContext().unregisterDomainChangeListener(domainModelChangeListener); super.dispose(); } @Override protected Control createSWTControl(Composite parent) throws DatabindingFailedException { composite = new Composite(parent, SWT.NONE); composite.setBackground(parent.getBackground()); GridLayoutFactory.fillDefaults().numColumns(2).spacing(2, 0).equalWidth(false) .applyTo(composite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.BEGINNING).applyTo(composite); stackComposite = new Composite(composite, SWT.NONE); stackComposite.setBackground(parent.getBackground()); GridLayoutFactory.fillDefaults().numColumns(1).spacing(2, 0).equalWidth(false) .applyTo(stackComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).applyTo(stackComposite); dateTimeComposite = new Composite(stackComposite, SWT.NONE); dateTimeComposite.setBackground(composite.getBackground()); GridLayoutFactory.fillDefaults().numColumns(3).spacing(2, 0).equalWidth(false) .applyTo(dateTimeComposite); GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).applyTo(dateTimeComposite); stackLayout = new StackLayout(); stackComposite.setLayout(stackLayout); unsetLabel = new Label(stackComposite, SWT.CENTER); SWTDataElementIdHelper.setElementIdDataWithSubId(unsetLabel, getVElement(), "unsetlabel", //$NON-NLS-1$ getViewModelContext()); unsetLabel.setText(getUnsetText()); unsetLabel.setBackground(stackComposite.getBackground()); unsetLabel.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); unsetLabel.setAlignment(SWT.CENTER); final DateTimeDisplayType dateTimeDisplayType = getDateTimeDisplayType(); createDateTimeWidgets(dateTimeDisplayType); bUnset = new Button(dateTimeComposite, SWT.PUSH); SWTDataElementIdHelper.setElementIdDataWithSubId(bUnset, getVElement(), "unset", getViewModelContext()); //$NON-NLS-1$ GridDataFactory.fillDefaults().grab(false, false).align(SWT.CENTER, SWT.CENTER).applyTo(bUnset); bUnset .setImage(imageRegistryService.getImage(FrameworkUtil.getBundle(DateTimeControlSWTRenderer.class), "icons/unset_feature.png")); //$NON-NLS-1$ bUnset.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_dateTime_buttonUnset"); //$NON-NLS-1$ final String tooltip = getDateTimeDisplayType() == DateTimeDisplayType.TIME_ONLY ? MessageKeys.DateTimeControlSWTRenderer_CleanTime : MessageKeys.DateTimeControlSWTRenderer_CleanDate; bUnset.setToolTipText(getLocalizedString(tooltip)); bUnset.addSelectionListener(new UnsetBtnSelectionAdapterExtension()); setBtn = createSetButton(); updateStack(); return composite; } @Override protected void applyReadOnly() { super.applyReadOnly(); // specific handling for buttons updateButtonVisibility(); } @Override protected void applyEnable() { super.applyEnable(); // specific handling for buttons updateButtonEnabling(); } /** * Updates the enablement of buttons according to the bound input. */ protected void updateButtonEnabling() { final boolean isEnable = getVElement().isEffectivelyEnabled() && !getVElement().isEffectivelyReadonly(); if (bUnset != null) { bUnset.setEnabled(isEnable); } if (setBtn != null) { setBtn.setEnabled(isEnable); } } /** * Updates the visibility of buttons according to the bound input. */ protected void updateButtonVisibility() { final boolean isVisible = !getVElement().isEffectivelyReadonly() && !isUnchangeableFeature(); if (bUnset != null) { bUnset.setVisible(isVisible); GridData.class.cast(bUnset.getLayoutData()).exclude = !isVisible; } if (setBtn != null) { try { if (getModelValue() != null) { final Object value = getModelValue().getValue(); if (getDateTimeDisplayType() == DateTimeDisplayType.TIME_ONLY) { setBtn.setVisible(isVisible && value == null); } else { setBtn.setVisible(isVisible); } GridData.class.cast(setBtn.getLayoutData()).exclude = !setBtn.isVisible(); } } catch (final DatabindingFailedException ex) { getReportService().report(new DatabindingFailedReport(ex)); } } // Null check to avoid NPE during postInit if (composite != null) { composite.layout(); } } private void updateStack() throws DatabindingFailedException { final IObservableValue observableValue = getEMFFormsDatabinding() .getObservableValue(getVElement().getDomainModelReference(), getViewModelContext().getDomainModel()); final EStructuralFeature structuralFeature = (EStructuralFeature) observableValue.getValueType(); final EObject eObject = (EObject) ((IObserving) observableValue).getObserved(); observableValue.dispose(); if (eObject.eIsSet(structuralFeature)) { stackLayout.topControl = dateTimeComposite; } else { stackLayout.topControl = unsetLabel; } } private Button createSetButton() { final String imagePath = getDateTimeDisplayType() == DateTimeDisplayType.TIME_ONLY ? "icons/set_feature.png" //$NON-NLS-1$ : "icons/date.png"; //$NON-NLS-1$ final Button setBtn = new Button(composite, SWT.PUSH); SWTDataElementIdHelper.setElementIdDataWithSubId(setBtn, getVElement(), "set", getViewModelContext()); //$NON-NLS-1$ GridDataFactory.fillDefaults().grab(false, false).align(SWT.CENTER, SWT.CENTER).applyTo(setBtn); setBtn.setImage( imageRegistryService.getImage(FrameworkUtil.getBundle(DateTimeControlSWTRenderer.class), imagePath)); setBtn.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_dateTime_buttonSet"); //$NON-NLS-1$ final String tooltip = getDateTimeDisplayType() == DateTimeDisplayType.TIME_ONLY ? MessageKeys.DateTimeControlSWTRenderer_SelectTime : MessageKeys.DateTimeControlSWTRenderer_SelectData; setBtn.setToolTipText(getLocalizedString(tooltip)); setBtn.addSelectionListener(new SetBtnSelectionAdapterExtension(setBtn)); return setBtn; } private void createDateTimeWidgets(DateTimeDisplayType dateTimeDisplayType) { dateWidget = new DateTime(dateTimeComposite, SWT.DATE | SWT.BORDER); SWTDataElementIdHelper.setElementIdDataWithSubId(dateWidget, getVElement(), "date", getViewModelContext()); //$NON-NLS-1$ dateWidget.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); dateWidget.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_dateTime_date"); //$NON-NLS-1$ timeWidget = new DateTime(dateTimeComposite, SWT.TIME | SWT.SHORT | SWT.BORDER); SWTDataElementIdHelper.setElementIdDataWithSubId(timeWidget, getVElement(), "time", getViewModelContext()); //$NON-NLS-1$ timeWidget.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); timeWidget.setData(CUSTOM_VARIANT, "org_eclipse_emf_ecp_control_dateTime_time"); //$NON-NLS-1$ if (dateTimeDisplayType == DateTimeDisplayType.TIME_ONLY) { dateWidget.setVisible(false); final GridData gridData = (GridData) dateWidget.getLayoutData(); gridData.exclude = true; } if (dateTimeDisplayType == DateTimeDisplayType.DATE_ONLY) { timeWidget.setVisible(false); final GridData gridData = (GridData) timeWidget.getLayoutData(); gridData.exclude = true; } } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTControlSWTRenderer#setValidationColor(org.eclipse.swt.widgets.Control, * org.eclipse.swt.graphics.Color) */ @Override protected void setValidationColor(Control control, Color validationColor) { ((Composite) control).getChildren()[0].setBackground(validationColor); ((Composite) control).getChildren()[1].setBackground(validationColor); } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTRenderer#getUnsetText() */ @Override protected String getUnsetText() { final String text = getDateTimeDisplayType() == DateTimeDisplayType.TIME_ONLY ? MessageKeys.DateTimeControl_NoTimeSetClickToSetTime : MessageKeys.DateTimeControl_NoDateSetClickToSetDate; return getLocalizedString(text); } private String getLocalizedString(String key) { return localizationService.getString(getClass(), key); } /** * Set button adapter. */ private class SetBtnSelectionAdapterExtension extends SelectionAdapter { private final Button btn; /** * @param btn * @param modelValue * @param viewModelContext */ SetBtnSelectionAdapterExtension(Button btn) { this.btn = btn; } @Override public void widgetSelected(SelectionEvent e) { if (getDateTimeDisplayType() == DateTimeDisplayType.TIME_ONLY) { setTime(); } else { setDate(); } } private void setDate() { if (dialog != null && !dialog.isDisposed()) { dialog.dispose(); return; } final IObservableValue modelValue; try { modelValue = getModelValue(); } catch (final DatabindingFailedException ex) { getReportService().report(new AbstractReport(ex)); return; } dialog = new Shell(btn.getShell(), SWT.NONE); dialog.setLayout(new GridLayout(1, false)); final DateTime calendar = new DateTime(dialog, SWT.CALENDAR | SWT.BORDER); final IObservableValue calendarObserver = WidgetProperties.selection().observe(calendar); final UpdateValueStrategy modelToTarget = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE); final Binding binding = getDataBindingContext().bindValue(calendarObserver, modelValue, modelToTarget, new DateModelToTargetUpdateStrategy(UpdateValueStrategy.POLICY_UPDATE)); final Calendar defaultCalendar = Calendar.getInstance(getLocale(getViewModelContext())); final Date date = (Date) modelValue.getValue(); if (date != null) { defaultCalendar.setTime(date); } calendar.setDate(defaultCalendar.get(Calendar.YEAR), defaultCalendar.get(Calendar.MONTH), defaultCalendar.get(Calendar.DAY_OF_MONTH)); final Button okButton = new Button(dialog, SWT.PUSH); okButton.setText(JFaceResources.getString(IDialogLabelKeys.OK_LABEL_KEY)); GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).grab(false, false).applyTo(okButton); okButton.addSelectionListener(new SelectionAdapter() { /** * {@inheritDoc} * * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { binding.updateTargetToModel(); binding.dispose(); dialog.close(); updateChangeListener(modelValue.getValue()); } }); dialog.pack(); dialog.layout(); dialog.setLocation(btn.getParent().toDisplay( btn.getLocation().x + btn.getSize().x - dialog.getSize().x, btn.getLocation().y + btn.getSize().y)); dialog.open(); } private void setTime() { try { final EStructuralFeature structuralFeature = (EStructuralFeature) getModelValue().getValueType(); final EObject eObject = (EObject) ((IObserving) getModelValue()).getObserved(); final Command setCommand = SetCommand.create(getEditingDomain(eObject), eObject, structuralFeature, new Date()); getEditingDomain(eObject).getCommandStack().execute(setCommand); updateChangeListener(getModelValue().getValue()); } catch (final DatabindingFailedException ex) { getReportService().report(new DatabindingFailedReport(ex)); } } } /** * Unset button adapter. */ private class UnsetBtnSelectionAdapterExtension extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { try { final EStructuralFeature structuralFeature = (EStructuralFeature) getModelValue().getValueType(); final EObject eObject = (EObject) ((IObserving) getModelValue()).getObserved(); final Command removeCommand = SetCommand.create(getEditingDomain(eObject), eObject, structuralFeature, null); getEditingDomain(eObject).getCommandStack().execute(removeCommand); updateChangeListener(getModelValue().getValue()); } catch (final DatabindingFailedException ex) { getReportService().report(new DatabindingFailedReport(ex)); // Do nothing. This should not happen because if getModelValue() fails, the control will never be // rendered and consequently this code will never be executed. } } } private Locale getLocale(ViewModelContext viewModelContext) { final ViewLocaleService service = viewModelContext.getService(ViewLocaleService.class); if (service == null) { return Locale.getDefault(); } return service.getLocale(); } private void updateChangeListener(Object value) { if (value == null) { if (stackLayout.topControl != unsetLabel) { stackLayout.topControl = unsetLabel; stackComposite.layout(); } } else { if (stackLayout.topControl != dateTimeComposite) { stackLayout.topControl = dateTimeComposite; stackComposite.layout(); } } updateButtonVisibility(); if (!ignoreEnableOnReadOnly()) { applyEnable(); } } private DateTimeDisplayType getDateTimeDisplayType() { if (getVElement() != null && getVElement().getAttachments() != null) { for (final VAttachment attachment : getVElement().getAttachments()) { if (VDateTimeDisplayAttachment.class.isInstance(attachment)) { final VDateTimeDisplayAttachment dateTimeAttachment = (VDateTimeDisplayAttachment) attachment; return dateTimeAttachment.getDisplayType(); } } } return DateTimeDisplayType.TIME_AND_DATE; } /** * {@inheritDoc} * * @see org.eclipse.emf.ecp.view.spi.core.swt.SimpleControlSWTControlSWTRenderer#rootDomainModelChanged() */ @Override protected void rootDomainModelChanged() throws DatabindingFailedException { super.rootDomainModelChanged(); updateStack(); stackComposite.layout(); } }