package name.abuchen.portfolio.ui.views;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import javax.inject.Inject;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.AccountTransaction;
import name.abuchen.portfolio.model.BuySellEntry;
import name.abuchen.portfolio.model.Portfolio;
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.PortfolioTransferEntry;
import name.abuchen.portfolio.model.Security;
import name.abuchen.portfolio.model.SecurityEvent;
import name.abuchen.portfolio.model.SecurityPrice;
import name.abuchen.portfolio.model.Transaction;
import name.abuchen.portfolio.model.TransactionOwner;
import name.abuchen.portfolio.model.TransactionPair;
import name.abuchen.portfolio.model.Watchlist;
import name.abuchen.portfolio.money.CurrencyConverterImpl;
import name.abuchen.portfolio.money.ExchangeRateProviderFactory;
import name.abuchen.portfolio.money.Quote;
import name.abuchen.portfolio.money.Values;
import name.abuchen.portfolio.online.QuoteFeed;
import name.abuchen.portfolio.ui.Images;
import name.abuchen.portfolio.ui.Messages;
import name.abuchen.portfolio.ui.PortfolioPart;
import name.abuchen.portfolio.ui.dialogs.transactions.AccountTransactionDialog;
import name.abuchen.portfolio.ui.dialogs.transactions.OpenDialogAction;
import name.abuchen.portfolio.ui.dialogs.transactions.SecurityTransactionDialog;
import name.abuchen.portfolio.ui.dialogs.transactions.SecurityTransferDialog;
import name.abuchen.portfolio.ui.util.AbstractDropDown;
import name.abuchen.portfolio.ui.util.Colors;
import name.abuchen.portfolio.ui.util.SWTHelper;
import name.abuchen.portfolio.ui.util.SimpleAction;
import name.abuchen.portfolio.ui.util.TableViewerCSVExporter;
import name.abuchen.portfolio.ui.util.viewers.Column;
import name.abuchen.portfolio.ui.util.viewers.ColumnEditingSupport;
import name.abuchen.portfolio.ui.util.viewers.ColumnEditingSupport.ModificationListener;
import name.abuchen.portfolio.ui.util.viewers.ColumnViewerSorter;
import name.abuchen.portfolio.ui.util.viewers.DateEditingSupport;
import name.abuchen.portfolio.ui.util.viewers.SharesLabelProvider;
import name.abuchen.portfolio.ui.util.viewers.ShowHideColumnHelper;
import name.abuchen.portfolio.ui.util.viewers.ValueEditingSupport;
import name.abuchen.portfolio.ui.views.actions.ConvertBuySellToDeliveryAction;
import name.abuchen.portfolio.ui.views.actions.ConvertDeliveryToBuySellAction;
import name.abuchen.portfolio.ui.views.columns.NoteColumn;
import name.abuchen.portfolio.ui.wizards.security.EditSecurityDialog;
import name.abuchen.portfolio.ui.wizards.security.SearchYahooWizardDialog;
import name.abuchen.portfolio.util.Dates;
public class SecurityListView extends AbstractListView implements ModificationListener
{
private class CreateSecurityDropDown extends AbstractDropDown
{
public CreateSecurityDropDown(ToolBar toolBar)
{
super(toolBar, Messages.SecurityMenuAddNewSecurity, Images.PLUS.image(), SWT.NONE);
}
@Override
public void menuAboutToShow(IMenuManager manager)
{
manager.add(new SimpleAction(Messages.SecurityMenuNewSecurity, a -> {
Security newSecurity = new Security();
newSecurity.setFeed(QuoteFeed.MANUAL);
openEditDialog(newSecurity);
}));
manager.add(new SimpleAction(Messages.SecurityMenuSearchYahoo, a -> {
SearchYahooWizardDialog dialog = new SearchYahooWizardDialog(getToolBar().getShell(), getClient());
if (dialog.open() == Dialog.OK)
openEditDialog(dialog.getSecurity());
}));
}
private void openEditDialog(Security newSecurity)
{
Dialog dialog = new EditSecurityDialog(getToolBar().getShell(), getClient(), newSecurity);
if (dialog.open() == Dialog.OK)
{
markDirty();
getClient().addSecurity(newSecurity);
if (watchlist != null)
watchlist.getSecurities().add(newSecurity);
setSecurityTableInput();
securities.updateQuotes(newSecurity);
}
}
}
private class FilterDropDown extends AbstractDropDown
{
private Predicate<Security> securityIsNotInactive = record -> !record.isRetired();
public FilterDropDown(ToolBar toolBar, IPreferenceStore preferenceStore)
{
super(toolBar, Messages.SecurityListFilter, Images.FILTER_OFF.image(), SWT.NONE);
if (preferenceStore.getBoolean(this.getClass().getSimpleName() + "-hideInactiveSecurities")) //$NON-NLS-1$
filter.add(securityIsNotInactive);
if (!filter.isEmpty())
getToolItem().setImage(Images.FILTER_ON.image());
toolBar.addDisposeListener(
e -> preferenceStore.setValue(this.getClass().getSimpleName() + "-hideInactiveSecurities", //$NON-NLS-1$
filter.contains(securityIsNotInactive)));
}
@Override
public void menuAboutToShow(IMenuManager manager)
{
manager.add(createAction(Messages.SecurityListFilterHideInactive, securityIsNotInactive));
}
private Action createAction(String label, Predicate<Security> predicate)
{
Action action = new Action(label, Action.AS_CHECK_BOX)
{
@Override
public void run()
{
boolean isChecked = filter.contains(predicate);
if (isChecked)
filter.remove(predicate);
else
filter.add(predicate);
setChecked(!isChecked);
getToolItem().setImage(filter.isEmpty() ? Images.FILTER_OFF.image() : Images.FILTER_ON.image());
securities.refresh();
}
};
action.setChecked(filter.contains(predicate));
return action;
}
}
@Inject
private ExchangeRateProviderFactory factory;
private LocalResourceManager resources;
private Color warningColor;
private SecuritiesTable securities;
private TableViewer prices;
private TableViewer transactions;
private TableViewer events;
private SecuritiesChart chart;
private SecurityDetailsViewer latest;
private List<Predicate<Security>> filter = new ArrayList<>();
private Watchlist watchlist;
private Pattern filterPattern;
@Override
protected String getDefaultTitle()
{
StringBuilder title = new StringBuilder();
if (watchlist == null)
title.append(Messages.LabelSecurities);
else
title.append(Messages.LabelSecurities).append(' ').append(watchlist.getName());
if (securities != null)
title.append(" (").append(securities.getColumnHelper().getConfigurationName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
return title.toString();
}
@Override
protected int[] getDefaultWeights(int numOfChildren)
{
return new int[] { 50, 50 };
}
@Override
public void notifyModelUpdated()
{
if (securities != null && !securities.getTableViewer().getTable().isDisposed())
{
updateTitle(getDefaultTitle());
securities.getTableViewer().refresh(true);
securities.getTableViewer().setSelection(securities.getTableViewer().getSelection());
}
}
@Override
public void markDirty()
{
super.markDirty();
// see issue #448: if the note column is edited, the information area is
// not updated accordingly. #markDirty is called by the SecuritiesTable
// when any column is edited
Security security = (Security) ((IStructuredSelection) securities.getTableViewer().getSelection())
.getFirstElement();
latest.setInput(security);
}
@Override
public void onModified(Object element, Object newValue, Object oldValue)
{
// called from prices table
Security security = (Security) prices.getData(Security.class.toString());
// if the date changed, the prices must be reordered --> binary search
if (newValue instanceof LocalDate)
{
SecurityPrice price = (SecurityPrice) element;
security.removePrice(price);
security.addPrice(price);
}
securities.refresh(security);
prices.refresh(element);
latest.setInput(security);
transactions.setInput(security.getTransactions(getClient()));
events.setInput(security.getEvents());
chart.updateChart(security);
markDirty();
}
@Override
public void init(PortfolioPart part, Object parameter)
{
super.init(part, parameter);
if (parameter instanceof Watchlist)
this.watchlist = (Watchlist) parameter;
}
@Override
protected void addButtons(ToolBar toolBar)
{
addSearchButton(toolBar);
new ToolItem(toolBar, SWT.SEPARATOR | SWT.VERTICAL).setWidth(20);
new CreateSecurityDropDown(toolBar);
new FilterDropDown(toolBar, getPreferenceStore());
addExportButton(toolBar);
addSaveButton(toolBar);
addConfigButton(toolBar);
}
private void addSearchButton(ToolBar toolBar)
{
final Text search = new Text(toolBar, SWT.SEARCH | SWT.ICON_CANCEL);
search.setSize(100, SWT.DEFAULT);
search.setMessage(Messages.LabelSearch);
ToolItem toolItem = new ToolItem(toolBar, SWT.SEPARATOR);
toolItem.setWidth(search.getSize().x);
toolItem.setControl(search);
search.addModifyListener(e -> {
String filter = search.getText().trim();
if (filter.length() == 0)
{
filterPattern = null;
securities.refresh();
}
else
{
filterPattern = Pattern.compile(".*" + filter + ".*", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ //$NON-NLS-2$
securities.refresh();
}
});
}
private void addExportButton(ToolBar toolBar)
{
Action export = new Action()
{
@Override
public void run()
{
new TableViewerCSVExporter(securities.getTableViewer()) //
.export(getTitle() + ".csv"); //$NON-NLS-1$
}
};
export.setImageDescriptor(Images.EXPORT.descriptor());
export.setToolTipText(Messages.MenuExportData);
new ActionContributionItem(export).fill(toolBar, -1);
}
private void addSaveButton(ToolBar toolBar)
{
Action save = new Action()
{
@Override
public void run()
{
securities.getColumnHelper().showSaveMenu(getActiveShell());
}
};
save.setImageDescriptor(Images.SAVE.descriptor());
save.setToolTipText(Messages.MenuConfigureChart);
new ActionContributionItem(save).fill(toolBar, -1);
}
private void addConfigButton(ToolBar toolBar)
{
Action config = new Action()
{
@Override
public void run()
{
securities.getColumnHelper().showHideShowColumnsMenu(getActiveShell());
}
};
config.setImageDescriptor(Images.CONFIG.descriptor());
config.setToolTipText(Messages.MenuShowHideColumns);
new ActionContributionItem(config).fill(toolBar, -1);
}
// //////////////////////////////////////////////////////////////
// top table: securities
// //////////////////////////////////////////////////////////////
@Override
protected void createTopTable(Composite parent)
{
this.resources = new LocalResourceManager(JFaceResources.getResources(), parent);
this.warningColor = resources.createColor(Colors.WARNING.swt());
securities = new SecuritiesTable(parent, this);
updateTitle(getDefaultTitle());
securities.getColumnHelper().addListener(() -> updateTitle(getDefaultTitle()));
securities.addSelectionChangedListener(event -> onSecurityChanged(
(Security) ((IStructuredSelection) event.getSelection()).getFirstElement()));
securities.addFilter(new ViewerFilter()
{
@Override
public boolean select(Viewer viewer, Object parentElement, Object element)
{
if (filterPattern == null)
return true;
Security security = (Security) element;
String[] properties = new String[] { security.getName(), //
security.getIsin(), //
security.getTickerSymbol(), //
security.getWkn(), //
security.getNote() //
};
for (String property : properties)
{
if (property != null && filterPattern.matcher(property).matches())
return true;
}
return false;
}
});
securities.addFilter(new ViewerFilter()
{
@Override
public boolean select(Viewer viewer, Object parentElement, Object element)
{
for (Predicate<Security> predicate : filter)
{
if (!predicate.test((Security) element))
return false;
}
return true;
}
});
setSecurityTableInput();
}
private void setSecurityTableInput()
{
if (watchlist != null)
securities.setInput(watchlist);
else
securities.setInput(getClient().getSecurities());
}
private void onSecurityChanged(Security security)
{
prices.setData(Security.class.toString(), security);
prices.setInput(security != null ? security.getPrices() : new ArrayList<SecurityPrice>(0));
prices.refresh();
latest.setInput(security);
transactions.setInput(security != null ? security.getTransactions(getClient()) : new ArrayList<Transaction>(0));
events.setInput(security != null ? security.getEvents() : Collections.emptyList());
chart.updateChart(security);
}
// //////////////////////////////////////////////////////////////
// bottom table: folder
// //////////////////////////////////////////////////////////////
@Override
protected void createBottomTable(Composite parent)
{
SashForm sash = new SashForm(parent, SWT.HORIZONTAL);
// folder
CTabFolder folder = new CTabFolder(sash, SWT.BORDER);
// latest
latest = new SecurityDetailsViewer(sash, SWT.BORDER, getClient());
SWTHelper.setSashWeights(sash, parent.getParent().getParent(), latest.getControl());
// tab 1: chart
CTabItem item = new CTabItem(folder, SWT.NONE);
item.setText(Messages.SecurityTabChart);
Composite chartComposite = new Composite(folder, SWT.NONE);
GridLayoutFactory.fillDefaults().numColumns(2).spacing(0, 0).applyTo(chartComposite);
item.setControl(chartComposite);
chart = new SecuritiesChart(chartComposite, getClient(),
new CurrencyConverterImpl(factory, getClient().getBaseCurrency()));
// tab 2: historical quotes
item = new CTabItem(folder, SWT.NONE);
item.setText(Messages.SecurityTabHistoricalQuotes);
item.setControl(createPricesTable(folder));
// tab 3: transactions
item = new CTabItem(folder, SWT.NONE);
item.setText(Messages.SecurityTabTransactions);
item.setControl(createTransactionTable(folder));
// tab 4: event
item = new CTabItem(folder, SWT.NONE);
item.setText(Messages.SecurityTabEvents);
item.setControl(createEventsTable(folder));
folder.setSelection(0);
}
// //////////////////////////////////////////////////////////////
// tab item: prices
// //////////////////////////////////////////////////////////////
protected Composite createPricesTable(Composite parent)
{
Composite container = new Composite(parent, SWT.NONE);
TableColumnLayout layout = new TableColumnLayout();
container.setLayout(layout);
prices = new TableViewer(container, SWT.FULL_SELECTION | SWT.MULTI);
ColumnEditingSupport.prepare(prices);
ShowHideColumnHelper support = new ShowHideColumnHelper(SecurityListView.class.getSimpleName() + "@prices", //$NON-NLS-1$
getPreferenceStore(), prices, layout);
Column column = new Column(Messages.ColumnDate, SWT.None, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
return Values.Date.format(((SecurityPrice) element).getTime());
}
@Override
public Color getBackground(Object element)
{
SecurityPrice current = (SecurityPrice) element;
List<?> all = (List<?>) prices.getInput();
int index = all.indexOf(current);
if (index == 0)
return null;
SecurityPrice previous = (SecurityPrice) all.get(index - 1);
int days = Dates.daysBetween(previous.getTime(), current.getTime());
return days > 3 ? warningColor : null;
}
});
ColumnViewerSorter.create(SecurityPrice.class, "time").attachTo(column, SWT.UP); //$NON-NLS-1$
new DateEditingSupport(SecurityPrice.class, "time").addListener(this).attachTo(column); //$NON-NLS-1$
support.addColumn(column);
column = new Column(Messages.ColumnQuote, SWT.RIGHT, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
Security security = (Security) prices.getData(Security.class.toString());
SecurityPrice price = (SecurityPrice) element;
return Values.Quote.format(security.getCurrencyCode(), price.getValue(), getClient().getBaseCurrency());
}
});
ColumnViewerSorter.create(SecurityPrice.class, "value").attachTo(column); //$NON-NLS-1$
new ValueEditingSupport(SecurityPrice.class, "value", Values.Quote).addListener(this).attachTo(column); //$NON-NLS-1$
support.addColumn(column);
support.createColumns();
prices.getTable().setHeaderVisible(true);
prices.getTable().setLinesVisible(true);
prices.setContentProvider(ArrayContentProvider.getInstance());
hookContextMenu(prices.getTable(), this::fillPricesContextMenu);
return container;
}
private void fillPricesContextMenu(IMenuManager manager)
{
Security security = (Security) prices.getData(Security.class.toString());
if (security != null)
{
manager.add(new Action(Messages.SecurityMenuAddPrice)
{
@Override
public void run()
{
Security security = (Security) prices.getData(Security.class.toString());
if (security == null)
return;
SecurityPrice price = new SecurityPrice();
price.setTime(LocalDate.now());
security.addPrice(price);
markDirty();
prices.setInput(security.getPrices());
latest.setInput(security);
transactions.setInput(security.getTransactions(getClient()));
events.setInput(security.getEvents());
chart.updateChart(security);
prices.setSelection(new StructuredSelection(price), true);
prices.editElement(price, 0);
}
});
manager.add(new Separator());
}
if (((IStructuredSelection) prices.getSelection()).getFirstElement() != null)
{
manager.add(new Action(Messages.SecurityMenuDeletePrice)
{
@Override
public void run()
{
Security security = (Security) prices.getData(Security.class.toString());
if (security == null)
return;
Iterator<?> iter = ((IStructuredSelection) prices.getSelection()).iterator();
while (iter.hasNext())
{
SecurityPrice price = (SecurityPrice) iter.next();
if (price == null)
continue;
security.removePrice(price);
}
markDirty();
prices.setInput(security.getPrices());
latest.setInput(security);
transactions.setInput(security.getTransactions(getClient()));
events.setInput(security.getEvents());
chart.updateChart(security);
}
});
}
if (prices.getTable().getItemCount() > 0)
{
manager.add(new Action(Messages.SecurityMenuDeleteAllPrices)
{
@Override
public void run()
{
Security security = (Security) prices.getData(Security.class.toString());
if (security == null)
return;
security.removeAllPrices();
markDirty();
prices.setInput(security.getPrices());
latest.setInput(security);
transactions.setInput(security.getTransactions(getClient()));
events.setInput(security.getEvents());
chart.updateChart(security);
}
});
}
if (security != null)
{
manager.add(new Separator());
new QuotesContextMenu(this).menuAboutToShow(manager, security);
}
}
// //////////////////////////////////////////////////////////////
// tab item: transactions
// //////////////////////////////////////////////////////////////
protected Composite createTransactionTable(Composite parent)
{
Composite container = new Composite(parent, SWT.NONE);
TableColumnLayout layout = new TableColumnLayout();
container.setLayout(layout);
transactions = new TableViewer(container, SWT.FULL_SELECTION);
ColumnViewerToolTipSupport.enableFor(transactions, ToolTip.NO_RECREATE);
ShowHideColumnHelper support = new ShowHideColumnHelper(
SecurityListView.class.getSimpleName() + "@transactions3", getPreferenceStore(), transactions, //$NON-NLS-1$
layout);
Column column = new Column(Messages.ColumnDate, SWT.None, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
return Values.Date.format(((TransactionPair<?>) element).getTransaction().getDate());
}
});
column.setSorter(ColumnViewerSorter.create((o1, o2) -> ((TransactionPair<?>) o1).getTransaction().getDate()
.compareTo(((TransactionPair<?>) o2).getTransaction().getDate())), SWT.UP);
support.addColumn(column);
column = new Column(Messages.ColumnTransactionType, SWT.None, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
Transaction t = ((TransactionPair<?>) element).getTransaction();
if (t instanceof PortfolioTransaction)
return ((PortfolioTransaction) t).getType().toString();
else if (t instanceof AccountTransaction)
return ((AccountTransaction) t).getType().toString();
else
return null;
}
});
support.addColumn(column);
column = new Column(Messages.ColumnShares, SWT.RIGHT, 80);
column.setLabelProvider(new SharesLabelProvider()
{
@Override
public Long getValue(Object element)
{
Transaction t = ((TransactionPair<?>) element).getTransaction();
if (t instanceof PortfolioTransaction)
{
return ((PortfolioTransaction) t).getShares();
}
else if (t instanceof AccountTransaction)
{
long shares = ((AccountTransaction) t).getShares();
return shares != 0 ? shares : null;
}
return null;
}
});
support.addColumn(column);
column = new Column(Messages.ColumnAmount, SWT.RIGHT, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
Transaction t = ((TransactionPair<?>) element).getTransaction();
return Values.Money.format(t.getMonetaryAmount(), getClient().getBaseCurrency());
}
});
column.setSorter(ColumnViewerSorter.create((o1, o2) -> {
long a1 = ((TransactionPair<?>) o1).getTransaction().getAmount();
long a2 = ((TransactionPair<?>) o2).getTransaction().getAmount();
return a1 > a2 ? 1 : a1 < a2 ? -1 : 0;
}));
support.addColumn(column);
column = new Column(Messages.ColumnPerShare, SWT.RIGHT, 80);
column.setDescription(Messages.ColumnPerShare_Description);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
Transaction t = ((TransactionPair<?>) element).getTransaction();
if (t instanceof PortfolioTransaction)
{
return Values.Quote.format(((PortfolioTransaction) t).getGrossPricePerShare(),
getClient().getBaseCurrency());
}
else if (t instanceof AccountTransaction)
{
long shares = ((AccountTransaction) t).getShares();
if (shares != 0)
{
long perShare = Math.round(((AccountTransaction) t).getGrossValueAmount()
* Values.Share.divider() * Values.Quote.factorToMoney() / shares);
return Values.Quote.format(Quote.of(t.getCurrencyCode(), perShare));
}
}
return null;
}
});
support.addColumn(column);
column = new Column(Messages.ColumnPortfolio, SWT.NONE, 120);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
TransactionOwner<?> owner = ((TransactionPair<?>) element).getOwner();
if (owner instanceof Portfolio)
return owner.toString();
return null;
}
});
support.addColumn(column);
column = new Column(Messages.ColumnOffsetAccount, SWT.NONE, 120);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
TransactionPair<?> pair = (TransactionPair<?>) element;
Transaction t = pair.getTransaction();
if (t instanceof PortfolioTransaction)
return t.getCrossEntry() != null ? t.getCrossEntry().getCrossOwner(t).toString() : null;
else
return pair.getOwner().toString();
}
});
support.addColumn(column);
column = new NoteColumn();
column.setEditingSupport(null);
support.addColumn(column);
support.createColumns();
transactions.getTable().setHeaderVisible(true);
transactions.getTable().setLinesVisible(true);
transactions.setContentProvider(ArrayContentProvider.getInstance());
hookContextMenu(transactions.getControl(), this::transactionMenuAboutToShow);
hookKeyListener();
return container;
}
private void hookKeyListener()
{
transactions.getControl().addKeyListener(new KeyAdapter()
{
@Override
public void keyPressed(KeyEvent e)
{
if (e.keyCode == 'e' && e.stateMask == SWT.MOD1)
{
TransactionPair<?> pair = (TransactionPair<?>) ((IStructuredSelection) transactions.getSelection())
.getFirstElement();
if (pair != null)
createEditAction(pair).run();
}
}
});
}
private void transactionMenuAboutToShow(IMenuManager manager) // NOSONAR
{
Security security = (Security) prices.getData(Security.class.toString());
if (security == null)
return;
TransactionPair<?> pair = (TransactionPair<?>) ((IStructuredSelection) transactions.getSelection())
.getFirstElement();
if (pair != null)
{
Action action = createEditAction(pair);
action.setAccelerator(SWT.MOD1 | 'E');
manager.add(action);
manager.add(new Separator());
if (pair.getTransaction() instanceof PortfolioTransaction)
{
Portfolio p = (Portfolio) pair.getOwner();
PortfolioTransaction t = (PortfolioTransaction) pair.getTransaction();
if (t.getType() == PortfolioTransaction.Type.BUY || t.getType() == PortfolioTransaction.Type.SELL)
{
manager.add(new ConvertBuySellToDeliveryAction(getClient(), new TransactionPair<>(p, t)));
manager.add(new Separator());
}
if (t.getType() == PortfolioTransaction.Type.DELIVERY_INBOUND
|| t.getType() == PortfolioTransaction.Type.DELIVERY_OUTBOUND)
{
manager.add(new ConvertDeliveryToBuySellAction(getClient(), new TransactionPair<>(p, t)));
manager.add(new Separator());
}
}
}
new SecurityContextMenu(SecurityListView.this).menuAboutToShow(manager, security);
manager.add(new Separator());
manager.add(new Action(Messages.MenuTransactionDelete)
{
@Override
public void run()
{
TransactionPair<?> pair = (TransactionPair<?>) ((IStructuredSelection) transactions.getSelection())
.getFirstElement();
if (pair == null)
return;
pair.deleteTransaction(getClient());
getClient().markDirty();
}
});
}
private Action createEditAction(TransactionPair<?> transactionPair)
{
if (transactionPair.getTransaction().getCrossEntry() instanceof BuySellEntry)
{
BuySellEntry entry = (BuySellEntry) transactionPair.getTransaction().getCrossEntry();
return new OpenDialogAction(this, Messages.MenuEditTransaction)
.type(SecurityTransactionDialog.class, d -> d.setBuySellEntry(entry))
.parameters(entry.getPortfolioTransaction().getType());
}
else if (transactionPair.getTransaction().getCrossEntry() instanceof PortfolioTransferEntry)
{
PortfolioTransferEntry entry = (PortfolioTransferEntry) transactionPair.getTransaction().getCrossEntry();
return new OpenDialogAction(this, Messages.MenuEditTransaction) //
.type(SecurityTransferDialog.class, d -> d.setEntry(entry));
}
else if (transactionPair.getTransaction() instanceof PortfolioTransaction)
{
@SuppressWarnings("unchecked")
TransactionPair<PortfolioTransaction> pair = (TransactionPair<PortfolioTransaction>) transactionPair;
return new OpenDialogAction(this, Messages.MenuEditTransaction) //
.type(SecurityTransactionDialog.class, d -> d.setDeliveryTransaction(pair)) //
.parameters(pair.getTransaction().getType());
}
else if (transactionPair.getTransaction() instanceof AccountTransaction)
{
@SuppressWarnings("unchecked")
TransactionPair<AccountTransaction> pair = (TransactionPair<AccountTransaction>) transactionPair;
return new OpenDialogAction(this, Messages.MenuEditTransaction) //
.type(AccountTransactionDialog.class,
d -> d.setTransaction((Account) pair.getOwner(), pair.getTransaction())) //
.parameters(pair.getTransaction().getType());
}
else
{
throw new IllegalArgumentException();
}
}
// //////////////////////////////////////////////////////////////
// tab item: transactions
// //////////////////////////////////////////////////////////////
protected Composite createEventsTable(Composite parent)
{
Composite container = new Composite(parent, SWT.NONE);
TableColumnLayout layout = new TableColumnLayout();
container.setLayout(layout);
events = new TableViewer(container, SWT.FULL_SELECTION);
ShowHideColumnHelper support = new ShowHideColumnHelper(SecurityListView.class.getSimpleName() + "@events", //$NON-NLS-1$
getPreferenceStore(), events, layout);
Column column = new Column(Messages.ColumnDate, SWT.None, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
return Values.Date.format(((SecurityEvent) element).getDate());
}
});
column.setSorter(ColumnViewerSorter.create(e -> ((SecurityEvent) e).getDate()), SWT.UP);
support.addColumn(column);
column = new Column(Messages.ColumnTransactionType, SWT.None, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
return ((SecurityEvent) element).getType().toString();
}
});
support.addColumn(column);
column = new Column(Messages.ColumnDetails, SWT.None, 80);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
return ((SecurityEvent) element).getDetails();
}
});
support.addColumn(column);
support.createColumns();
events.getTable().setHeaderVisible(true);
events.getTable().setLinesVisible(true);
events.setContentProvider(ArrayContentProvider.getInstance());
return container;
}
}