/**
* Copyright 2012 multibit.org
*
* Licensed under the MIT license (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://opensource.org/licenses/mit-license.php
*
* 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 org.multibit.viewsystem.swing.view.panels;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionConfidence;
import com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
import org.multibit.MultiBit;
import org.multibit.controller.Controller;
import org.multibit.controller.bitcoin.BitcoinController;
import org.multibit.exchange.CurrencyConverter;
import org.multibit.exchange.CurrencyConverterListener;
import org.multibit.exchange.ExchangeRate;
import org.multibit.model.bitcoin.WalletTableData;
import org.multibit.model.core.CoreModel;
import org.multibit.utils.DateUtils;
import org.multibit.utils.ImageLoader;
import org.multibit.viewsystem.DisplayHint;
import org.multibit.viewsystem.View;
import org.multibit.viewsystem.Viewable;
import org.multibit.viewsystem.swing.ColorAndFontConstants;
import org.multibit.viewsystem.swing.MultiBitFrame;
import org.multibit.viewsystem.swing.UpdateTransactionsTimerTask;
import org.multibit.viewsystem.swing.WalletTableModel;
import org.multibit.viewsystem.swing.action.ExportTransactionsSubmitAction;
import org.multibit.viewsystem.swing.action.HelpContextAction;
import org.multibit.viewsystem.swing.action.ShowTransactionDetailsAction;
import org.multibit.viewsystem.swing.view.components.FontSizer;
import org.multibit.viewsystem.swing.view.components.HelpButton;
import org.multibit.viewsystem.swing.view.components.MultiBitButton;
import org.multibit.viewsystem.swing.view.components.MultiBitLabel;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.*;
import javax.swing.text.*;
import java.awt.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.Timer;
public class ShowTransactionsPanel extends JPanel implements Viewable, CurrencyConverterListener {
private static final long serialVersionUID = 1235108897887842662L;
private final Controller controller;
private final BitcoinController bitcoinController;
private MultiBitFrame mainFrame;
private JTable table;
private WalletTableModel walletTableModel;
private TableRowSorter<TableModel> rowSorter;
private static final String SPACER = " "; // 3 spaces
private static final int STATUS_WIDTH_DELTA = 6;
private static final int TABLE_BORDER = 3;
private static final int MINIMUM_ICON_HEIGHT = 16;
public static final int HEIGHT_DELTA = 3;
public static final String PROGRESS_0_ICON_FILE = "/images/circleProgress0.png";
private static final String PROGRESS_1_ICON_FILE = "/images/circleProgress1.png";
private static final String PROGRESS_2_ICON_FILE = "/images/circleProgress2.png";
private static final String PROGRESS_3_ICON_FILE = "/images/circleProgress3.png";
private static final String PROGRESS_4_ICON_FILE = "/images/circleProgress4.png";
private static final String PROGRESS_5_ICON_FILE = "/images/circleProgress5.png";
private static final String RTL_PROGRESS_1_ICON_FILE = "/images/circleProgress1.png";
private static final String RTL_PROGRESS_2_ICON_FILE = "/images/circleProgress2.png";
private static final String RTL_PROGRESS_3_ICON_FILE = "/images/circleProgress3.png";
private static final String RTL_PROGRESS_4_ICON_FILE = "/images/circleProgress4.png";
private static final String RTL_PROGRESS_5_ICON_FILE = "/images/circleProgress5.png";
private static final String TICK_ICON_FILE = "/images/tick.png";
private static final String PICKAXE_ICON_FILE = "/images/pickaxe.png";
private static final String SMALL_EXCLAMATION_MARK_ICON_FILE = "/images/smallExclamationMark.png";
private ListSelectionModel listSelectionModel;
private int selectedRow = -1;
private Action showTransactionDetailsAction;
private MultiBitButton showTransactionsButton;
private Action exportTransactionsSubmitAction;
private MultiBitButton exportTransactionsButton;
public static final int UPDATE_TRANSACTIONS_DELAY_TIME = 1000; // milliseconds
private JScrollPane scrollPane;
/**
* Timer used to condense multiple updates
*/
private static Timer updateTransactionsTimer;
private static UpdateTransactionsTimerTask updateTransactionsTimerTask;
public ShowTransactionsPanel(BitcoinController bitcoinController, MultiBitFrame mainFrame) {
this.bitcoinController = bitcoinController;
this.controller = this.bitcoinController;
this.mainFrame = mainFrame;
updateTransactionsTimerTask = new UpdateTransactionsTimerTask(controller, this, mainFrame);
updateTransactionsTimer = new Timer();
updateTransactionsTimer.scheduleAtFixedRate(updateTransactionsTimerTask, UPDATE_TRANSACTIONS_DELAY_TIME, UPDATE_TRANSACTIONS_DELAY_TIME);
initUI();
applyComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()));
CurrencyConverter.INSTANCE.addCurrencyConverterListener(this);
}
private void initUI() {
setLayout(new BorderLayout());
JPanel buttonPanel = createButtonPanel();
JPanel transactionsPanel = createTransactionsPanel();
add(transactionsPanel, BorderLayout.CENTER);
buttonPanel.setMinimumSize(new Dimension(60, 60));
add(buttonPanel, BorderLayout.SOUTH);
}
public static void updateTransactions() {
if (updateTransactionsTimerTask != null) {
updateTransactionsTimerTask.setUpdateTransactions(true);
}
}
private JPanel createTransactionsPanel() {
JPanel transactionsPanel = new JPanel();
transactionsPanel.setMinimumSize(new Dimension(550, 160));
transactionsPanel.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
transactionsPanel.setLayout(new GridBagLayout());
transactionsPanel.setOpaque(true);
GridBagConstraints constraints = new GridBagConstraints();
walletTableModel = new WalletTableModel(bitcoinController);
table = new JTable(walletTableModel);
table.setOpaque(false);
table.setBorder(BorderFactory.createEmptyBorder());
table.setComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()));
table.setRowHeight(Math.max(MINIMUM_ICON_HEIGHT, getFontMetrics(FontSizer.INSTANCE.getAdjustedDefaultFont()).getHeight()) + HEIGHT_DELTA);
// Use status icons.
table.getColumnModel().getColumn(0).setCellRenderer(new ImageRenderer());
table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
table.setRowSelectionAllowed(true);
table.setColumnSelectionAllowed(false);
table.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
// 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());
// Justify column headers.
justifyColumnHeaders();
// Description leading justified (set explicitly as it does not seem to work otherwise).
if (ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()).isLeftToRight()) {
table.getColumnModel().getColumn(2).setCellRenderer(new LeadingJustifiedRenderer());
} else {
table.getColumnModel().getColumn(2).setCellRenderer(new TrailingJustifiedStringRenderer());
}
// Amount decimal aligned
DecimalAlignRenderer decimalAlignRenderer = new DecimalAlignRenderer();
table.getColumnModel().getColumn(3).setCellRenderer(decimalAlignRenderer);
FontMetrics fontMetrics = getFontMetrics(FontSizer.INSTANCE.getAdjustedDefaultFont());
TableColumn tableColumn = table.getColumnModel().getColumn(0); // status
int statusWidth = fontMetrics.stringWidth(controller.getLocaliser().getString("walletData.statusText"));
tableColumn.setPreferredWidth(statusWidth + STATUS_WIDTH_DELTA);
tableColumn = table.getColumnModel().getColumn(1); // Date.
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd MMM yyyy HH:mm", controller.getLocaliser().getLocale());
int dateWidth = Math.max(fontMetrics.stringWidth(controller.getLocaliser().getString("walletData.dateText")),
fontMetrics.stringWidth(dateFormatter.format(new Date(DateUtils.nowUtc().getMillis()))));
tableColumn.setPreferredWidth(dateWidth);
tableColumn = table.getColumnModel().getColumn(2); // Description.
tableColumn.setPreferredWidth(250);
tableColumn = table.getColumnModel().getColumn(3); // Amount (BTC).
int amountBTCWidth = Math.max(fontMetrics.stringWidth(controller.getLocaliser().getString("sendBitcoinPanel.amountLabel") + " (BTC)"),
fontMetrics.stringWidth("00000.000000000"));
tableColumn.setPreferredWidth(amountBTCWidth);
tableColumn.setMinWidth(amountBTCWidth);
if (CurrencyConverter.INSTANCE.isShowingFiat()) {
tableColumn = table.getColumnModel().getColumn(4); // Amount (fiat).
int amountFiatWidth = Math.max(fontMetrics.stringWidth(controller.getLocaliser().getString("sendBitcoinPanel.amountLabel") + " (USD)"),
fontMetrics.stringWidth("000.0000"));
tableColumn.setPreferredWidth(amountFiatWidth);
table.getColumnModel().getColumn(4).setCellRenderer(new TrailingJustifiedNumericRenderer());
}
// 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);
Comparator<Date> comparator = 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;
}
}
};
rowSorter.setComparator(1, comparator);
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(controller.getLocaliser().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);
}
}
};
rowSorter.setComparator(3, comparatorNumber);
if (CurrencyConverter.INSTANCE.isShowingFiat()) {
rowSorter.setComparator(4, comparatorNumber);
}
scrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
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);
table.getTableHeader().setFont(FontSizer.INSTANCE.getAdjustedDefaultFont());
}
private JPanel createButtonPanel() {
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(ColorAndFontConstants.MID_BACKGROUND_COLOR);
buttonPanel.setComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()));
Action helpAction;
if (ComponentOrientation.LEFT_TO_RIGHT == ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())) {
helpAction = new HelpContextAction(controller, ImageLoader.HELP_CONTENTS_BIG_ICON_FILE,
"multiBitFrame.helpMenuText", "multiBitFrame.helpMenuTooltip", "multiBitFrame.helpMenuText",
HelpContentsPanel.HELP_TRANSACTIONS_URL);
} else {
helpAction = new HelpContextAction(controller, ImageLoader.HELP_CONTENTS_BIG_RTL_ICON_FILE,
"multiBitFrame.helpMenuText", "multiBitFrame.helpMenuTooltip", "multiBitFrame.helpMenuText",
HelpContentsPanel.HELP_TRANSACTIONS_URL);
}
HelpButton helpButton = new HelpButton(helpAction, controller);
helpButton.setText("");
helpButton.setToolTipText(controller.getLocaliser().getString("multiBitFrame.helpMenuTooltip"));
helpButton.setHorizontalAlignment(SwingConstants.LEADING);
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.weightx = 0.3;
constraints.weighty = 0.1;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.anchor = GridBagConstraints.LINE_START;
buttonPanel.add(helpButton, constraints);
showTransactionDetailsAction = new ShowTransactionDetailsAction(bitcoinController, mainFrame, this);
showTransactionsButton = new MultiBitButton(showTransactionDetailsAction, controller);
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);
exportTransactionsSubmitAction = new ExportTransactionsSubmitAction(bitcoinController, mainFrame);
exportTransactionsButton = new MultiBitButton(exportTransactionsSubmitAction, controller);
showTransactionsButton.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_START;
buttonPanel.add(exportTransactionsButton, 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.LINE_END;
buttonPanel.add(fill1, constraints);
return buttonPanel;
}
private void scrollPaneSetup() {
scrollPane.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
scrollPane.getViewport().setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
scrollPane.setComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()));
scrollPane.getHorizontalScrollBar().setUnitIncrement(CoreModel.SCROLL_INCREMENT);
scrollPane.getVerticalScrollBar().setUnitIncrement(CoreModel.SCROLL_INCREMENT);
scrollPane.setOpaque(true);
scrollPane.getViewport().setOpaque(true);
scrollPane.setBorder(BorderFactory.createMatteBorder(1, 1, 0, 1, SystemColor.windowBorder));
}
@Override
public void displayView(DisplayHint displayHint) {
//log.debug("ShowTransactionsPanel#displayView called on panel " + System.identityHashCode(this) + " for wallet " + controller.getModel().getActiveWalletFilename());
if (this.bitcoinController.getModel().getActiveWallet() == null) {
return;
}
justifyColumnHeaders();
scrollPaneSetup();
// Amount decimal aligned
DecimalAlignRenderer decimalAlignRenderer = new DecimalAlignRenderer();
table.getColumnModel().getColumn(3).setCellRenderer(decimalAlignRenderer);
walletTableModel.recreateWalletData();
if (selectedRow > -1 && selectedRow < table.getRowCount()) {
table.setRowSelectionInterval(selectedRow, selectedRow);
}
}
@Override
public void navigateAwayFromView() {
}
class ImageRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 154545L;
JLabel primaryLabel = new JLabel();
// If the component is a doubleIcon the next fields are used.
JLabel extraLabel = new JLabel();
boolean doubleIcon = false;
JPanel combinationPanel = new JPanel();
ImageIcon shapeTriangleIcon = ImageLoader.createImageIcon(ImageLoader.SHAPE_TRIANGLE_ICON_FILE);
ImageIcon shapeSquareIcon = ImageLoader.createImageIcon(ImageLoader.SHAPE_SQUARE_ICON_FILE);
ImageIcon shapeHeptagonIcon = ImageLoader.createImageIcon(ImageLoader.SHAPE_PENTAGON_ICON_FILE);
ImageIcon shapeHexagonIcon = ImageLoader.createImageIcon(ImageLoader.SHAPE_HEXAGON_ICON_FILE);
ImageIcon pickaxeIcon = ImageLoader.createImageIcon(PICKAXE_ICON_FILE);
ImageIcon smallExclamationMarkIcon = ImageLoader.createImageIcon(SMALL_EXCLAMATION_MARK_ICON_FILE);
ImageIcon tickIcon = ImageLoader.createImageIcon(TICK_ICON_FILE);
ImageIcon progress0Icon = ImageLoader.createImageIcon(PROGRESS_0_ICON_FILE);
ImageIcon progress1Icon = ImageLoader.createImageIcon(PROGRESS_1_ICON_FILE);
ImageIcon progress2Icon = ImageLoader.createImageIcon(PROGRESS_2_ICON_FILE);
ImageIcon progress3Icon = ImageLoader.createImageIcon(PROGRESS_3_ICON_FILE);
ImageIcon progress4Icon = ImageLoader.createImageIcon(PROGRESS_4_ICON_FILE);
ImageIcon progress5Icon = ImageLoader.createImageIcon(PROGRESS_5_ICON_FILE);
ImageIcon rtlProgress1Icon = ImageLoader.createImageIcon(RTL_PROGRESS_1_ICON_FILE);
ImageIcon rtlProgress2Icon = ImageLoader.createImageIcon(RTL_PROGRESS_2_ICON_FILE);
ImageIcon rtlProgress3Icon = ImageLoader.createImageIcon(RTL_PROGRESS_3_ICON_FILE);
ImageIcon rtlProgress4Icon = ImageLoader.createImageIcon(RTL_PROGRESS_4_ICON_FILE);
ImageIcon rtlProgress5Icon = ImageLoader.createImageIcon(RTL_PROGRESS_5_ICON_FILE);
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
// Prepare the primary icon (used always), and an extra icon and containing panel for use as required.
primaryLabel.setHorizontalAlignment(SwingConstants.CENTER);
primaryLabel.setVerticalAlignment(SwingConstants.CENTER);
primaryLabel.setOpaque(true);
extraLabel.setHorizontalAlignment(SwingConstants.CENTER);
extraLabel.setVerticalAlignment(SwingConstants.CENTER);
extraLabel.setOpaque(true);
combinationPanel.setOpaque(true);
combinationPanel.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
// Prepare a double icon panel for use as required.
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.weightx = 1;
constraints.weighty = 1;
constraints.anchor = GridBagConstraints.LINE_END;
combinationPanel.add(primaryLabel, constraints);
constraints.fill = GridBagConstraints.NONE;
constraints.gridx = 1;
constraints.gridy = 0;
constraints.weightx = 1;
constraints.weighty = 1;
constraints.anchor = GridBagConstraints.LINE_START;
combinationPanel.add(extraLabel, constraints);
// Get the transaction and transaction confidence
Transaction transaction = (Transaction)value;
TransactionConfidence confidence = null;
if (transaction != null) {
confidence = transaction.getConfidence();
}
ConfidenceType confidenceType = null;
if (confidence != null) {
confidenceType = confidence.getConfidenceType();
}
if (confidenceType == null) {
confidenceType = ConfidenceType.UNKNOWN;
}
// Coinbase transactions have an extra pickaxe icon.
if (transaction != null && transaction.isCoinBase()) {
extraLabel.setIcon(pickaxeIcon);
doubleIcon = true;
} else {
doubleIcon = false;
}
// Work out the primary icon.
switch (confidenceType) {
case UNKNOWN: {
primaryLabel.setText("?");
primaryLabel.setIcon(null);
break;
}
case BUILDING: {
if (bitcoinController.getMultiBitService().getChain() == null) {
primaryLabel.setText("?");
primaryLabel.setIcon(null);
} else {
int numberOfBlocksEmbedded = bitcoinController.getMultiBitService().getChain().getBestChainHeight() - confidence.getAppearedAtChainHeight() + 1;
if (transaction != null && transaction.isCoinBase()) {
// Coinbase tx mature slower than regular blocks.
numberOfBlocksEmbedded = numberOfBlocksEmbedded / 20;
}
ImageIcon buildingIcon = getBuildingIcon(numberOfBlocksEmbedded, transaction);
primaryLabel.setIcon(buildingIcon);
primaryLabel.setText("");
if (numberOfBlocksEmbedded >= 6) {
primaryLabel.setToolTipText(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("multiBitFrame.status.isConfirmed")));
} else {
if (transaction != null && transaction.isCoinBase()) {
primaryLabel.setToolTipText(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("multiBitFrame.status.beingConfirmedAndCoinbase")));
} else {
primaryLabel.setToolTipText(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("multiBitFrame.status.beingConfirmed")));
}
}
}
break;
}
case PENDING: {
primaryLabel.setIcon(getConfidenceIcon(confidence));
primaryLabel.setText("");
primaryLabel.setToolTipText(HelpContentsPanel.createTooltipText(getUnconfirmedConfidenceToolTip(transaction)));
if (transaction != null) {
if (transaction.getLockTime() > 0) {
extraLabel.setIcon(smallExclamationMarkIcon);
doubleIcon = true;
} else {
doubleIcon = false;
}
}
break;
}
case DEAD: {
primaryLabel.setIcon(smallExclamationMarkIcon);
primaryLabel.setText(controller.getLocaliser().getString("multiBitFrame.status.dead"));
break;
}
default: {
primaryLabel.setIcon(null);
primaryLabel.setText("?");
break;
}
}
// Propagate the tooltip text.
extraLabel.setToolTipText(primaryLabel.getToolTipText());
combinationPanel.setToolTipText(primaryLabel.getToolTipText());
// Set foreground and background colors.
if (isSelected) {
selectedRow = row;
primaryLabel.setBackground(table.getSelectionBackground());
primaryLabel.setForeground(table.getSelectionForeground());
extraLabel.setBackground(table.getSelectionBackground());
extraLabel.setForeground(table.getSelectionForeground());
combinationPanel.setBackground(table.getSelectionBackground());
} else {
primaryLabel.setForeground(table.getForeground());
extraLabel.setForeground(table.getForeground());
combinationPanel.setForeground(table.getForeground());
if (row % 2 == 1) {
primaryLabel.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
extraLabel.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
combinationPanel.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
} else {
primaryLabel.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
extraLabel.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
combinationPanel.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
primaryLabel.setOpaque(true);
extraLabel.setOpaque(true);
combinationPanel.setOpaque(true);
}
}
// Return either a single icon or a double icon panel.
if (doubleIcon) {
return combinationPanel;
} else {
return primaryLabel;
}
}
private ImageIcon getBuildingIcon(int numberOfBlocksEmbedded, Transaction transaction) {
TransactionConfidence confidence = null;
if (transaction != null) {
confidence = transaction.getConfidence();
}
if (numberOfBlocksEmbedded < 0) {
numberOfBlocksEmbedded = 0;
}
if (numberOfBlocksEmbedded > 6) {
numberOfBlocksEmbedded = 6;
}
boolean isLeftToRight = ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()).isLeftToRight();
switch (numberOfBlocksEmbedded) {
case 0: {
return getConfidenceIcon(confidence);
}
case 1: {
if (isLeftToRight) {
return progress1Icon;
} else {
return rtlProgress1Icon;
}
}
case 2: {
if (isLeftToRight) {
return progress2Icon;
} else {
return rtlProgress2Icon;
}
}
case 3: {
if (isLeftToRight) {
return progress3Icon;
} else {
return rtlProgress3Icon;
}
}
case 4: {
if (isLeftToRight) {
return progress4Icon;
} else {
return rtlProgress4Icon;
}
}
case 5: {
if (isLeftToRight) {
return progress5Icon;
} else {
return rtlProgress5Icon;
}
}
case 6: {
return tickIcon;
}
default:
return getConfidenceIcon(confidence);
}
}
private String getUnconfirmedConfidenceToolTip(Transaction transaction) {
TransactionConfidence confidence = null;
if (transaction != null) {
confidence = transaction.getConfidence();
}
// Work out the line describing the is the transaction is standard or not.
String transactionTrustfulness = "";
if (transaction != null) {
if (transaction.getLockTime() > 0) {
// Non standard transaction.
transactionTrustfulness = MultiBit.getController().getLocaliser().getString("multiBitFrame.status.notConfirmedAndNotStandard") + ".";
} else {
// Normal transaction.
if (transaction.isCoinBase()) {
transactionTrustfulness = MultiBit.getController().getLocaliser().getString("multiBitFrame.status.notConfirmedAndCoinbase") + ".";
} else {
transactionTrustfulness = MultiBit.getController().getLocaliser().getString("multiBitFrame.status.notConfirmed") + ".";
}
}
}
// Work out the line describing the number of peers.
int peers = 0;
if (confidence != null) {
peers = confidence.getBroadcastByCount();
}
StringBuilder builder = new StringBuilder();
if (peers == 0) {
builder.append(MultiBit.getController().getLocaliser()
.getString("transactionConfidence.seenByUnknownNumberOfPeers"));
} else {
builder
.append(MultiBit.getController().getLocaliser().getString("transactionConfidence.seenBy"))
.append(" ");
builder.append(peers);
if (peers > 1)
builder
.append(" ")
.append(MultiBit.getController().getLocaliser().getString("transactionConfidence.peers"))
.append(". ");
else
builder
.append(" ")
.append(MultiBit.getController().getLocaliser().getString("transactionConfidence.peer"))
.append(". ");
}
return HelpContentsPanel.createMultilineTooltipText(new String[] {
transactionTrustfulness, builder.toString() });
}
private ImageIcon getConfidenceIcon(TransactionConfidence confidence) {
// By default return a triangle which indicates the least known.
ImageIcon iconToReturn = shapeTriangleIcon;
if (confidence != null) {
if (confidence.getConfidenceType() == ConfidenceType.BUILDING) {
return progress0Icon;
}
if (confidence.getBroadcastBy() != null) {
int numberOfPeers = confidence.getBroadcastByCount();
if (numberOfPeers >= 4) {
return progress0Icon;
} else {
switch (numberOfPeers) {
case 0 : iconToReturn = shapeTriangleIcon; break;
case 1 : iconToReturn = shapeSquareIcon; break;
case 2 : iconToReturn = shapeHeptagonIcon; break;
case 3 : iconToReturn = shapeHexagonIcon; break;
default:
iconToReturn = shapeTriangleIcon;
}
}
}
}
return iconToReturn;
}
}
class TrailingJustifiedNumericRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1549545L;
MultiBitLabel label;
public TrailingJustifiedNumericRenderer() {
label = new MultiBitLabel("");
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
label.setHorizontalAlignment(SwingConstants.TRAILING);
label.setOpaque(true);
label.setBorder(new EmptyBorder(new Insets(0, TABLE_BORDER, 1, TABLE_BORDER)));
label.setText(value + SPACER);
if ((value + "").contains("-")) {
// Debit.
if (isSelected) {
label.setForeground(table.getSelectionForeground());
} else {
label.setForeground(ColorAndFontConstants.DEBIT_FOREGROUND_COLOR);
}
} else {
// Credit.
if (isSelected) {
label.setForeground(table.getSelectionForeground());
} else {
label.setForeground(ColorAndFontConstants.CREDIT_FOREGROUND_COLOR);
}
}
if (isSelected) {
selectedRow = row;
label.setBackground(table.getSelectionBackground());
} else {
if (row % 2 == 1) {
label.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
} else {
label.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
label.setOpaque(true);
}
}
return label;
}
}
class TrailingJustifiedStringRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1549545L;
MultiBitLabel label;
public TrailingJustifiedStringRenderer() {
label = new MultiBitLabel("");
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
label.setHorizontalAlignment(SwingConstants.TRAILING);
label.setOpaque(true);
label.setBorder(new EmptyBorder(new Insets(0, TABLE_BORDER, 1, TABLE_BORDER)));
label.setText(value + SPACER);
if (isSelected) {
label.setForeground(table.getSelectionForeground());
} else {
label.setForeground(Color.BLACK);
}
if (isSelected) {
selectedRow = row;
label.setBackground(table.getSelectionBackground());
} else {
if (row % 2 == 1) {
label.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
} else {
label.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
label.setOpaque(true);
}
}
return label;
}
}
class TrailingJustifiedDateRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1549545L;
SimpleDateFormat dateFormatter;
MultiBitLabel label;
public TrailingJustifiedDateRenderer() {
label = new MultiBitLabel("");
dateFormatter = new SimpleDateFormat("dd MMM yyyy HH:mm", controller.getLocaliser().getLocale());
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
label.setHorizontalAlignment(SwingConstants.TRAILING);
label.setOpaque(true);
label.setBorder(new EmptyBorder(new Insets(0, TABLE_BORDER, 1, TABLE_BORDER)));
String formattedDate = "";
if (value != null) {
if (value instanceof Date) {
if (((Date) value).getTime() != 0) {
try {
formattedDate = dateFormatter.format(value);
} catch (IllegalArgumentException iae) {
// ok
}
}
} else {
formattedDate = value.toString();
}
}
label.setText(formattedDate + SPACER);
if (isSelected) {
selectedRow = row;
label.setBackground(table.getSelectionBackground());
label.setForeground(table.getSelectionForeground());
} else {
label.setForeground(table.getForeground());
if (row % 2 == 1) {
label.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
} else {
label.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
label.setOpaque(true);
}
}
return label;
}
}
class LeadingJustifiedRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1549545L;
MultiBitLabel label;
public LeadingJustifiedRenderer() {
label = new MultiBitLabel("");
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
label.setHorizontalAlignment(SwingConstants.LEADING);
label.setOpaque(true);
label.setBorder(new EmptyBorder(new Insets(0, TABLE_BORDER, 1, TABLE_BORDER)));
label.setText((String) value);
if (isSelected) {
selectedRow = row;
label.setBackground(table.getSelectionBackground());
label.setForeground(table.getSelectionForeground());
} else {
label.setForeground(table.getForeground());
if (row % 2 == 1) {
label.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
} else {
label.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
label.setOpaque(true);
}
}
return label;
}
}
class DecimalAlignRenderer implements TableCellRenderer {
private final TabStop tabStopRight = new TabStop(40, TabStop.ALIGN_RIGHT, TabStop.LEAD_NONE);
private final TabStop tabStopLeft = new TabStop(41, TabStop.ALIGN_LEFT, TabStop.LEAD_NONE);
private final TabSet tabSet = new TabSet(new TabStop[] { tabStopRight, tabStopLeft });
private AttributeSet paragraphAttributeSet;
private JTextPane pane;
private Style style;
public DecimalAlignRenderer() {
pane = new JTextPane();
StyleContext styleContext = StyleContext.getDefaultStyleContext();
paragraphAttributeSet = styleContext.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.TabSet, tabSet);
pane.setParagraphAttributes(paragraphAttributeSet, true);
style = pane.addStyle("number", null);
pane.setOpaque(true);
pane.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR));
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
JPanel outerPanel = new JPanel(new BorderLayout());
outerPanel.setOpaque(true);
outerPanel.setBorder(BorderFactory.createEmptyBorder());
JLabel filler = new JLabel();
filler.setOpaque(true);
if (value == null) {
pane.setText("\t" + controller.getLocaliser().bitcoinValueToString(BigInteger.ZERO, false, false));
} else {
String contents = value.toString();
String splitChar;
String[] split;
if (controller.getLocaliser().getDecimalFormatSymbols().getDecimalSeparator() == ',') {
// , as decimal point
splitChar = ",";
split = contents.split(",");
} else {
// . as decimal point
splitChar = ".";
split = contents.split("\\.");
}
if (split == null) {
pane.setText("");
} else if (split.length == 1) {
// Integer amount - no decimal point. Add a space to pad it
// left.
pane.setText("\t" + split[0] + " ");
} else {
pane.setText("\t" + split[0] + splitChar + "\t" + split[1] + " ");
}
// log.debug("pane.getText = " + pane.getText());
}
if ((value.toString()).contains("-")) {
// debit
if (isSelected) {
pane.setForeground(table.getSelectionForeground());
} else {
pane.setForeground(ColorAndFontConstants.DEBIT_FOREGROUND_COLOR);
}
} else {
// credit
if (isSelected) {
pane.setForeground(table.getSelectionForeground());
} else {
pane.setForeground(ColorAndFontConstants.CREDIT_FOREGROUND_COLOR);
}
}
if (isSelected) {
selectedRow = row;
pane.setBackground(table.getSelectionBackground());
outerPanel.setBackground(table.getSelectionBackground());
filler.setBackground(table.getSelectionBackground());
pane.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, table.getSelectionBackground()));
} else {
if (row % 2 == 1) {
pane.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
pane.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR));
outerPanel.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
filler.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR);
outerPanel.setOpaque(true);
} else {
pane.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
pane.setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, ColorAndFontConstants.ALTERNATE_TABLE_COLOR));
outerPanel.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
filler.setBackground(ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
pane.setOpaque(true);
outerPanel.setOpaque(true);
filler.setOpaque(true);
}
}
StyleConstants.setForeground(style, pane.getForeground());
if (row % 2 == 1 || isSelected) {
StyleConstants.setBackground(style, pane.getBackground());
} else {
StyleConstants.setBackground(style, ColorAndFontConstants.ALTERNATE_TABLE_COLOR);
}
StyleConstants.setBold(style, FontSizer.INSTANCE.getAdjustedDefaultFont().isBold());
StyleConstants.setItalic(style, FontSizer.INSTANCE.getAdjustedDefaultFont().isItalic());
StyleConstants.setFontSize(style, FontSizer.INSTANCE.getAdjustedDefaultFont().getSize());
StyleConstants.setFontFamily(style, FontSizer.INSTANCE.getAdjustedDefaultFont().getFontName());
StyleConstants.setSpaceBelow(style, 10);
pane.getStyledDocument().setCharacterAttributes(0, pane.getText().length(), pane.getStyle("number"), true);
outerPanel.add(pane, BorderLayout.LINE_START);
outerPanel.add(filler, BorderLayout.CENTER);
// Avoid flicker by doing layout.
outerPanel.doLayout();
return outerPanel;
}
}
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;
}
}
}
}
}
public WalletTableData getSelectedRowData() {
int row = table.getSelectedRow();
return walletTableModel.getRow(rowSorter.convertRowIndexToModel(row));
}
public JTable getTable() {
return table;
}
@Override
public Icon getViewIcon() {
return ImageLoader.createImageIcon(ImageLoader.TRANSACTIONS_ICON_FILE);
}
@Override
public String getViewTitle() {
return controller.getLocaliser().getString("showTransactionsAction.text");
}
@Override
public String getViewTooltip() {
return controller.getLocaliser().getString("showTransactionsAction.tooltip");
}
@Override
public View getViewId() {
return View.TRANSACTIONS_VIEW;
}
@Override
public void lostExchangeRate(ExchangeRate exchangeRate) {
}
@Override
public void foundExchangeRate(ExchangeRate exchangeRate) {
initUI();
}
@Override
public void updatedExchangeRate(ExchangeRate exchangeRate) {
ShowTransactionsPanel.updateTransactions();
}
}