package name.abuchen.portfolio.ui.views;
import java.time.LocalDate;
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.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ToolBar;
import name.abuchen.portfolio.model.Account;
import name.abuchen.portfolio.model.Portfolio;
import name.abuchen.portfolio.money.CurrencyConverter;
import name.abuchen.portfolio.money.CurrencyConverterImpl;
import name.abuchen.portfolio.money.ExchangeRateProviderFactory;
import name.abuchen.portfolio.snapshot.PortfolioSnapshot;
import name.abuchen.portfolio.ui.Images;
import name.abuchen.portfolio.ui.Messages;
import name.abuchen.portfolio.ui.PortfolioPart;
import name.abuchen.portfolio.ui.util.AbstractDropDown;
import name.abuchen.portfolio.ui.util.SimpleAction;
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.ListEditingSupport;
import name.abuchen.portfolio.ui.util.viewers.ShowHideColumnHelper;
import name.abuchen.portfolio.ui.views.columns.NameColumn;
import name.abuchen.portfolio.ui.views.columns.NameColumn.NameColumnLabelProvider;
import name.abuchen.portfolio.ui.views.columns.NoteColumn;
public class PortfolioListView extends AbstractListView implements ModificationListener
{
private static final String FILTER_INACTIVE_PORTFOLIOS = "filter-retired-portfolios"; //$NON-NLS-1$
@Inject
private ExchangeRateProviderFactory factory;
private TableViewer portfolios;
private StatementOfAssetsViewer statementOfAssets;
private PortfolioTransactionsViewer transactions;
private ShowHideColumnHelper portfolioColumns;
private boolean isFiltered = false;
@Override
protected String getDefaultTitle()
{
return Messages.LabelPortfolios;
}
@Override
public void init(PortfolioPart part, Object parameter)
{
super.init(part, parameter);
isFiltered = part.getPreferenceStore().getBoolean(FILTER_INACTIVE_PORTFOLIOS);
}
private void setInput()
{
portfolios.setInput(isFiltered ? getClient().getActivePortfolios() : getClient().getPortfolios());
}
@Override
public void notifyModelUpdated()
{
// actions from the security context menu (buy, sell, ...) call
// #notifyModelUpdated when adding new transactions
portfolios.setSelection(portfolios.getSelection());
}
@Override
public void onModified(Object element, Object newValue, Object oldValue)
{
markDirty();
}
@Override
protected void addButtons(ToolBar toolBar)
{
addNewButton(toolBar);
addFilterButton(toolBar);
addConfigButton(toolBar);
}
private void addNewButton(ToolBar toolBar)
{
SimpleAction.Runnable newPortfolioAction = a -> {
Portfolio portfolio = new Portfolio();
portfolio.setName(Messages.LabelNoName);
if (!getClient().getAccounts().isEmpty())
{
portfolio.setReferenceAccount(getClient().getAccounts().get(0));
}
else
{
Account account = new Account();
account.setName(Messages.LabelDefaultReferenceAccountName);
getClient().addAccount(account);
portfolio.setReferenceAccount(account);
}
getClient().addPortfolio(portfolio);
markDirty();
setInput();
portfolios.editElement(portfolio, 0);
};
AbstractDropDown.create(toolBar, Messages.MenuCreatePortfolioOrTransaction, Images.PLUS.image(), SWT.NONE,
(dd, manager) -> {
manager.add(new SimpleAction(Messages.PortfolioMenuAdd, newPortfolioAction));
manager.add(new Separator());
Portfolio portfolio = (Portfolio) portfolios.getStructuredSelection().getFirstElement();
new SecurityContextMenu(PortfolioListView.this).menuAboutToShow(manager, null, portfolio);
});
}
private void addFilterButton(ToolBar toolBar)
{
Action filter = new Action()
{
@Override
public void run()
{
isFiltered = !isFiltered;
getPart().getPreferenceStore().setValue(FILTER_INACTIVE_PORTFOLIOS, isFiltered);
setImageDescriptor(isFiltered ? Images.FILTER_ON.descriptor() : Images.FILTER_OFF.descriptor());
setInput();
}
};
filter.setImageDescriptor(isFiltered ? Images.FILTER_ON.descriptor() : Images.FILTER_OFF.descriptor());
filter.setToolTipText(Messages.PortfolioFilterRetiredPortfolios);
new ActionContributionItem(filter).fill(toolBar, -1);
}
private void addConfigButton(final ToolBar toolBar)
{
new AbstractDropDown(toolBar, Messages.MenuShowHideColumns, Images.CONFIG.image(), SWT.NONE)
{
@Override
public void menuAboutToShow(IMenuManager manager)
{
MenuManager m = new MenuManager(Messages.LabelPortfolios);
portfolioColumns.menuAboutToShow(m);
manager.add(m);
m = new MenuManager(Messages.LabelTransactions);
transactions.getColumnSupport().menuAboutToShow(m);
manager.add(m);
}
};
}
// //////////////////////////////////////////////////////////////
// top table: accounts
// //////////////////////////////////////////////////////////////
protected void createTopTable(Composite parent)
{
Composite container = new Composite(parent, SWT.NONE);
TableColumnLayout layout = new TableColumnLayout();
container.setLayout(layout);
portfolios = new TableViewer(container, SWT.FULL_SELECTION);
ColumnEditingSupport.prepare(portfolios);
portfolioColumns = new ShowHideColumnHelper(PortfolioListView.class.getSimpleName() + "@top2", //$NON-NLS-1$
getPreferenceStore(), portfolios, layout);
Column column = new NameColumn("0", Messages.ColumnPortfolio, SWT.None, 100); //$NON-NLS-1$
column.setLabelProvider(new NameColumnLabelProvider()
{
@Override
public Color getForeground(Object e)
{
boolean isRetired = ((Portfolio) e).isRetired();
return isRetired ? Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY) : null;
}
});
column.getEditingSupport().addListener(this);
portfolioColumns.addColumn(column);
column = new Column(Messages.ColumnReferenceAccount, SWT.None, 160);
column.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object e)
{
Portfolio p = (Portfolio) e;
return p.getReferenceAccount() != null ? p.getReferenceAccount().getName() : null;
}
});
ColumnViewerSorter.create(Portfolio.class, "referenceAccount").attachTo(column); //$NON-NLS-1$
new ListEditingSupport(Portfolio.class, "referenceAccount", getClient().getAccounts()) //$NON-NLS-1$
.addListener(this).attachTo(column);
portfolioColumns.addColumn(column);
column = new NoteColumn();
column.getEditingSupport().addListener(this);
portfolioColumns.addColumn(column);
portfolioColumns.createColumns();
portfolios.getTable().setHeaderVisible(true);
portfolios.getTable().setLinesVisible(true);
portfolios.setContentProvider(ArrayContentProvider.getInstance());
setInput();
portfolios.addSelectionChangedListener(event -> {
Portfolio portfolio = (Portfolio) ((IStructuredSelection) event.getSelection()).getFirstElement();
if (portfolio != null)
{
transactions.setInput(portfolio, portfolio.getTransactions());
transactions.refresh();
CurrencyConverter converter = new CurrencyConverterImpl(factory,
portfolio.getReferenceAccount().getCurrencyCode());
statementOfAssets.setInput(PortfolioSnapshot.create(portfolio, converter, LocalDate.now()));
}
else
{
transactions.setInput(null, null);
transactions.refresh();
statementOfAssets.setInput((PortfolioSnapshot) null);
}
});
hookContextMenu(portfolios.getTable(), this::fillPortfolioContextMenu);
}
private void fillPortfolioContextMenu(IMenuManager manager)
{
final Portfolio portfolio = (Portfolio) ((IStructuredSelection) portfolios.getSelection()).getFirstElement();
if (portfolio == null)
return;
new SecurityContextMenu(this).menuAboutToShow(manager, null, portfolio);
manager.add(new Separator());
manager.add(new Action(
portfolio.isRetired() ? Messages.PortfolioMenuActivate : Messages.PortfolioMenuDeactivate)
{
@Override
public void run()
{
portfolio.setRetired(!portfolio.isRetired());
markDirty();
setInput();
}
});
manager.add(new Action(Messages.PortfolioMenuDelete)
{
@Override
public void run()
{
getClient().removePortfolio(portfolio);
markDirty();
setInput();
}
});
}
// //////////////////////////////////////////////////////////////
// bottom table: transactions
// //////////////////////////////////////////////////////////////
@Override
protected void createBottomTable(Composite parent)
{
CTabFolder folder = new CTabFolder(parent, SWT.BORDER);
CTabItem item = new CTabItem(folder, SWT.NONE);
item.setText(Messages.LabelStatementOfAssets);
statementOfAssets = make(StatementOfAssetsViewer.class);
item.setControl(statementOfAssets.createControl(folder));
hookContextMenu(statementOfAssets.getTableViewer().getControl(),
manager -> statementOfAssets.hookMenuListener(manager, PortfolioListView.this));
item = new CTabItem(folder, SWT.NONE);
item.setText(Messages.TabTransactions);
transactions = new PortfolioTransactionsViewer(folder, this);
item.setControl(transactions.getControl());
folder.setSelection(0);
if (!getClient().getActivePortfolios().isEmpty())
portfolios.setSelection(new StructuredSelection(portfolios.getElementAt(0)), true);
}
}