package org.multibit.hd.ui.views.components;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.bitcoinj.core.Coin;
import org.joda.time.DateTime;
import org.multibit.hd.core.dto.*;
import org.multibit.hd.ui.languages.MessageKey;
import org.multibit.hd.ui.views.components.renderers.AmountBTCTableHeaderRenderer;
import org.multibit.hd.ui.views.components.tables.ContactTableModel;
import org.multibit.hd.ui.views.components.tables.PaymentTableModel;
import org.multibit.hd.ui.views.components.tables.StripedTable;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import static org.multibit.hd.ui.MultiBitUI.*;
/**
* <p>Utility to provide the following to UI:</p>
* <ul>
* <li>Provision of localised tables with themed rendering</li>
* </ul>
*
* @since 0.0.1
*
*/
public class Tables {
/**
* Utilities have no public constructor
*/
private Tables() {
}
/**
* @param contacts The contacts to show
* @param enterButton The button to be pressed on "Enter" or double click
*
* @return A new "contacts" striped table
*/
public static StripedTable newContactsTable(List<Contact> contacts, JButton enterButton) {
ContactTableModel model = new ContactTableModel(contacts);
StripedTable table = new StripedTable(model);
// Ensure it is accessible
AccessibilityDecorator.apply(table, MessageKey.CONTACTS);
// Decorate with standard screen theme
TableDecorator.applyScreenTheme(table, enterButton);
// Apply any exceptions
table.setRowHeight(LARGE_ICON_SIZE + TABLE_SPACER);
// Checkbox column
TableColumn checkBoxTableColumn = table.getColumnModel().getColumn(ContactTableModel.CHECKBOX_COLUMN_INDEX);
checkBoxTableColumn.setCellRenderer(Renderers.newCheckboxRenderer());
resizeColumn(table, ContactTableModel.CHECKBOX_COLUMN_INDEX, NORMAL_ICON_SIZE + TABLE_SPACER);
// Gravatar column
TableColumn gravatarTableColumn = table.getColumnModel().getColumn(ContactTableModel.GRAVATAR_COLUMN_INDEX);
gravatarTableColumn.setCellRenderer(Renderers.newImageIconRenderer());
resizeColumn(table, ContactTableModel.GRAVATAR_COLUMN_INDEX, LARGE_ICON_SIZE + TABLE_SPACER);
// Email column
TableColumn emailTableColumn = table.getColumnModel().getColumn(ContactTableModel.EMAIL_COLUMN_INDEX);
emailTableColumn.setCellRenderer(Renderers.newLeadingJustifiedStringRenderer());
// Address
TableColumn addressTableColumn = table.getColumnModel().getColumn(ContactTableModel.ADDRESS_COLUMN_INDEX);
addressTableColumn.setCellRenderer(Renderers.newLeadingJustifiedStringRenderer());
// Name
TableColumn nameTableColumn = table.getColumnModel().getColumn(ContactTableModel.NAME_COLUMN_INDEX);
nameTableColumn.setCellRenderer(Renderers.newLeadingJustifiedStringRenderer());
// Tags
TableColumn tagTableColumn = table.getColumnModel().getColumn(ContactTableModel.TAG_COLUMN_INDEX);
tagTableColumn.setCellRenderer(Renderers.newLeadingJustifiedStringRenderer());
justifyColumnHeaders(table);
return table;
}
/**
* @param paymentData The payments to show
* @param enterButton The button to be pressed on "Enter" or double click
*
* @return A new "payments" striped table
*/
public static StripedTable newPaymentsTable(Set<PaymentData> paymentData, JButton enterButton) {
PaymentTableModel model = new PaymentTableModel(paymentData);
StripedTable table = new StripedTable(model);
// Ensure it is accessible
AccessibilityDecorator.apply(table, MessageKey.PAYMENTS);
TableDecorator.applyScreenTheme(table, enterButton);
// Date column
TableColumn dateTableColumn = table.getColumnModel().getColumn(PaymentTableModel.DATE_COLUMN_INDEX);
dateTableColumn.setCellRenderer(Renderers.newTrailingJustifiedDateRenderer());
resizeColumn(table, PaymentTableModel.DATE_COLUMN_INDEX, 150, 200);
// Status column
TableColumn statusTableColumn = table.getColumnModel().getColumn(PaymentTableModel.STATUS_COLUMN_INDEX);
statusTableColumn.setCellRenderer(Renderers.newRAGStatusRenderer(model));
resizeColumn(table, PaymentTableModel.STATUS_COLUMN_INDEX, 60, 90);
// Type column
TableColumn typeTableColumn = table.getColumnModel().getColumn(PaymentTableModel.TYPE_COLUMN_INDEX);
typeTableColumn.setCellRenderer(Renderers.newPaymentTypeRenderer());
resizeColumn(table, PaymentTableModel.TYPE_COLUMN_INDEX, 120, 150);
// Amount BTC column
TableColumn column = table.getColumnModel().getColumn(PaymentTableModel.AMOUNT_BTC_COLUMN_INDEX);
column.setHeaderRenderer(new AmountBTCTableHeaderRenderer(
table.getTableHeader().getDefaultRenderer(),
new int[]{PaymentTableModel.AMOUNT_BTC_COLUMN_INDEX}
));
TableColumn amountBTCTableColumn = table.getColumnModel().getColumn(PaymentTableModel.AMOUNT_BTC_COLUMN_INDEX);
amountBTCTableColumn.setCellRenderer(Renderers.newTrailingJustifiedNumericRenderer());
resizeColumn(table, PaymentTableModel.AMOUNT_BTC_COLUMN_INDEX, 120, 180);
// Description
TableColumn descriptionTableColumn = table.getColumnModel().getColumn(PaymentTableModel.DESCRIPTION_COLUMN_INDEX);
descriptionTableColumn.setCellRenderer(Renderers.newLeadingJustifiedStringRenderer());
// Amount Fiat column
TableColumn amountFiatTableColumn = table.getColumnModel().getColumn(PaymentTableModel.AMOUNT_FIAT_COLUMN_INDEX);
amountFiatTableColumn.setCellRenderer(Renderers.newTrailingJustifiedFiatRenderer());
resizeColumn(table, PaymentTableModel.AMOUNT_FIAT_COLUMN_INDEX, 120, 180);
// Row sorter for date
TableRowSorter<TableModel> rowSorter = new TableRowSorter<>(table.getModel());
table.setRowSorter(rowSorter);
// Sort by date descending
List<TableRowSorter.SortKey> sortKeys = Lists.newArrayList();
sortKeys.add(new TableRowSorter.SortKey(PaymentTableModel.DATE_COLUMN_INDEX, SortOrder.DESCENDING));
rowSorter.setSortKeys(sortKeys);
// Comparator for date
Comparator<DateTime> comparatorDate = newDateTimeComparator();
rowSorter.setComparator(PaymentTableModel.DATE_COLUMN_INDEX, comparatorDate);
// Comparator for status
Comparator<PaymentStatus> comparatorStatus = newStatusComparator();
rowSorter.setComparator(PaymentTableModel.STATUS_COLUMN_INDEX, comparatorStatus);
// Comparator for payment type
Comparator<PaymentType> comparatorPaymentType = newPaymentTypeComparator();
rowSorter.setComparator(PaymentTableModel.TYPE_COLUMN_INDEX, comparatorPaymentType);
// Comparator for amount BTC
Comparator<Optional> comparatorCoinOptional = newOptionalCoinComparator();
rowSorter.setComparator(PaymentTableModel.AMOUNT_BTC_COLUMN_INDEX, comparatorCoinOptional);
// Comparator for amount fiat
Comparator<FiatPayment> comparatorFiatPayment = newFiatPaymentComparator();
rowSorter.setComparator(PaymentTableModel.AMOUNT_FIAT_COLUMN_INDEX, comparatorFiatPayment);
justifyColumnHeaders(table);
return table;
}
/**
* @return A new DateTime comparator for use with a TableRowSorter
*/
private static Comparator<DateTime> newDateTimeComparator() {
return new Comparator<DateTime>() {
@Override
public int compare(DateTime o1, DateTime o2) {
if (o1 != null && o2 == null) {
return 1;
}
return o1 != null ? o1.compareTo(o2) : 0;
}
};
}
/**
* @return A new status comparator for use with a TableRowSorter
*/
private static Comparator<PaymentStatus> newStatusComparator() {
return new Comparator<PaymentStatus>() {
@Override
public int compare(PaymentStatus o1, PaymentStatus o2) {
if (o1 != null && o2 == null) {
return 1;
}
return o1 != null ? o1.compareToWithOrdinal(o2) : 0;
}
};
}
/**
* @return A new Optional-of-Coin comparator for use with a TableRowSorter
*/
private static Comparator<Optional> newOptionalCoinComparator() {
return new Comparator<Optional>() {
@Override
public int compare(Optional o1, Optional o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return -1;
}
} else {
if (o2 == null) {
return 1;
}
// Both not null
boolean present1 = o1.isPresent();
boolean present2 = o2.isPresent();
if (present1) {
if (present2) {
// Both present
if (o1.get() instanceof Coin && o2.get() instanceof Coin) {
return ((Coin) o1.get()).compareTo((Coin) o2.get());
} else {
return 0; // All none Coins are equal
}
} else {
return 1;
}
} else {
if (present2) {
return -1;
} else {
return 0;
}
}
}
}
};
}
/**
* @return A new FiatPayment comparator for use with a TableRowSorter
*/
private static Comparator<FiatPayment> newFiatPaymentComparator() {
return new Comparator<FiatPayment>() {
@Override
public int compare(FiatPayment o1, FiatPayment o2) {
if (o1 != null && o2 == null) {
return 1;
}
return o1 != null ? o1.compareTo(o2) : 0;
}
};
}
/**
* @return A new PaymentType comparator for use with a TableRowSorter
*/
private static Comparator<PaymentType> newPaymentTypeComparator() {
return new Comparator<PaymentType>() {
@Override
public int compare(PaymentType o1, PaymentType o2) {
if (o1 != null && o2 == null) {
return 1;
}
return o1 != null ? o1.compareTo(o2) : 0;
}
};
}
/**
* <p>Center the column headers</p>
*
* @param table The table
*/
private static void justifyColumnHeaders(JTable table) {
TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer();
JLabel label = (JLabel) renderer;
label.setHorizontalAlignment(JLabel.CENTER);
}
/**
* <p>Resize a column by setting its preferred with</p>
*
* @param table The table
* @param columnIndex The column index
* @param preferredWidth The preferred width
*/
private static void resizeColumn(StripedTable table, int columnIndex, int preferredWidth) {
String id = table.getColumnName(columnIndex);
table.getColumn(id).setPreferredWidth(preferredWidth);
}
/**
* <p>Resize a column by setting its preferred width</p>
*
* @param table The table
* @param columnIndex The column index
* @param preferredWidth The preferred width
* @param maxWidth The maximum width
*/
private static void resizeColumn(StripedTable table, int columnIndex, int preferredWidth, int maxWidth) {
String id = table.getColumnName(columnIndex);
table.getColumn(id).setPreferredWidth(preferredWidth);
table.getColumn(id).setMaxWidth(maxWidth);
}
}