/*
*
* Copyright 2014 http://Bither.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package net.bither.viewsystem.froms;
import net.bither.Bither;
import net.bither.BitherSetting;
import net.bither.bitherj.api.http.BitherUrl;
import net.bither.bitherj.core.AddressManager;
import net.bither.bitherj.core.HDAccount;
import net.bither.bitherj.core.Tx;
import net.bither.bitherj.utils.Utils;
import net.bither.implbitherj.BlockNotificationCenter;
import net.bither.implbitherj.TxNotificationCenter;
import net.bither.languages.MessageKey;
import net.bither.model.TxTableModel;
import net.bither.utils.DateUtils;
import net.bither.utils.LocaliserUtils;
import net.bither.utils.ViewUtil;
import net.bither.viewsystem.action.ShowTransactionDetailsAction;
import net.bither.viewsystem.base.*;
import net.bither.viewsystem.base.renderer.DecimalAlignRenderer;
import net.bither.viewsystem.base.renderer.ImageRenderer;
import net.bither.viewsystem.base.renderer.TrailingJustifiedDateRenderer;
import net.bither.viewsystem.components.ScrollBarUIDecorator;
import net.bither.viewsystem.themes.Themes;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
public class ShowTransactionsForm implements Viewable, TxNotificationCenter.ITxListener, BlockNotificationCenter.IBlockListener {
private JTable table;
private TxTableModel txTableModel;
private TableRowSorter<TableModel> rowSorter;
private ListSelectionModel listSelectionModel;
private int selectedRow = -1;
private Action showTransactionDetailsAction;
private JButton showTransactionsButton;
private JScrollPane scrollPane;
private ShowTransactionHeaderForm showTransactionHeaderForm;
private JPanel panelMain;
private List<Tx> txList = new ArrayList<Tx>();
private JButton btnAddress;
public ShowTransactionsForm() {
TxNotificationCenter.addTxListener(ShowTransactionsForm.this);
BlockNotificationCenter.addBlockChange(ShowTransactionsForm.this);
initUI();
panelMain.applyComponentOrientation(ComponentOrientation.getOrientation(LocaliserUtils.getLocale()));
refreshTx();
}
private void refreshTx() {
new Thread(new Runnable() {
@Override
public void run() {
if (Bither.getActionAddress() != null) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (Bither.getActionAddress() instanceof HDAccount) {
btnAddress.setVisible(false);
} else {
btnAddress.setVisible(true);
}
}
});
final List<Tx> actionTxList = Bither.getActionAddress().getTxs();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
txList.clear();
txList.addAll(actionTxList);
txTableModel.fireTableDataChanged();
}
});
}
}
}).start();
}
private void initUI() {
showTransactionHeaderForm = new ShowTransactionHeaderForm();
JPanel panel = showTransactionHeaderForm.getPanel();
JPanel btnTxPanel = createTxDetailButtonPanel();
panelMain.add(panel, BorderLayout.NORTH);
JPanel transactionsPanel = createTransactionsPanel();
panelMain.add(transactionsPanel, BorderLayout.CENTER);
panelMain.add(btnTxPanel, BorderLayout.SOUTH);
if (AddressManager.getInstance().getAllAddresses().size() == 0 &&
AddressManager.getInstance().getHdAccount() == null) {
showTransactionHeaderForm.setVisible(false);
} else {
showTransactionHeaderForm.setVisible(true);
}
}
private JPanel createTransactionsPanel() {
JPanel transactionsPanel = new JPanel();
transactionsPanel.setMinimumSize(new Dimension(550, 160));
transactionsPanel.setBackground(Themes.currentTheme.detailPanelBackground());
transactionsPanel.setLayout(new GridBagLayout());
transactionsPanel.setOpaque(true);
GridBagConstraints constraints = new GridBagConstraints();
txTableModel = new TxTableModel(txList);
table = new JTable(txTableModel);
table.setOpaque(false);
table.setBorder(BorderFactory.createEmptyBorder());
table.setComponentOrientation(ComponentOrientation.getOrientation(LocaliserUtils.getLocale()));
table.setRowHeight(Math.max(BitherSetting.MINIMUM_ICON_HEIGHT, panelMain.getFontMetrics(FontSizer.INSTANCE.getAdjustedDefaultFont()).getHeight()) + BitherSetting.HEIGHT_DELTA);
// Use status icons.
table.getColumnModel().getColumn(0).setCellRenderer(new ImageRenderer(ShowTransactionsForm.this));
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(false);
table.setBackground(Themes.currentTheme.detailPanelBackground());
// No row is currently selected.
selectedRow = -1;
// Listener for row selections.
listSelectionModel = table.getSelectionModel();
listSelectionModel.addListSelectionListener(new SharedListSelectionHandler(showTransactionDetailsAction));
// Date right justified.
table.getColumnModel().getColumn(1).setCellRenderer(new TrailingJustifiedDateRenderer(ShowTransactionsForm.this));
// Justify column headers.
justifyColumnHeaders();
// // Description leading justified (set explicitly as it does not seem to work otherwise).
// if (ComponentOrientation.getOrientation(LocaliserUtils.getLocale()).isLeftToRight()) {
// table.getColumnModel().getColumn(2).setCellRenderer(new LeadingJustifiedRenderer(ShowTransactionsForm.this));
// } else {
// table.getColumnModel().getColumn(2).setCellRenderer(new TrailingJustifiedStringRenderer(ShowTransactionsForm.this));
// }
// Amount decimal aligned
DecimalAlignRenderer decimalAlignRenderer = new DecimalAlignRenderer(ShowTransactionsForm.this);
table.getColumnModel().getColumn(2).setCellRenderer(decimalAlignRenderer);
FontMetrics fontMetrics = panelMain.getFontMetrics(FontSizer.INSTANCE.getAdjustedDefaultFont());
TableColumn tableColumn = table.getColumnModel().getColumn(0); // status
int statusWidth = fontMetrics.stringWidth(LocaliserUtils.getString("walletData.statusText"));
tableColumn.setPreferredWidth(statusWidth + BitherSetting.STATUS_WIDTH_DELTA);
tableColumn = table.getColumnModel().getColumn(1); // Date.
int dateWidth = Math.max(fontMetrics.stringWidth(LocaliserUtils.getString("walletData.dateText")),
fontMetrics.stringWidth(DateUtils.getDateTimeString(new Date(DateUtils.nowUtc().getMillis()))));
tableColumn.setPreferredWidth(dateWidth);
tableColumn = table.getColumnModel().getColumn(2); // Amount (BTC).
int amountBTCWidth = Math.max(fontMetrics.stringWidth(LocaliserUtils.getString("sendBitcoinPanel.amountLabel") + " (BTC)"),
fontMetrics.stringWidth("00000.000000000"));
tableColumn.setPreferredWidth(amountBTCWidth);
tableColumn.setMinWidth(amountBTCWidth);
// Row sorter.
rowSorter = new TableRowSorter<TableModel>(table.getModel());
table.setRowSorter(rowSorter);
// Sort by date descending.
List<TableRowSorter.SortKey> sortKeys = new ArrayList<TableRowSorter.SortKey>();
sortKeys.add(new TableRowSorter.SortKey(1, SortOrder.DESCENDING));
rowSorter.setSortKeys(sortKeys);
rowSorter.setComparator(1, dateComparator);
rowSorter.setComparator(2, comparatorNumber);
scrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPaneSetup();
showTransactionDetailsAction.setEnabled(table.getSelectedRow() > -1);
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.weightx = 1;
constraints.weighty = 1;
transactionsPanel.add(scrollPane, constraints);
return transactionsPanel;
}
private void justifyColumnHeaders() {
TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer();
JLabel label = (JLabel) renderer;
label.setHorizontalAlignment(JLabel.CENTER);
}
private JPanel createTxDetailButtonPanel() {
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
buttonPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, SystemColor.windowBorder));
buttonPanel.setOpaque(true);
buttonPanel.setBackground(Themes.currentTheme.detailPanelBackground());
buttonPanel.setComponentOrientation(ComponentOrientation.getOrientation(LocaliserUtils.getLocale()));
showTransactionDetailsAction = new ShowTransactionDetailsAction(this);
showTransactionsButton = Buttons.newLaunchBrowserButton(showTransactionDetailsAction
, MessageKey.TRANSCATION);
showTransactionsButton.setEnabled(false);
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = 1;
constraints.gridy = 0;
constraints.weightx = 0.1;
constraints.weighty = 1.0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.anchor = GridBagConstraints.LINE_START;
buttonPanel.add(showTransactionsButton, constraints);
btnAddress = Buttons.newLaunchBrowserButton(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
try {
String url = BitherUrl.BLOCKCHAIN_INFO_ADDRESS_URL + Bither.getActionAddress().getAddress();
ViewUtil.openURI(new URI(url));
} catch (URISyntaxException ie) {
ie.printStackTrace();
}
}
}, MessageKey.ADDRESS);
if (Bither.getActionAddress() == null) {
btnAddress.setEnabled(false);
}
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = 2;
constraints.gridy = 0;
constraints.weightx = 0.1;
constraints.weighty = 1.0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.anchor = GridBagConstraints.LINE_END;
buttonPanel.add(btnAddress, constraints);
// JPanel fill1 = new JPanel();
// fill1.setOpaque(false);
// constraints.fill = GridBagConstraints.HORIZONTAL;
// constraints.gridx = 3;
// constraints.gridy = 0;
// constraints.weightx = 200;
// constraints.weighty = 1;
// constraints.gridwidth = 1;
// constraints.gridheight = 1;
// constraints.anchor = GridBagConstraints.CENTER;
// buttonPanel.add(fill1, constraints);
return buttonPanel;
}
private void scrollPaneSetup() {
scrollPane.setBackground(Themes.currentTheme.detailPanelBackground());
scrollPane.getViewport().setBackground(Themes.currentTheme.detailPanelBackground());
scrollPane.setComponentOrientation(ComponentOrientation.getOrientation(LocaliserUtils.getLocale()));
scrollPane.getHorizontalScrollBar().setUnitIncrement(BitherSetting.SCROLL_INCREMENT);
scrollPane.getVerticalScrollBar().setUnitIncrement(BitherSetting.SCROLL_INCREMENT);
scrollPane.setOpaque(true);
scrollPane.getViewport().setOpaque(true);
scrollPane.setBorder(BorderFactory.createMatteBorder(1, 1, 0, 1, SystemColor.windowBorder));
ScrollBarUIDecorator.apply(scrollPane, false);
}
@Override
public void displayView(DisplayHint displayHint) {
if (AddressManager.getInstance().getAllAddresses().size() == 0 &&
AddressManager.getInstance().getHdAccount() == null) {
showTransactionHeaderForm.setVisible(false);
} else {
showTransactionHeaderForm.setVisible(true);
}
justifyColumnHeaders();
scrollPaneSetup();
// Amount decimal aligned
DecimalAlignRenderer decimalAlignRenderer = new DecimalAlignRenderer(ShowTransactionsForm.this);
table.getColumnModel().getColumn(2).setCellRenderer(decimalAlignRenderer);
showTransactionHeaderForm.updateUI();
refreshTx();
if (selectedRow > -1 && selectedRow < table.getRowCount()) {
table.setRowSelectionInterval(selectedRow, selectedRow);
}
}
public void setSelectedRow(int row) {
selectedRow = row;
}
public Tx getSelectedRowData() {
int row = table.getSelectedRow();
return txTableModel.getRow(rowSorter.convertRowIndexToModel(row));
}
public JTable getTable() {
return table;
}
@Override
public ViewEnum getViewId() {
return ViewEnum.TRANSACTIONS_VIEW;
}
@Override
public void notificatTx(String address, Tx tx, Tx.TxNotificationType txNotificationType, long deltaBalance) {
String actionAddress = "";
if (Bither.getActionAddress() != null) {
actionAddress = Bither.getActionAddress().getAddress();
}
if (Utils.compareString(address, actionAddress)) {
displayView(DisplayHint.WALLET_TRANSACTIONS_HAVE_CHANGED);
}
}
@Override
public void blockChange() {
displayView(DisplayHint.WALLET_TRANSACTIONS_HAVE_CHANGED);
}
@Override
public JPanel getPanel() {
return panelMain;
}
{
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
$$$setupUI$$$();
}
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* @noinspection ALL
*/
private void $$$setupUI$$$() {
panelMain = new JPanel();
panelMain.setLayout(new BorderLayout(0, 0));
}
/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return panelMain;
}
class SharedListSelectionHandler implements ListSelectionListener {
private Action showTransactionDetailsAction;
SharedListSelectionHandler(Action action) {
this.showTransactionDetailsAction = action;
}
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel lsm = (ListSelectionModel) e.getSource();
if (lsm.isSelectionEmpty()) {
showTransactionDetailsAction.setEnabled(false);
showTransactionsButton.invalidate();
showTransactionsButton.validate();
showTransactionsButton.repaint();
} else {
// Find out which indexes are selected.
int minIndex = lsm.getMinSelectionIndex();
int maxIndex = lsm.getMaxSelectionIndex();
for (int i = minIndex; i <= maxIndex; i++) {
if (lsm.isSelectedIndex(i)) {
showTransactionDetailsAction.setEnabled(true);
showTransactionsButton.invalidate();
showTransactionsButton.validate();
showTransactionsButton.repaint();
break;
}
}
}
}
}
Comparator<Date> dateComparator = new Comparator<Date>() {
@Override
public int compare(Date o1, Date o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1;
}
} else {
if (o2 == null) {
return -1;
}
}
long n1 = o1.getTime();
long n2 = o2.getTime();
if (n1 == 0) {
// Object 1 has missing date.
return 1;
}
if (n2 == 0) {
// Object 2 has missing date.
return -1;
}
if (n1 < n2) {
return -1;
} else if (n1 > n2) {
return 1;
} else {
return 0;
}
}
};
Comparator<String> comparatorNumber = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
try {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1;
}
} else {
if (o2 == null) {
return -1;
}
}
DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(LocaliserUtils.getLocale());
formatter.setParseBigDecimal(true);
// Convert spaces to non breakable space.
o1 = o1.replaceAll(" ", "\u00A0");
o2 = o2.replaceAll(" ", "\u00A0");
BigDecimal parsedO1 = (BigDecimal) formatter.parse(o1);
BigDecimal parsedO2 = (BigDecimal) formatter.parse(o2);
return parsedO1.compareTo(parsedO2);
} catch (NumberFormatException nfe) {
return o1.compareTo(o2);
} catch (ParseException e) {
return o1.compareTo(o2);
}
}
};
}