package name.abuchen.portfolio.ui.dialogs.transactions;
import static name.abuchen.portfolio.ui.util.FormDataFactory.startingWith;
import static name.abuchen.portfolio.ui.util.SWTHelper.amountWidth;
import static name.abuchen.portfolio.ui.util.SWTHelper.currencyWidth;
import static name.abuchen.portfolio.ui.util.SWTHelper.widest;
import java.time.LocalDate;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.core.databinding.beans.BeanProperties;
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.e4.core.di.extensions.Preference;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.AccountTransferEntry;
import name.abuchen.portfolio.model.Client;
import name.abuchen.portfolio.money.ExchangeRateProviderFactory;
import name.abuchen.portfolio.money.Values;
import name.abuchen.portfolio.ui.Messages;
import name.abuchen.portfolio.ui.UIConstants;
import name.abuchen.portfolio.ui.dialogs.transactions.AccountTransferModel.Properties;
import name.abuchen.portfolio.ui.util.DateTimePicker;
import name.abuchen.portfolio.ui.util.SimpleDateTimeSelectionProperty;
@SuppressWarnings("restriction")
public class AccountTransferDialog extends AbstractTransactionDialog // NOSONAR
{
private final class AccountsMustBeDifferentValidator extends MultiValidator
{
IObservableValue source;
IObservableValue target;
public AccountsMustBeDifferentValidator(IObservableValue source, IObservableValue target)
{
this.source = source;
this.target = target;
}
@Override
protected IStatus validate()
{
Object from = source.getValue();
Object to = target.getValue();
return from != null && to != null && from != to ? ValidationStatus.ok()
: ValidationStatus.error(Messages.MsgAccountMustBeDifferent);
}
}
@Inject
private Client client;
@Preference(value = UIConstants.Preferences.USE_INDIRECT_QUOTATION)
@Inject
private boolean useIndirectQuotation = false;
@Inject
public AccountTransferDialog(@Named(IServiceConstants.ACTIVE_SHELL) Shell parentShell)
{
super(parentShell);
}
@PostConstruct
private void createModel(ExchangeRateProviderFactory factory) // NOSONAR
{
AccountTransferModel m = new AccountTransferModel(client);
m.setExchangeRateProviderFactory(factory);
setModel(m);
}
private AccountTransferModel model()
{
return (AccountTransferModel) this.model;
}
@Override
protected void createFormElements(Composite editArea)
{
//
// input elements
//
// source account
ComboInput source = new ComboInput(editArea, Messages.ColumnAccountFrom);
source.value.setInput(including(client.getActiveAccounts(), model().getSourceAccount()));
IObservableValue sourceObservable = source.bindValue(Properties.sourceAccount.name(),
Messages.MsgAccountFromMissing);
source.bindCurrency(Properties.sourceAccountCurrency.name());
// target account
ComboInput target = new ComboInput(editArea, Messages.ColumnAccountTo);
target.value.setInput(including(client.getActiveAccounts(), model().getTargetAccount()));
IObservableValue targetObservable = target.bindValue(Properties.targetAccount.name(),
Messages.MsgAccountToMissing);
target.bindCurrency(Properties.targetAccountCurrency.name());
MultiValidator validator = new AccountsMustBeDifferentValidator(sourceObservable, targetObservable);
context.addValidationStatusProvider(validator);
// date
Label lblDate = new Label(editArea, SWT.RIGHT);
lblDate.setText(Messages.ColumnDate);
DateTimePicker valueDate = new DateTimePicker(editArea);
context.bindValue(new SimpleDateTimeSelectionProperty().observe(valueDate.getControl()),
BeanProperties.value(Properties.date.name()).observe(model));
// other input fields
Input fxAmount = new Input(editArea, Messages.ColumnAmount);
fxAmount.bindValue(Properties.fxAmount.name(), Messages.ColumnAmount, Values.Amount, true);
fxAmount.bindCurrency(Properties.sourceAccountCurrency.name());
Input exchangeRate = new Input(editArea, useIndirectQuotation ? "/ " : "x "); //$NON-NLS-1$ //$NON-NLS-2$
exchangeRate.bindBigDecimal(
useIndirectQuotation ? Properties.inverseExchangeRate.name() : Properties.exchangeRate.name(),
Values.ExchangeRate.pattern());
exchangeRate.bindCurrency(useIndirectQuotation ? Properties.inverseExchangeRateCurrencies.name()
: Properties.exchangeRateCurrencies.name());
model().addPropertyChangeListener(Properties.exchangeRate.name(),
e -> exchangeRate.value.setToolTipText(AbstractModel.createCurrencyToolTip(
model().getExchangeRate(), model().getTargetAccountCurrency(),
model().getSourceAccountCurrency())));
Input amount = new Input(editArea, "="); //$NON-NLS-1$
amount.bindValue(Properties.amount.name(), Messages.ColumnAmount, Values.Amount, true);
amount.bindCurrency(Properties.targetAccountCurrency.name());
// note
Label lblNote = new Label(editArea, SWT.LEFT);
lblNote.setText(Messages.ColumnNote);
Text valueNote = new Text(editArea, SWT.BORDER);
context.bindValue(WidgetProperties.text(SWT.Modify).observe(valueNote),
BeanProperties.value(Properties.note.name()).observe(model));
//
// form layout
//
int amountWidth = amountWidth(amount.value);
int currencyWidth = currencyWidth(fxAmount.currency);
startingWith(source.value.getControl(), source.label).suffix(source.currency)
.thenBelow(target.value.getControl()).label(target.label).suffix(target.currency)
.thenBelow(valueDate.getControl()).label(lblDate) //
// fxAmount - exchange rate - amount
.thenBelow(fxAmount.value).width(amountWidth).label(fxAmount.label) //
.thenRight(fxAmount.currency).width(currencyWidth) //
.thenRight(exchangeRate.label) //
.thenRight(exchangeRate.value).width(amountWidth) //
.thenRight(exchangeRate.currency).width(amountWidth) //
.thenRight(amount.label) //
.thenRight(amount.value).width(amountWidth) //
// note
.suffix(amount.currency, currencyWidth) //
.thenBelow(valueNote).left(target.value.getControl()).right(amount.value).label(lblNote);
int widest = widest(source.label, target.label, lblDate, fxAmount.label, lblNote);
startingWith(source.label).width(widest);
//
// hide / show exchange rate if necessary
//
model.addPropertyChangeListener(Properties.exchangeRateCurrencies.name(), event -> {
String sourceCurrency = model().getSourceAccountCurrency();
String targetCurrency = model().getTargetAccountCurrency();
// make exchange rate visible if both are set but different
boolean visible = sourceCurrency.length() > 0 && targetCurrency.length() > 0
&& !sourceCurrency.equals(targetCurrency);
exchangeRate.setVisible(visible);
amount.setVisible(visible);
});
WarningMessages warnings = new WarningMessages(this);
warnings.add(() -> model().getDate().isAfter(LocalDate.now()) ? Messages.MsgDateIsInTheFuture : null);
model.addPropertyChangeListener(Properties.date.name(), e -> warnings.check());
model.firePropertyChange(Properties.exchangeRateCurrencies.name(), "", model().getExchangeRateCurrencies()); //$NON-NLS-1$
}
@Override
public void setAccount(Account account)
{
model().setSourceAccount(account);
}
public void setEntry(AccountTransferEntry entry)
{
model().setSource(entry);
}
}