package name.abuchen.portfolio.ui.dialogs.transactions; import java.text.MessageFormat; import java.util.List; import javax.annotation.PostConstruct; import org.eclipse.core.databinding.AggregateValidationStatus; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.BeanProperties; import org.eclipse.core.databinding.beans.PojoProperties; import org.eclipse.core.databinding.conversion.NumberToStringConverter; import org.eclipse.core.databinding.conversion.StringToNumberConverter; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.validation.MultiValidator; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.databinding.swt.WidgetProperties; import org.eclipse.jface.databinding.viewers.ViewersObservables; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import com.ibm.icu.text.DecimalFormat; import com.ibm.icu.text.NumberFormat; import name.abuchen.portfolio.model.Account; import name.abuchen.portfolio.model.Portfolio; import name.abuchen.portfolio.model.Security; import name.abuchen.portfolio.money.Values; import name.abuchen.portfolio.ui.Images; import name.abuchen.portfolio.ui.Messages; import name.abuchen.portfolio.ui.util.CurrencyToStringConverter; import name.abuchen.portfolio.ui.util.IValidatingConverter; import name.abuchen.portfolio.ui.util.StringToCurrencyConverter; public abstract class AbstractTransactionDialog extends TitleAreaDialog { public class Input { public final Label label; public final Text value; public final Label currency; public Input(Composite editArea, String text) { label = new Label(editArea, SWT.LEFT); label.setText(text); value = new Text(editArea, SWT.BORDER | SWT.RIGHT); value.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { value.selectAll(); } }); currency = new Label(editArea, SWT.NONE); } public void bindValue(String property, String description, Values<?> values, boolean isMandatory) { StringToCurrencyConverter converter = new StringToCurrencyConverter(values); UpdateValueStrategy strategy = new UpdateValueStrategy(); strategy.setAfterGetValidator(converter); strategy.setConverter(converter); if (isMandatory) { strategy.setAfterConvertValidator(convertedValue -> { Long v = (Long) convertedValue; return v != null && v.longValue() > 0 ? ValidationStatus.ok() : ValidationStatus.error( MessageFormat.format(Messages.MsgDialogInputRequired, description)); }); } context.bindValue(WidgetProperties.text(SWT.Modify).observe(value), // BeanProperties.value(property).observe(model), // strategy, new UpdateValueStrategy().setConverter(new CurrencyToStringConverter(values))); } public void bindCurrency(String property) { context.bindValue(WidgetProperties.text().observe(currency), // BeanProperties.value(property).observe(model)); } public void bindBigDecimal(String property, String pattern) { NumberFormat format = new DecimalFormat(pattern); IValidatingConverter converter = IValidatingConverter.wrap(StringToNumberConverter.toBigDecimal()); context.bindValue(WidgetProperties.text(SWT.Modify).observe(value), // BeanProperties.value(property).observe(model), // new UpdateValueStrategy().setAfterGetValidator(converter).setConverter(converter), new UpdateValueStrategy().setConverter(NumberToStringConverter.fromBigDecimal(format))); } public void setVisible(boolean visible) { label.setVisible(visible); value.setVisible(visible); currency.setVisible(visible); } } public class ComboInput { public final Label label; public final ComboViewer value; public final Label currency; public ComboInput(Composite editArea, String text) { if (text != null) { label = new Label(editArea, SWT.RIGHT); label.setText(text); } else { label = null; } value = new ComboViewer(editArea); value.setContentProvider(new ArrayContentProvider()); currency = new Label(editArea, SWT.NONE); } public IObservableValue bindValue(String property, String missingValueMessage) { UpdateValueStrategy strategy = new UpdateValueStrategy(); strategy.setAfterConvertValidator( v -> v != null ? ValidationStatus.ok() : ValidationStatus.error(missingValueMessage)); IObservableValue observable = ViewersObservables.observeSingleSelection(value); context.bindValue(observable, BeanProperties.value(property).observe(model), strategy, null); return observable; } public void bindCurrency(String property) { context.bindValue(WidgetProperties.text().observe(currency), BeanProperties.value(property).observe(model)); } } class ModelStatusListener { public void setStatus(IStatus status) { setErrorMessage(status.getSeverity() == IStatus.OK ? null : status.getMessage()); for (int buttonId : new int[] { IDialogConstants.OK_ID, SAVE_AND_NEW_ID }) { Control button = getButton(buttonId); if (button != null) button.setEnabled(status.getSeverity() == IStatus.OK); } } public IStatus getStatus() { // irrelevant return ValidationStatus.ok(); } } public static final int SAVE_AND_NEW_ID = 4711; protected AbstractModel model; protected DataBindingContext context = new DataBindingContext(); protected ModelStatusListener status = new ModelStatusListener(); public AbstractTransactionDialog(Shell parentShell) { super(parentShell); setTitleImage(Images.BANNER.image()); } protected void setModel(AbstractModel model) { this.model = model; } @PostConstruct public void registerValidationStatusListener() { this.context.addValidationStatusProvider(new MultiValidator() { IObservableValue observable = BeanProperties.value("calculationStatus").observe(model); //$NON-NLS-1$ @Override protected IStatus validate() { return (IStatus) observable.getValue(); } }); } @Override public void create() { super.create(); setTitle(model.getHeading()); setMessage(""); //$NON-NLS-1$ setShellStyle(getShellStyle() | SWT.RESIZE); status.setStatus(AggregateValidationStatus.getStatusMaxSeverity(context.getValidationStatusProviders())); } @Override protected int getShellStyle() { return super.getShellStyle() | SWT.RESIZE; } @Override protected void createButtonsForButtonBar(Composite parent) { createButton(parent, IDialogConstants.OK_ID, Messages.LabelSave, true); createButton(parent, SAVE_AND_NEW_ID, Messages.LabelSaveAndNew, false); createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); } @Override protected Control createDialogArea(Composite parent) { Composite area = (Composite) super.createDialogArea(parent); Composite editArea = new Composite(area, SWT.NONE); FormLayout layout = new FormLayout(); layout.marginWidth = 5; layout.marginHeight = 5; editArea.setLayout(layout); createFormElements(editArea); context.bindValue(PojoProperties.value("status").observe(status), //$NON-NLS-1$ new AggregateValidationStatus(context, AggregateValidationStatus.MAX_SEVERITY)); return editArea; } protected abstract void createFormElements(Composite editArea); @Override protected void okPressed() { model.applyChanges(); super.okPressed(); } @Override protected void buttonPressed(int buttonId) { if (buttonId == SAVE_AND_NEW_ID) { model.applyChanges(); model.resetToNewTransaction(); getDialogArea().setFocus(); } else { super.buttonPressed(buttonId); } } public void setAccount(Account account) {} public void setPortfolio(Portfolio portfolio) {} public void setSecurity(Security security) {} /** * make sure drop-down boxes contain the security, portfolio and account of * this transaction (they might be "retired" and do not show by default) */ protected <T> List<T> including(List<T> list, T element) { if (element != null && !list.contains(element)) list.add(0, element); return list; } }