/** * 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; import com.google.bitcoin.core.ECKey; import com.google.bitcoin.core.Sha256Hash; import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.Wallet; import com.google.bitcoin.script.Script; import org.bitcoinj.wallet.Protos.Wallet.EncryptionType; import org.joda.money.Money; import org.multibit.Localiser; import org.multibit.controller.Controller; import org.multibit.controller.bitcoin.BitcoinController; import org.multibit.controller.core.CoreController; import org.multibit.controller.exchange.ExchangeController; import org.multibit.exchange.CurrencyConverter; import org.multibit.exchange.CurrencyConverterListener; import org.multibit.exchange.ExchangeRate; import org.multibit.exchange.TickerTimerTask; import org.multibit.message.Message; import org.multibit.message.MessageManager; import org.multibit.model.bitcoin.WalletBusyListener; import org.multibit.model.bitcoin.WalletData; import org.multibit.model.core.StatusEnum; import org.multibit.model.exchange.ExchangeModel; import org.multibit.network.ReplayManager; import org.multibit.platform.GenericApplication; import org.multibit.platform.listener.GenericQuitEventListener; import org.multibit.platform.listener.GenericQuitResponse; import org.multibit.store.MultiBitWalletVersion; import org.multibit.utils.ImageLoader; import org.multibit.viewsystem.DisplayHint; import org.multibit.viewsystem.View; import org.multibit.viewsystem.ViewSystem; import org.multibit.viewsystem.Viewable; import org.multibit.viewsystem.swing.action.*; import org.multibit.viewsystem.swing.view.ViewFactory; import org.multibit.viewsystem.swing.view.components.*; import org.multibit.viewsystem.swing.view.panels.HelpContentsPanel; import org.multibit.viewsystem.swing.view.panels.SendBitcoinConfirmPanel; import org.multibit.viewsystem.swing.view.panels.ShowTransactionsPanel; import org.multibit.viewsystem.swing.view.ticker.TickerTablePanel; import org.multibit.viewsystem.swing.view.walletlist.SingleWalletPanel; import org.multibit.viewsystem.swing.view.walletlist.WalletListPanel; import org.simplericity.macify.eawt.ApplicationEvent; import org.simplericity.macify.eawt.ApplicationListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import javax.swing.plaf.basic.BasicSplitPaneDivider; import javax.swing.text.DefaultEditorKit; import java.awt.*; import java.awt.event.*; import java.awt.font.TextAttribute; import java.math.BigInteger; import java.util.List; import java.util.Map; import java.util.Timer; /* * JFrame displaying Swing version of MultiBit */ public class MultiBitFrame extends JFrame implements ViewSystem, ApplicationListener, WalletBusyListener, CurrencyConverterListener { private static final Logger log = LoggerFactory.getLogger(MultiBitFrame.class); private static final double PROPORTION_OF_VERTICAL_SCREEN_TO_FILL = 0.75D; private static final double PROPORTION_OF_HORIZONTAL_SCREEN_TO_FILL = 0.82D; public static final String EXAMPLE_LONG_FIELD_TEXT = "1JiM1UyTGqpLqgayxTPbWbcdVeoepmY6pK++++"; public static final String EXAMPLE_MEDIUM_FIELD_TEXT = "Typical text 00.12345678 BTC (000.01 XYZ)"; public static final int WALLET_WIDTH_DELTA = 25; public static final int SCROLL_BAR_DELTA = 20; public static final int WIDTH_OF_SPLIT_PANE_DIVIDER = 9; public static final int MENU_HORIZONTAL_INSET = 8; public static final int MENU_VERTICAL_INSET = 1; public static final int BALANCE_SPACER = 7; public static final String SPENDABLE_TEXT_IN_ENGLISH = "Spendable"; private StatusBar statusBar; private StatusEnum online = StatusEnum.CONNECTING; public static final String SEPARATOR = " - "; private static final long serialVersionUID = 7621813615342923041L; private final Controller controller; private final CoreController coreController; private final BitcoinController bitcoinController; private final ExchangeController exchangeController; private Localiser localiser; private String helpContext; public String getHelpContext() { return helpContext; } @Override public void setHelpContext(String helpContext) { this.helpContext = helpContext; } private MultiBitLabel estimatedBalanceLabelLabel; private BlinkLabel estimatedBalanceBTCLabel; private BlinkLabel estimatedBalanceFiatLabel; public BlinkLabel getEstimatedBalanceBTCLabel() { return estimatedBalanceBTCLabel; } public BlinkLabel getEstimatedBalanceFiatLabel() { return estimatedBalanceFiatLabel; } private HelpButton availableBalanceLabelButton; private HelpButton availableBalanceBTCButton; private HelpButton availableBalanceFiatButton; /** * list of wallets shown in left hand column */ private WalletListPanel walletsView; private JSplitPane splitPane; private static final int TOOLTIP_DISMISSAL_DELAY = 12000; // millisecs /** * Provide the Application reference during construction */ private final GenericApplication application; private final GenericQuitResponse multiBitFrameQuitResponse = new GenericQuitResponse() { @Override public void cancelQuit() { log.debug("Quit Canceled"); } @Override public void performQuit() { log.debug("Performed Quit"); } }; final private GenericQuitEventListener quitEventListener; /** * the tabbed pane containing the views */ private MultiBitTabbedPane viewTabbedPane; public Logger logger = LoggerFactory.getLogger(MultiBitFrame.class.getName()); private ViewFactory viewFactory; private Timer fileChangeTimer; private HealthCheckTimerTask healthCheckTimerTask; private Timer tickerTimer1; private Timer tickerTimer2; private TickerTimerTask tickerTimerTask1; private TickerTimerTask tickerTimerTask2; private JPanel headerPanel; private TickerTablePanel tickerTablePanel; private MultiBitWalletBusyAction addPasswordAction; private MultiBitWalletBusyAction changePasswordAction; private MultiBitWalletBusyAction removePasswordAction; private MultiBitWalletBusyAction signMessageAction; private MultiBitWalletBusyAction showImportPrivateKeysAction; private MultiBitWalletBusyAction showExportPrivateKeysAction; private MultiBitWalletBusyAction showCheckPrivateKeysAction; private MultiBitWalletBusyAction resetTransactionsAction; /** * For events coming from Peers condense the events into regular updates. * This is to prevent the UI thrashing with hundreds of events per second. */ public static final int FIRE_DATA_CHANGED_UPDATE_LATER_DELAY_TIME = 1000; // milliseconds /** * Timer used to condense multiple updates */ private static Timer fireDataChangedTimer; private static FireDataChangedTimerTask fireDataChangedTimerTask; @SuppressWarnings("deprecation") public MultiBitFrame(CoreController coreController, BitcoinController bitcoinController, ExchangeController exchangeController, GenericApplication application, View initialView) { this.coreController = coreController; this.bitcoinController = bitcoinController; this.exchangeController = exchangeController; this.controller = this.coreController; this.quitEventListener = this.coreController; this.localiser = controller.getLocaliser(); this.application = application; // Remap to command v and C on a Mac if (application != null && application.isMac()) { InputMap im = (InputMap) UIManager.get("TextField.focusInputMap"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), DefaultEditorKit.copyAction); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.META_DOWN_MASK), DefaultEditorKit.cutAction); } ColorAndFontConstants.init(); FontSizer.INSTANCE.initialise(controller); UIManager.put("ToolTip.font", FontSizer.INSTANCE.getAdjustedDefaultFont()); setCursor(Cursor.WAIT_CURSOR); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); String titleText = localiser.getString("multiBitFrame.title"); if (this.bitcoinController.getModel().getActiveWallet() != null) { titleText = titleText + SEPARATOR + this.bitcoinController.getModel().getActivePerWalletModelData().getWalletDescription() + SEPARATOR + this.bitcoinController.getModel().getActivePerWalletModelData().getWalletFilename(); } setTitle(titleText); ToolTipManager.sharedInstance().setDismissDelay(TOOLTIP_DISMISSAL_DELAY); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent arg0) { quitEventListener.onQuitEvent(null, multiBitFrameQuitResponse); } }); applyComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())); sizeAndCenter(); viewFactory = new ViewFactory(this.bitcoinController, this.exchangeController, this); initUI(initialView); this.bitcoinController.registerWalletBusyListener(this); // Initialise the file change timer. fileChangeTimer = new Timer(); healthCheckTimerTask = new HealthCheckTimerTask(this.bitcoinController); fileChangeTimer.schedule(healthCheckTimerTask, HealthCheckTimerTask.INITIAL_DELAY, HealthCheckTimerTask.DEFAULT_REPEAT_RATE); // Initialise the tickers. tickerTimer1 = new Timer(); tickerTimerTask1 = new TickerTimerTask(this.exchangeController, this, true); tickerTimer1.schedule(tickerTimerTask1, TickerTimerTask.INITIAL_DELAY, TickerTimerTask.DEFAULT_REPEAT_RATE); tickerTimer2 = new Timer(); tickerTimerTask2 = new TickerTimerTask(this.exchangeController, this, false); tickerTimer2.schedule(tickerTimerTask2, TickerTimerTask.INITIAL_DELAY + TickerTimerTask.TASK_SEPARATION, TickerTimerTask.DEFAULT_REPEAT_RATE); // Initialise status bar. statusBar.initialise(); estimatedBalanceLabelLabel.setFocusable(false); estimatedBalanceBTCLabel.setFocusable(false); estimatedBalanceFiatLabel.setFocusable(false); availableBalanceLabelButton.setFocusable(false); availableBalanceBTCButton.setFocusable(false); availableBalanceFiatButton.setFocusable(false); updateHeader(); calculateDividerPosition(); MultiBitTabbedPane.setEnableUpdates(true); CurrencyConverter.INSTANCE.addCurrencyConverterListener(this); displayView(null != initialView ? initialView : View.DEFAULT_VIEW()); pack(); setVisible(true); fireDataChangedTimerTask = new FireDataChangedTimerTask(this); fireDataChangedTimer = new Timer(); fireDataChangedTimer.scheduleAtFixedRate(fireDataChangedTimerTask, FIRE_DATA_CHANGED_UPDATE_LATER_DELAY_TIME, FIRE_DATA_CHANGED_UPDATE_LATER_DELAY_TIME); } public GenericApplication getApplication() { return application; } private void sizeAndCenter() { // Get the screen size as a java dimension. Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int height = (int) (screenSize.height * PROPORTION_OF_VERTICAL_SCREEN_TO_FILL); int width = (int) (screenSize.width * PROPORTION_OF_HORIZONTAL_SCREEN_TO_FILL); // Set the jframe height and width. setPreferredSize(new Dimension(width, height)); double startVerticalPositionRatio = (1 - PROPORTION_OF_VERTICAL_SCREEN_TO_FILL) / 2; double startHorizontalPositionRatio = (1 - PROPORTION_OF_HORIZONTAL_SCREEN_TO_FILL) / 2; setLocation((int) (width * startHorizontalPositionRatio), (int) (height * startVerticalPositionRatio)); } private void initUI(View initialView) { Container contentPane = getContentPane(); contentPane.setLayout(new GridBagLayout()); contentPane.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR); GridBagConstraints constraints = new GridBagConstraints(); GridBagConstraints constraints2 = new GridBagConstraints(); // Set the application icon. ImageIcon imageIcon = ImageLoader.createImageIcon(ImageLoader.MULTIBIT_ICON_FILE); if (imageIcon != null) { setIconImage(imageIcon.getImage()); } headerPanel = new JPanel(); headerPanel.setOpaque(true); headerPanel.setBackground(ColorAndFontConstants.BACKGROUND_COLOR); headerPanel.setLayout(new GridBagLayout()); headerPanel.applyComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())); JPanel balancePanel = createBalancePanel(); constraints2.fill = GridBagConstraints.BOTH; constraints2.gridx = 0; constraints2.gridy = 0; constraints2.gridwidth = 1; constraints2.gridheight = 1; constraints2.weightx = 1.0; constraints2.weighty = 1.0; constraints2.anchor = GridBagConstraints.LINE_START; headerPanel.add(balancePanel, constraints2); addMenuBar(constraints, contentPane); constraints.fill = GridBagConstraints.BOTH; constraints.gridx = 0; constraints.gridy = 0; constraints.gridwidth = 2; constraints.weightx = 1.0; constraints.weighty = 1.0; constraints.anchor = GridBagConstraints.LINE_START; contentPane.add(headerPanel, constraints); // Create the wallet list panel. walletsView = new WalletListPanel(this.bitcoinController, this); // Create the tabbedpane that holds the views. viewTabbedPane = new MultiBitTabbedPane(controller); viewTabbedPane.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR); // Add the send bitcoin tab. JPanel sendBitcoinOutlinePanel = new JPanel(new BorderLayout()); Viewable sendBitcoinView = viewFactory.getView(View.SEND_BITCOIN_VIEW); sendBitcoinOutlinePanel.add((JPanel) sendBitcoinView, BorderLayout.CENTER); viewTabbedPane.addTab(sendBitcoinView.getViewTitle(), sendBitcoinView.getViewIcon(), sendBitcoinView.getViewTooltip(), sendBitcoinOutlinePanel); // Add the receive bitcoin tab. JPanel receiveBitcoinOutlinePanel = new JPanel(new BorderLayout()); Viewable receiveBitcoinView = viewFactory.getView(View.RECEIVE_BITCOIN_VIEW); receiveBitcoinOutlinePanel.add((JPanel) receiveBitcoinView, BorderLayout.CENTER); viewTabbedPane.addTab(receiveBitcoinView.getViewTitle(), receiveBitcoinView.getViewIcon(), receiveBitcoinView.getViewTooltip(), receiveBitcoinOutlinePanel); // Add the transactions tab. JPanel transactionsOutlinePanel = new JPanel(new BorderLayout()); Viewable transactionsView = viewFactory.getView(View.TRANSACTIONS_VIEW); transactionsOutlinePanel.add((JPanel) transactionsView, BorderLayout.CENTER); viewTabbedPane.addTab(transactionsView.getViewTitle(), transactionsView.getViewIcon(), transactionsView.getViewTooltip(), transactionsOutlinePanel); if (initialView == View.SEND_BITCOIN_VIEW) { viewTabbedPane.setSelectedIndex(0); } else if (initialView == View.RECEIVE_BITCOIN_VIEW) { viewTabbedPane.setSelectedIndex(1); } else if (initialView == View.TRANSACTIONS_VIEW) { viewTabbedPane.setSelectedIndex(2); } // Create a split pane with the two scroll panes in it. if (ComponentOrientation.LEFT_TO_RIGHT == ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())) { splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, walletsView, viewTabbedPane); } else { splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, viewTabbedPane, walletsView); splitPane.setResizeWeight(1.0); } splitPane.setOneTouchExpandable(false); splitPane.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, SystemColor.windowBorder)); splitPane.setBackground(ColorAndFontConstants.VERY_LIGHT_BACKGROUND_COLOR); splitPane.setOpaque(true); BasicSplitPaneDivider divider = ((javax.swing.plaf.basic.BasicSplitPaneUI) splitPane.getUI()).getDivider(); divider.setDividerSize(WIDTH_OF_SPLIT_PANE_DIVIDER); constraints.fill = GridBagConstraints.BOTH; constraints.gridx = 0; constraints.gridy = 1; constraints.gridwidth = 2; constraints.gridheight = 1; constraints.weightx = 1.0; constraints.weighty = 1000.0; constraints.gridwidth = 1; constraints.anchor = GridBagConstraints.LINE_START; contentPane.add(splitPane, constraints); calculateDividerPosition(); // Cannot get the RTL wallets drawing nicely so switch off adjustment. splitPane.setEnabled(ComponentOrientation.LEFT_TO_RIGHT.equals(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()))); statusBar = new StatusBar(this.bitcoinController, this); statusBar.updateOnlineStatusText(online); MessageManager.INSTANCE.addMessageListener(statusBar); constraints.fill = GridBagConstraints.BOTH; constraints.gridx = 0; constraints.gridy = 2; constraints.weightx = 1; constraints.weighty = 0.1; constraints.gridwidth = 2; contentPane.add(statusBar, constraints); } private JPanel createBalancePanel() { // Change the 'available to spend' text in English to 'Spendable'. // This is not in the localisation files as it is a synonym and I did not want the localisers to have to reenter their text. String spendableText; if (controller.getLocaliser().getLocale().getLanguage().equals("en")) { spendableText = SPENDABLE_TEXT_IN_ENGLISH; } else { spendableText = controller.getLocaliser().getString("multiBitFrame.availableToSpend2"); } FontMetrics fontMetrics = this.getFontMetrics(FontSizer.INSTANCE.getAdjustedDefaultFont()); int spendableWidth = fontMetrics.stringWidth(spendableText); int spendableHeight = fontMetrics.getHeight(); tickerTablePanel = new TickerTablePanel(this, this.exchangeController); JPanel headerPanel = new JPanel(); int heightOfBalances = 2 * spendableHeight + 3 * BALANCE_SPACER - 2 + 3 * ColorAndFontConstants.MULTIBIT_LARGE_FONT_INCREASE; int heightOfHeaderToUse = Math.max(heightOfBalances, tickerTablePanel.getIdealHeight()); headerPanel.setMinimumSize(new Dimension(700, heightOfHeaderToUse)); headerPanel.setPreferredSize(new Dimension(700, heightOfHeaderToUse)); headerPanel.setOpaque(true); headerPanel.setBackground(ColorAndFontConstants.MID_BACKGROUND_COLOR); headerPanel.applyComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())); headerPanel.setLayout(new GridBagLayout()); headerPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, SystemColor.windowBorder)); GridBagConstraints constraints = new GridBagConstraints(); // Top row filler. constraints.gridx = 0; constraints.gridy = 0; constraints.weightx = 0.01; constraints.weighty = 0.6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(MultiBitTitledPanel.createStent(BALANCE_SPACER, BALANCE_SPACER), constraints); constraints.fill = GridBagConstraints.NONE; constraints.gridx = 3; constraints.gridy = 0; constraints.weightx = 0.01; constraints.weighty = 0.6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; int stentWidth = Math.max(spendableWidth, fontMetrics.stringWidth(controller.getLocaliser().getString("multiBitFrame.balanceLabel"))); headerPanel.add(MultiBitTitledPanel.createStent(stentWidth, 1), constraints); estimatedBalanceLabelLabel = new MultiBitLabel(controller.getLocaliser().getString("multiBitFrame.balanceLabel"), JTextField.RIGHT); estimatedBalanceLabelLabel.setToolTipText(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("multiBitFrame.balanceLabel.tooltip"))); estimatedBalanceLabelLabel.setFont(FontSizer.INSTANCE.getAdjustedDefaultFontWithDelta(3 * ColorAndFontConstants.MULTIBIT_LARGE_FONT_INCREASE)); constraints.gridx = 3; constraints.gridy = 1; constraints.weightx = 0.6; constraints.weighty = 0.4; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_END; headerPanel.add(estimatedBalanceLabelLabel, constraints); headerPanel.add(MultiBitTitledPanel.createStent(spendableWidth, spendableHeight), constraints); constraints.gridx = 4; constraints.gridy = 0; constraints.weightx = 0.01; constraints.weighty = 0.6; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(MultiBitTitledPanel.createStent(BALANCE_SPACER), constraints); constraints.gridx = 0; constraints.gridy = 2; constraints.weightx = 0.01; constraints.weighty = 0.1; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(MultiBitTitledPanel.createStent(BALANCE_SPACER, BALANCE_SPACER - 2), constraints); estimatedBalanceBTCLabel = new BlinkLabel(controller, true); estimatedBalanceBTCLabel.setToolTipText(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("multiBitFrame.balanceLabel.tooltip"))); estimatedBalanceBTCLabel.setBorder(BorderFactory.createEmptyBorder()); constraints.gridx = 5; constraints.gridy = 1; constraints.weightx = 0.6; constraints.weighty = 0.6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(estimatedBalanceBTCLabel, constraints); estimatedBalanceFiatLabel = new BlinkLabel(controller, true); estimatedBalanceFiatLabel.setToolTipText(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("multiBitFrame.balanceLabel.tooltip"))); estimatedBalanceFiatLabel.setBorder(BorderFactory.createEmptyBorder()); constraints.gridx = 6; constraints.gridy = 0; constraints.weightx = 0.01; constraints.weighty = 0.6; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(MultiBitTitledPanel.createStent(12), constraints); constraints.gridx = 7; constraints.gridy = 1; constraints.weightx = 0.6; constraints.weighty = 0.6; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(estimatedBalanceFiatLabel, constraints); Action availableBalanceHelpAction = new HelpContextAction(controller, null, "multiBitFrame.availableToSpend2", "multiBitFrame.availableToSpend.tooltip", "multiBitFrame.helpMenuText", HelpContentsPanel.HELP_AVAILABLE_TO_SPEND_URL); if (controller.getLocaliser().getLocale().getLanguage().equals("en")) { availableBalanceHelpAction.putValue(Action.NAME, SPENDABLE_TEXT_IN_ENGLISH); } availableBalanceLabelButton = new HelpButton(availableBalanceHelpAction, controller); availableBalanceLabelButton.setHorizontalAlignment(JLabel.RIGHT); availableBalanceLabelButton.setBorder(BorderFactory.createEmptyBorder()); String tooltipText = HelpContentsPanel.createMultilineTooltipText(new String[]{ controller.getLocaliser().getString("multiBitFrame.availableToSpend.tooltip"), "\n", controller.getLocaliser().getString("multiBitFrame.helpMenuTooltip")}); availableBalanceLabelButton.setToolTipText(tooltipText); availableBalanceLabelButton.setBorder(BorderFactory.createEmptyBorder()); constraints.gridx = 3; constraints.gridy = 3; constraints.weightx = 0.6; constraints.weighty = 0.4; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_END; headerPanel.add(availableBalanceLabelButton, constraints); headerPanel.add(MultiBitTitledPanel.createStent(spendableWidth, spendableHeight), constraints); constraints.gridx = 5; constraints.gridy = 3; constraints.weightx = 0.6; constraints.weighty = 0.01; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; availableBalanceBTCButton = new HelpButton(availableBalanceHelpAction, controller); availableBalanceBTCButton.setBorder(BorderFactory.createEmptyBorder()); availableBalanceBTCButton.setToolTipText(tooltipText); headerPanel.add(availableBalanceBTCButton, constraints); constraints.gridx = 7; constraints.gridy = 3; constraints.weightx = 0.6; constraints.weighty = 0.01; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; availableBalanceFiatButton = new HelpButton(availableBalanceHelpAction, controller); availableBalanceFiatButton.setBorder(BorderFactory.createEmptyBorder()); availableBalanceFiatButton.setToolTipText(tooltipText); //availableBalanceFiatButton.setBorder(BorderFactory.createLineBorder(Color.RED)); headerPanel.add(availableBalanceFiatButton, constraints); // Bottom Filler constraints.gridx = 0; constraints.gridy = 4; constraints.weightx = 0.01; constraints.weighty = 10; constraints.fill = GridBagConstraints.BOTH; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(MultiBitTitledPanel.createStent(BALANCE_SPACER, BALANCE_SPACER), constraints); JPanel forcerBottom = new JPanel(); forcerBottom.setOpaque(false); //forcerBottom.setBorder(BorderFactory.createLineBorder(Color.CYAN)); constraints.fill = GridBagConstraints.VERTICAL; constraints.gridx = 0; constraints.gridy = 4; constraints.weightx = 0.01; constraints.weighty = 1000; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(forcerBottom, constraints); JPanel forcer1 = new JPanel(); forcer1.setOpaque(false); //forcer1.setBorder(BorderFactory.createLineBorder(Color.CYAN)); constraints.fill = GridBagConstraints.BOTH; constraints.gridx = 8; constraints.gridy = 2; constraints.weightx = 1000; constraints.weighty = 1000; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_END; headerPanel.add(forcer1, constraints); JPanel forcer2 = new JPanel(); forcer2.setOpaque(false); //forcer2.setBorder(BorderFactory.createLineBorder(Color.YELLOW)); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 8; constraints.gridy = 1; constraints.weightx = 10000; constraints.weighty = 0.01; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_END; headerPanel.add(forcer2, constraints); // Initially invisible. availableBalanceLabelButton.setVisible(false); availableBalanceLabelButton.setEnabled(false); availableBalanceBTCButton.setVisible(false); availableBalanceBTCButton.setEnabled(false); availableBalanceFiatButton.setVisible(false); availableBalanceFiatButton.setEnabled(false); JPanel filler3 = new JPanel(); filler3.setOpaque(false); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 8; constraints.gridy = 0; constraints.weightx = 1000; constraints.weighty = 1.0; constraints.gridwidth = 1; constraints.gridheight = 2; constraints.anchor = GridBagConstraints.LINE_START; headerPanel.add(filler3, constraints); // Add the link suggesting users try MultiBit HD JPanel tryMultiBitHDPanel = new JPanel(); tryMultiBitHDPanel.setOpaque(false); LaunchTryMultiBitHDAction launchTryMultiBitHDAction = new LaunchTryMultiBitHDAction(controller, "multiBitFrame.tryMultiBitHD", "multiBitFrame.tryMultiBitHD.tooltip"); JButton tryMultiBitHDButton = new JButton(launchTryMultiBitHDAction); tryMultiBitHDButton.setBorderPainted(false); tryMultiBitHDButton.setOpaque(false); tryMultiBitHDButton.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); MouseListener l = new MouseAdapter() { Font original; @Override public void mouseEntered(MouseEvent e) { original = e.getComponent().getFont(); Map attributes = original.getAttributes(); attributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); e.getComponent().setFont(original.deriveFont(attributes)); } @Override public void mouseExited(MouseEvent e) { e.getComponent().setFont(original); } }; tryMultiBitHDButton.addMouseListener(l); tryMultiBitHDPanel.add(tryMultiBitHDButton); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 9; constraints.gridy = 0; constraints.weightx = 10; constraints.weighty = 10; constraints.gridwidth = 1; constraints.gridheight = 5; headerPanel.add(tryMultiBitHDPanel, constraints); // Add ticker panel. constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 10; constraints.gridy = 0; constraints.weightx = 10; constraints.weighty = 10000; constraints.gridwidth = 1; constraints.gridheight = 5; constraints.anchor = GridBagConstraints.CENTER; headerPanel.add(tickerTablePanel, constraints); // Add a little stent to keep it off the right hand edge. int stent = 6; // A reasonable default. Insets tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets"); if (tabAreaInsets != null) { stent = tabAreaInsets.right; } constraints.fill = GridBagConstraints.HORIZONTAL; constraints.gridx = 11; constraints.gridy = 0; constraints.weightx = 1; constraints.weighty = 1; constraints.anchor = GridBagConstraints.BASELINE_TRAILING; headerPanel.add(MultiBitTitledPanel.createStent(stent), constraints); return headerPanel; } /** * @param constraints * @param contentPane */ private void addMenuBar(GridBagConstraints constraints, Container contentPane) { ComponentOrientation componentOrientation = ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()); // Create the menu bar. JMenuBar menuBar = new JMenuBar(); menuBar.setComponentOrientation(componentOrientation); menuBar.setOpaque(false); MnemonicUtil mnemonicUtil = new MnemonicUtil(controller.getLocaliser()); // Build the File menu. JMenu fileMenu = new JMenu(localiser.getString("multiBitFrame.fileMenuText")); fileMenu.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); fileMenu.setComponentOrientation(componentOrientation); fileMenu.setMnemonic(mnemonicUtil.getMnemonic("multiBitFrame.fileMenuMnemonic")); tweakAppearance(fileMenu); menuBar.add(fileMenu); // Build the Trade menu. JMenu tradeMenu = new JMenu(localiser.getString("multiBitFrame.tradeMenuText")); tradeMenu.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); tradeMenu.setComponentOrientation(componentOrientation); tradeMenu.setMnemonic(mnemonicUtil.getMnemonic("multiBitFrame.tradeMenuMnemonic")); tweakAppearance(tradeMenu); menuBar.add(tradeMenu); // Build the View menu. JMenu viewMenu = new JMenu(localiser.getString("multiBitFrame.viewMenuText")); viewMenu.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); viewMenu.setComponentOrientation(componentOrientation); viewMenu.setMnemonic(mnemonicUtil.getMnemonic("multiBitFrame.viewMenuMnemonic")); tweakAppearance(viewMenu); menuBar.add(viewMenu); // Build the Tools menu. JMenu toolsMenu = new JMenu(localiser.getString("multiBitFrame.toolsMenuText")); toolsMenu.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); toolsMenu.setComponentOrientation(componentOrientation); toolsMenu.setMnemonic(mnemonicUtil.getMnemonic("multiBitFrame.toolsMenuMnemonic")); tweakAppearance(toolsMenu); menuBar.add(toolsMenu); // Build the Help menu. JMenu helpMenu = new JMenu(localiser.getString("multiBitFrame.helpMenuText")); helpMenu.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); helpMenu.setComponentOrientation(componentOrientation); helpMenu.setMnemonic(mnemonicUtil.getMnemonic("multiBitFrame.helpMenuMnemonic")); tweakAppearance(helpMenu); menuBar.add(helpMenu); // Create new wallet action. CreateWalletSubmitAction createNewWalletAction = new CreateWalletSubmitAction(this.bitcoinController, ImageLoader.createImageIcon(ImageLoader.CREATE_NEW_ICON_FILE), this); JMenuItem menuItem = new JMenuItem(createNewWalletAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); // Open wallet action. OpenWalletAction openWalletAction = new OpenWalletAction(this.bitcoinController, ImageLoader.createImageIcon(ImageLoader.OPEN_WALLET_ICON_FILE), this); menuItem = new JMenuItem(openWalletAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); CloseWalletAction closeWalletAction = new CloseWalletAction(this.bitcoinController, ImageLoader.createImageIcon(ImageLoader.CLOSE_WALLET_ICON_FILE), this); menuItem = new JMenuItem(closeWalletAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); fileMenu.addSeparator(); // Add password action. addPasswordAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.ADD_PASSWORD_ICON_FILE, "addPasswordAction.text", "addPasswordAction.tooltip", "addPasswordAction.mnemonic", View.ADD_PASSWORD_VIEW); menuItem = new JMenuItem(addPasswordAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); // Change password action. changePasswordAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.CHANGE_PASSWORD_ICON_FILE, "changePasswordAction.text", "changePasswordAction.tooltip", "changePasswordAction.mnemonic", View.CHANGE_PASSWORD_VIEW); menuItem = new JMenuItem(changePasswordAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); // Remove password action. removePasswordAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.REMOVE_PASSWORD_ICON_FILE, "removePasswordAction.text", "removePasswordAction.tooltip", "removePasswordAction.mnemonic", View.REMOVE_PASSWORD_VIEW); menuItem = new JMenuItem(removePasswordAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); // Exit action. if (application != null && !application.isMac()) { // Non Macs have an Exit Menu item. fileMenu.addSeparator(); { AbstractAction exitAction = new AbstractExitAction(this.controller) { @Override public void actionPerformed(ActionEvent e) { quitEventListener.onQuitEvent(null, multiBitFrameQuitResponse); } }; menuItem = new JMenuItem(exitAction); } menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); fileMenu.add(menuItem); } // Show welcome action. MultiBitAction showWelcomeAction = new MultiBitAction(controller, ImageLoader.WELCOME_ICON_FILE, "welcomePanel.text", "welcomePanel.title", "welcomePanel.mnemonic", View.WELCOME_VIEW); menuItem = new JMenuItem(showWelcomeAction); showWelcomeAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("welcomePanel.title"))); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); helpMenu.add(menuItem); MultiBitAction showHelpContentsAction; if (ComponentOrientation.LEFT_TO_RIGHT == ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())) { showHelpContentsAction = new MultiBitAction(controller, ImageLoader.HELP_CONTENTS_ICON_FILE, "showHelpContentsAction.text", "showHelpContentsAction.tooltip", "showHelpContentsAction.mnemonic", View.HELP_CONTENTS_VIEW); } else { showHelpContentsAction = new MultiBitAction(controller, ImageLoader.HELP_CONTENTS_RTL_ICON_FILE, "showHelpContentsAction.text", "showHelpContentsAction.tooltip", "showHelpContentsAction.mnemonic", View.HELP_CONTENTS_VIEW); } showHelpContentsAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("showHelpContentsAction.tooltip"))); menuItem = new JMenuItem(showHelpContentsAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); helpMenu.add(menuItem); if (application != null && !application.isMac()) { // Non Macs have a Help About menu item. MultiBitAction helpAboutAction = new MultiBitAction(controller, ImageLoader.MULTIBIT_SMALL_ICON_FILE, "helpAboutAction.text", "helpAboutAction.tooltip", "helpAboutAction.mnemonic", View.HELP_ABOUT_VIEW); helpAboutAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("helpAboutAction.tooltip"))); menuItem = new JMenuItem(helpAboutAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); helpMenu.add(menuItem); } // View Transactions action. MultiBitAction showTransactionsAction = new MultiBitAction(controller, ImageLoader.TRANSACTIONS_ICON_FILE, "showTransactionsAction.text", "showTransactionsAction.tooltip", "showTransactionsAction.mnemonic", View.TRANSACTIONS_VIEW); showTransactionsAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("showTransactionsAction.tooltip"))); menuItem = new JMenuItem(showTransactionsAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); viewMenu.add(menuItem); // View Charts action. MultiBitAction showChartsAction = new MultiBitAction(controller, ImageLoader.CHART_LINE_ICON_FILE, "chartsPanelAction.text", "chartsPanelAction.tooltip", "chartsPanelAction.mnemonic", View.CHARTS_VIEW); showChartsAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("chartsPanelAction.tooltip"))); menuItem = new JMenuItem(showChartsAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); viewMenu.add(menuItem); // Show messages action. MultiBitAction showMessagesAction = new MultiBitAction(controller, ImageLoader.MESSAGES_ICON_FILE, "messagesPanel.text", "messagesPanel.tooltip", "messagesPanel.mnemonic", View.MESSAGES_VIEW); showMessagesAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("messagesPanel.tooltip"))); menuItem = new JMenuItem(showMessagesAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); viewMenu.add(menuItem); // Send bitcoin action. MultiBitAction sendBitcoinAction = new MultiBitAction(controller, ImageLoader.SEND_BITCOIN_ICON_FILE, "sendBitcoinAction.text", "sendBitcoinAction.tooltip", "sendBitcoinAction.mnemonic", View.SEND_BITCOIN_VIEW); sendBitcoinAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("sendBitcoinAction.tooltip"))); menuItem = new JMenuItem(sendBitcoinAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); tradeMenu.add(menuItem); MultiBitAction receiveBitcoinAction = new MultiBitAction(controller, ImageLoader.RECEIVE_BITCOIN_ICON_FILE, "receiveBitcoinAction.text", "receiveBitcoinAction.tooltip", "receiveBitcoinAction.mnemonic", View.RECEIVE_BITCOIN_VIEW); receiveBitcoinAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("receiveBitcoinAction.tooltip"))); menuItem = new JMenuItem(receiveBitcoinAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); tradeMenu.add(menuItem); // Show preferences. if (application != null && !application.isMac()) { // Non Macs have a Preferences menu item. MultiBitAction showPreferencesAction = new MultiBitAction(controller, ImageLoader.PREFERENCES_ICON_FILE, "showPreferencesAction.text", "showPreferencesAction.tooltip", "showPreferencesAction.mnemonic", View.PREFERENCES_VIEW); showPreferencesAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("showPreferencesAction.tooltip"))); menuItem = new JMenuItem(showPreferencesAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); viewMenu.add(menuItem); } viewMenu.addSeparator(); // Show ticker. String viewTicker = controller.getModel().getUserPreference(ExchangeModel.TICKER_SHOW); boolean isTickerVisible = !Boolean.FALSE.toString().equals(viewTicker); String tickerKey; if (isTickerVisible) { tickerKey = "multiBitFrame.ticker.hide.text"; } else { tickerKey = "multiBitFrame.ticker.show.text"; } final JMenuItem showTicker = new JMenuItem(controller.getLocaliser().getString(tickerKey)); showTicker.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); showTicker.setComponentOrientation(componentOrientation); showTicker.setIcon(ImageLoader.createImageIcon(ImageLoader.MONEY_ICON_FILE)); if (tickerTablePanel != null) { tickerTablePanel.setVisible(isTickerVisible); } final MultiBitFrame thisFrame = this; showTicker.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { if (tickerTablePanel != null) { if (tickerTablePanel.isVisible()) { tickerTablePanel.setVisible(false); controller.getModel().setUserPreference(ExchangeModel.TICKER_SHOW, Boolean.FALSE.toString()); showTicker.setText(controller.getLocaliser().getString("multiBitFrame.ticker.show.text")); } else { tickerTablePanel.setVisible(true); controller.getModel().setUserPreference(ExchangeModel.TICKER_SHOW, Boolean.TRUE.toString()); showTicker.setText(controller.getLocaliser().getString("multiBitFrame.ticker.hide.text")); // Cancel any existing timer. if (tickerTimer1 != null) { tickerTimer1.cancel(); } if (tickerTimer2 != null) { tickerTimer2.cancel(); } // Start ticker timer. tickerTimer1 = new Timer(); tickerTimer1.schedule(new TickerTimerTask(exchangeController, thisFrame, true), 0, TickerTimerTask.DEFAULT_REPEAT_RATE); boolean showSecondRow = Boolean.TRUE.toString().equals( controller.getModel().getUserPreference(ExchangeModel.TICKER_SHOW_SECOND_ROW)); if (showSecondRow) { tickerTimer2 = new Timer(); tickerTimer2.schedule(new TickerTimerTask(exchangeController, thisFrame, false), TickerTimerTask.TASK_SEPARATION, TickerTimerTask.DEFAULT_REPEAT_RATE); } } } } }); viewMenu.add(showTicker); // Sign message. signMessageAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.MESSAGE_SIGN_ICON_FILE, "signMessageAction.text", "signMessageAction.tooltip", "signMessageAction.mnemonic", View.SIGN_MESSAGE_VIEW); menuItem = new JMenuItem(signMessageAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); toolsMenu.add(menuItem); // Verify message. MultiBitAction verifyMessageAction = new MultiBitAction(this.bitcoinController, ImageLoader.MESSAGE_VERIFY_ICON_FILE, "verifyMessageAction.text", "verifyMessageAction.tooltip", "verifyMessageAction.mnemonic", View.VERIFY_MESSAGE_VIEW); verifyMessageAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("verifyMessageAction.tooltip"))); menuItem = new JMenuItem(verifyMessageAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); toolsMenu.add(menuItem); toolsMenu.addSeparator(); // Import private keys. showImportPrivateKeysAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.IMPORT_PRIVATE_KEYS_ICON_FILE, "showImportPrivateKeysAction.text", "showImportPrivateKeysAction.tooltip", "showImportPrivateKeysAction.mnemonic", View.SHOW_IMPORT_PRIVATE_KEYS_VIEW); menuItem = new JMenuItem(showImportPrivateKeysAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); toolsMenu.add(menuItem); // Export private keys. showExportPrivateKeysAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.EXPORT_PRIVATE_KEYS_ICON_FILE, "showExportPrivateKeysAction.text", "showExportPrivateKeysAction.tooltip", "showExportPrivateKeysAction.mnemonic", View.SHOW_EXPORT_PRIVATE_KEYS_VIEW); menuItem = new JMenuItem(showExportPrivateKeysAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); toolsMenu.add(menuItem); // Check private keys. showCheckPrivateKeysAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.CHECK_PRIVATE_KEYS_ICON_FILE, "showCheckPrivateKeysAction.text", "showCheckPrivateKeysAction.tooltip", "showCheckPrivateKeysAction.mnemonic", View.SHOW_CHECK_PRIVATE_KEYS_VIEW); menuItem = new JMenuItem(showCheckPrivateKeysAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); toolsMenu.add(menuItem); toolsMenu.addSeparator(); resetTransactionsAction = new MultiBitWalletBusyAction(this.bitcoinController, ImageLoader.RESET_TRANSACTIONS_ICON_FILE, "resetTransactionsAction.text", "resetTransactionsAction.tooltip", "resetTransactionsAction.mnemonic", View.RESET_TRANSACTIONS_VIEW); resetTransactionsAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("resetTransactionsAction.tooltip"))); menuItem = new JMenuItem(resetTransactionsAction); menuItem.setFont(FontSizer.INSTANCE.getAdjustedDefaultFont()); menuItem.setComponentOrientation(componentOrientation); toolsMenu.add(menuItem); setJMenuBar(menuBar); return; } private void tweakAppearance(JMenu menu) { menu.setMargin(new Insets(MENU_HORIZONTAL_INSET, MENU_VERTICAL_INSET, MENU_HORIZONTAL_INSET, MENU_VERTICAL_INSET)); } /** * Recreate all views. */ @Override public void recreateAllViews(final boolean initUI, final View initialView) { // if initUI set, do an invokeLater or else it can sometimes leave the menu items in the Mac header row. if (EventQueue.isDispatchThread() && !initUI) { recreateAllViewsOnSwingThread(initUI, initialView); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { recreateAllViewsOnSwingThread(initUI, initialView); } }); } } private void recreateAllViewsOnSwingThread(final boolean initUI, View initialView) { ColorAndFontConstants.init(); // Close down current view. if (controller.getCurrentView() != View.UNKNOWN_VIEW) { navigateAwayFromView(controller.getCurrentView()); } if (initUI) { // Remember and replay task and remove any listeners. List<WalletData> replayPerWalletModelDataList = null; if (ReplayManager.INSTANCE.getCurrentReplayTask() != null) { replayPerWalletModelDataList = ReplayManager.INSTANCE.getCurrentReplayTask().getPerWalletModelDataToReplay(); } ReplayManager.INSTANCE.removeDownloadListeners(replayPerWalletModelDataList); // Remove the WalletBusyListeners. this.bitcoinController.logNumberOfWalletBusyListeners(); this.bitcoinController.clearWalletBusyListeners(); this.localiser = controller.getLocaliser(); Container contentPane = getContentPane(); viewFactory.initialise(); contentPane.removeAll(); viewTabbedPane.removeAllTabs(); initUI(null); // TODO check task is still running by taskid. if (replayPerWalletModelDataList != null) { ReplayManager.INSTANCE.addDownloadListeners(replayPerWalletModelDataList); } if (initialView != null && !(initialView == View.TRANSACTIONS_VIEW) && !(initialView == View.SEND_BITCOIN_VIEW) && !(initialView == View.RECEIVE_BITCOIN_VIEW)) { JPanel currentTabPanel = new JPanel(new BorderLayout()); Viewable currentView = viewFactory.getView(initialView); currentTabPanel.add((JPanel) currentView, BorderLayout.CENTER); viewTabbedPane.addTab(currentView.getViewTitle(), currentView.getViewIcon(), currentView.getViewTooltip(), currentTabPanel, true); } try { applyComponentOrientation(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())); } catch (ClassCastException cce) { // Look and feel exception - ignore. } } statusBar.refreshOnlineStatusText(); updateHeader(); // Tell the wallets list to display. if (walletsView != null) { walletsView.displayView(DisplayHint.COMPLETE_REDRAW); } if (tickerTablePanel != null) { tickerTablePanel.update(); } // Tell all the tabs in the tabbedPane to update. if (viewTabbedPane != null) { for (int i = 0; i < viewTabbedPane.getTabCount(); i++) { JPanel tabComponent = (JPanel) viewTabbedPane.getComponentAt(i); Component[] components = tabComponent.getComponents(); if (components != null && components.length > 0 && components[0] instanceof Viewable) { Viewable loopView = ((Viewable) components[0]); loopView.displayView(DisplayHint.COMPLETE_REDRAW); if (initialView != null && loopView.getViewId().toString().equals(initialView.toString())) { viewTabbedPane.setSelectedIndex(i); } } } } this.bitcoinController.logNumberOfWalletBusyListeners(); } /** * Display next view. */ @Override public void displayView(View viewToDisplay) { log.debug("Displaying view '" + viewToDisplay + "'"); // Open wallet view obselete - show transactions if (View.OPEN_WALLET_VIEW == viewToDisplay) { viewToDisplay = View.TRANSACTIONS_VIEW; } // Create Bulk addreses obselete - show transactions if (View.CREATE_BULK_ADDRESSES_VIEW == viewToDisplay) { viewToDisplay = View.TRANSACTIONS_VIEW; } // Show wallets view always on display. if (View.YOUR_WALLETS_VIEW == viewToDisplay) { walletsView.displayView(DisplayHint.COMPLETE_REDRAW); return; } controller.setCurrentView(viewToDisplay); final Viewable nextViewFinal = viewFactory.getView(viewToDisplay); if (nextViewFinal == null) { log.debug("Cannot display view " + viewToDisplay); return; } if (EventQueue.isDispatchThread()) { displayViewOnSwingThread(nextViewFinal); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { displayViewOnSwingThread(nextViewFinal); } }); } } private void displayViewOnSwingThread(final Viewable nextViewFinal) { String viewName = nextViewFinal.getViewId().toString(); boolean foundTab = false; if (viewTabbedPane.getTabCount() > 0) { //log.debug("viewTabbedPane " + System.identityHashCode(viewTabbedPane) + " initally has " + viewTabbedPane.getTabCount() + " tabs."); for (int i = 0; i < viewTabbedPane.getTabCount(); i++) { JPanel tabComponent = (JPanel) viewTabbedPane.getComponentAt(i); if (tabComponent != null) { Component[] childComponents = tabComponent.getComponents(); String tabName = null; if (childComponents != null && childComponents.length > 0 && childComponents[0] instanceof Viewable) { tabName = ((Viewable) childComponents[0]).getViewId().toString(); } if (viewName != null && viewName.equals(tabName)) { foundTab = true; ((JPanel) viewTabbedPane.getComponentAt(i)).removeAll(); ((JPanel) viewTabbedPane.getComponentAt(i)).add((JPanel) nextViewFinal); viewTabbedPane.setSelectedIndex(i); break; } } } } if (!foundTab && nextViewFinal instanceof JPanel) { JPanel tabOutlinePanel = new JPanel(new BorderLayout()); tabOutlinePanel.add((JPanel) nextViewFinal, BorderLayout.CENTER); viewTabbedPane.addTab(nextViewFinal.getViewTitle(), nextViewFinal.getViewIcon(), nextViewFinal.getViewTooltip(), tabOutlinePanel, true); viewTabbedPane.setSelectedComponent(tabOutlinePanel); } nextViewFinal.displayView(DisplayHint.COMPLETE_REDRAW); //log.debug("viewTabbedPane " + System.identityHashCode(viewTabbedPane) + " finally has " + viewTabbedPane.getTabCount() + " tabs."); this.setCursor(Cursor.getDefaultCursor()); } /** * Navigate away from view - this may be on another thread hence the * SwingUtilities.invokeLater. */ @Override public void navigateAwayFromView(View viewToNavigateAwayFrom) { if (View.YOUR_WALLETS_VIEW == viewToNavigateAwayFrom) { // Do nothing return; } final Viewable viewToNavigateAwayFromFinal = viewFactory.getView(viewToNavigateAwayFrom); if (viewToNavigateAwayFromFinal != null) { if (EventQueue.isDispatchThread()) { viewToNavigateAwayFromFinal.navigateAwayFromView(); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { viewToNavigateAwayFromFinal.navigateAwayFromView(); } }); } } } @Override public void setOnlineStatus(StatusEnum statusEnum) { online = statusEnum; if (statusBar != null) { statusBar.updateOnlineStatusText(statusEnum); } } @Override public void walletBusyChange(boolean newWalletIsBusy) { if (EventQueue.isDispatchThread()) { updateMenuItemsOnWalletChange(); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { updateMenuItemsOnWalletChange(); } }); } } private void updateMenuItemsOnWalletChange() { signMessageAction.setEnabled(!this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()); showImportPrivateKeysAction.setEnabled(!this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()); showExportPrivateKeysAction.setEnabled(!this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()); showCheckPrivateKeysAction.setEnabled(!this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()); resetTransactionsAction.setEnabled(!this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()); if (this.bitcoinController.getModel().getActiveWallet() == null) { // Cannot do anything password related. addPasswordAction.setEnabled(false); changePasswordAction.setEnabled(false); removePasswordAction.setEnabled(false); } else { if (this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()) { addPasswordAction.setEnabled(false); changePasswordAction.setEnabled(false); removePasswordAction.setEnabled(false); } else { if (this.bitcoinController.getModel().getActiveWallet().getEncryptionType() == EncryptionType.ENCRYPTED_SCRYPT_AES) { addPasswordAction.setEnabled(false); changePasswordAction.setEnabled(true); removePasswordAction.setEnabled(true); } else { if (this.bitcoinController.getModel().getActiveWalletWalletInfo() == null || this.bitcoinController.getModel().getActiveWalletWalletInfo().getWalletVersion() == MultiBitWalletVersion.SERIALIZED) { addPasswordAction.setEnabled(false); } else { addPasswordAction.setEnabled(true); } changePasswordAction.setEnabled(false); removePasswordAction.setEnabled(false); } } } if (this.bitcoinController.getModel().getActivePerWalletModelData().isBusy()) { String walletIsBusyText = HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("multiBitSubmitAction.walletIsBusy", new Object[]{controller.getLocaliser().getString(this.bitcoinController.getModel().getActivePerWalletModelData().getBusyTaskKey())})); signMessageAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); showImportPrivateKeysAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); showExportPrivateKeysAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); showCheckPrivateKeysAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); resetTransactionsAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); addPasswordAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); changePasswordAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); removePasswordAction.putValue(Action.SHORT_DESCRIPTION, walletIsBusyText); } else { signMessageAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("signMessageAction.tooltip"))); showImportPrivateKeysAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("showImportPrivateKeysAction.tooltip"))); showExportPrivateKeysAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("showExportPrivateKeysAction.tooltip"))); showCheckPrivateKeysAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("showCheckPrivateKeysAction.tooltip"))); resetTransactionsAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("resetTransactionsAction.tooltip"))); addPasswordAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("addPasswordAction.tooltip"))); changePasswordAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("changePasswordAction.tooltip"))); removePasswordAction.putValue(Action.SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(controller.getLocaliser().getString("removePasswordAction.tooltip"))); } } @Override /** * Update due to a block being downloaded. * This typically comes in from a Peer. */ public void blockDownloaded() { // Update transaction screen in case status icons have changed. if (View.TRANSACTIONS_VIEW == controller.getCurrentView()) { ShowTransactionsPanel.updateTransactions(); } } @Override public void onCoinsReceived(Wallet wallet, Transaction transaction, BigInteger prevBalance, BigInteger newBalance) { fireDataChangedUpdateLater(DisplayHint.WALLET_TRANSACTIONS_HAVE_CHANGED); } @Override public void onCoinsSent(Wallet wallet, Transaction transaction, BigInteger prevBalance, BigInteger newBalance) { fireDataChangedUpdateLater(DisplayHint.WALLET_TRANSACTIONS_HAVE_CHANGED); } /** * One of the wallets has been reorganised due to a block chain reorganise */ @Override public void onReorganize(Wallet wallet) { log.info("Wallet has been reorganised."); recreateAllViews(false, controller.getCurrentView()); } @Override public void onTransactionConfidenceChanged(Wallet wallet, Transaction transaction) { if (controller.getCurrentView() == View.TRANSACTIONS_VIEW) { ShowTransactionsPanel.updateTransactions(); } else if (controller.getCurrentView() == View.SEND_BITCOIN_VIEW) { final int numberOfPeers = (transaction == null || transaction.getConfidence() == null) ? 0 : transaction.getConfidence().getBroadcastByCount(); //log.debug("numberOfPeers = " + numberOfPeers); final Sha256Hash transactionHash = (transaction == null) ? null : transaction.getHash(); //log.debug((transaction != null && transaction.getConfidence() != null) ? transaction.getConfidence().toString() : "No transaction confidence for tx"); SendBitcoinConfirmPanel.updatePanelDueToTransactionConfidenceChange(transactionHash, numberOfPeers); } } @Override public void fireFilesHaveBeenChangedByAnotherProcess(WalletData perWalletModelData) { if (this.bitcoinController.getModel().getActiveWalletFilename() != null && this.bitcoinController.getModel().getActiveWalletFilename().equals(perWalletModelData.getWalletFilename())) { Message message = new Message(HelpContentsPanel.createTooltipText(controller.getLocaliser().getString("singleWalletPanel.dataHasChanged.tooltip.1") + " " + controller.getLocaliser().getString("singleWalletPanel.dataHasChanged.tooltip.2")), true); MessageManager.INSTANCE.addMessage(message); } fireDataChangedUpdateNow(DisplayHint.COMPLETE_REDRAW); } /** * Mark that the UI needs to be updated as soon as possible. */ @Override public void fireDataChangedUpdateNow(final DisplayHint displayHint) { if (EventQueue.isDispatchThread()) { fireDataChangedOnSwingThread(displayHint); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { fireDataChangedOnSwingThread(displayHint); } }); } } /** * Mark that the UI needs updating the next time the fireDataChangedTimer fires. */ @Override public void fireDataChangedUpdateLater(DisplayHint displayHint) { if (fireDataChangedTimerTask != null) { fireDataChangedTimerTask.setFireDataChanged(true); } } /** * Actually update the UI. * (Called back from the FireDataChangedTimerTask). */ private void fireDataChangedOnSwingThread(DisplayHint displayHint) { updateHeader(); // Update the password related menu items. updateMenuItemsOnWalletChange(); // Tell the wallets list to display. if (walletsView != null) { walletsView.displayView(displayHint); } // Tell the current view to update itself. Viewable currentViewView = viewFactory.getView(controller.getCurrentView()); if (currentViewView != null) { currentViewView.displayView(displayHint); } } /** * Update the Ticker Panel after the exchange data has changed. */ public void fireExchangeDataChanged() { updateHeader(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tickerTablePanel.update(); } }); } public void updateHeader(final String syncMessage, final double syncPercent) { final boolean isBusy = this.bitcoinController.getModel().getActivePerWalletModelData().isBusy(); if (EventQueue.isDispatchThread()) { updateHeaderOnSwingThread(null, null, isBusy, syncMessage, syncPercent); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { updateHeaderOnSwingThread(null, null, isBusy, syncMessage, syncPercent); } }); } } public void updateHeader() { final BigInteger finalEstimatedBalance = this.bitcoinController.getModel().getActiveWalletEstimatedBalance(); final BigInteger finalAvailableToSpend = this.bitcoinController.getModel().getActiveWalletAvailableBalance(); final boolean isBusy = this.bitcoinController.getModel().getActivePerWalletModelData().isBusy(); if (EventQueue.isDispatchThread()) { updateHeaderOnSwingThread(finalEstimatedBalance, finalAvailableToSpend, isBusy, null, -1); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { updateHeaderOnSwingThread(finalEstimatedBalance, finalAvailableToSpend, isBusy, null, -1); } }); } } private void updateHeaderOnSwingThread(final BigInteger estimatedBalance, final BigInteger availableToSpend, final boolean isBusy, final String syncMessage, final double syncPercent) { if (isBusy) { estimatedBalanceLabelLabel.setText(" "); estimatedBalanceLabelLabel.setToolTipText(null); if (syncMessage != null && !syncMessage.isEmpty()) { estimatedBalanceBTCLabel.setText(syncMessage); estimatedBalanceBTCLabel.setToolTipText(syncMessage); } if (syncPercent > -1) { estimatedBalanceFiatLabel.setText("(" + (int) syncPercent + "%)"); estimatedBalanceFiatLabel.setToolTipText(syncMessage); } else if (syncPercent == 0 || (syncMessage != null && !syncMessage.isEmpty())) { estimatedBalanceFiatLabel.setText(" "); estimatedBalanceFiatLabel.setToolTipText(null); } else { estimatedBalanceFiatLabel.setToolTipText(null); } availableBalanceBTCButton.setText(" "); availableBalanceFiatButton.setText(" "); availableBalanceLabelButton.setEnabled(false); availableBalanceBTCButton.setEnabled(false); availableBalanceFiatButton.setEnabled(false); availableBalanceLabelButton.setVisible(false); availableBalanceBTCButton.setVisible(false); availableBalanceFiatButton.setVisible(false); } else { estimatedBalanceLabelLabel.setText(controller.getLocaliser().getString("multiBitFrame.balanceLabel")); estimatedBalanceLabelLabel.setToolTipText(controller.getLocaliser().getString("multiBitFrame.balanceLabel.tooltip")); estimatedBalanceBTCLabel.setText(controller.getLocaliser().bitcoinValueToString(estimatedBalance, true, false)); estimatedBalanceBTCLabel.setToolTipText(controller.getLocaliser().getString("multiBitFrame.balanceLabel.tooltip")); estimatedBalanceFiatLabel.setToolTipText(controller.getLocaliser().getString("multiBitFrame.balanceLabel.tooltip")); if (CurrencyConverter.INSTANCE.getRate() != null && CurrencyConverter.INSTANCE.isShowingFiat()) { Money fiat = CurrencyConverter.INSTANCE.convertFromBTCToFiat(estimatedBalance); estimatedBalanceFiatLabel.setText("(" + CurrencyConverter.INSTANCE.getFiatAsLocalisedString(fiat) + ")"); } else { estimatedBalanceFiatLabel.setText(" "); } if (availableToSpend != null && availableToSpend.equals(estimatedBalance)) { availableBalanceBTCButton.setText(" "); availableBalanceFiatButton.setText(" "); availableBalanceLabelButton.setEnabled(false); availableBalanceBTCButton.setEnabled(false); availableBalanceFiatButton.setEnabled(false); availableBalanceLabelButton.setVisible(false); availableBalanceBTCButton.setVisible(false); availableBalanceFiatButton.setVisible(false); } else { availableBalanceBTCButton .setText(controller.getLocaliser().bitcoinValueToString(availableToSpend, true, false)); if (CurrencyConverter.INSTANCE.getRate() != null && CurrencyConverter.INSTANCE.isShowingFiat()) { Money fiat = CurrencyConverter.INSTANCE.convertFromBTCToFiat(availableToSpend); if (fiat != null) { availableBalanceFiatButton.setText("(" + CurrencyConverter.INSTANCE.getFiatAsLocalisedString(fiat) + ")"); } } else { availableBalanceFiatButton.setText(" "); } availableBalanceLabelButton.setEnabled(true); availableBalanceBTCButton.setEnabled(true); availableBalanceFiatButton.setEnabled(true); availableBalanceLabelButton.setVisible(true); availableBalanceBTCButton.setVisible(true); availableBalanceFiatButton.setVisible(true); } String titleText = localiser.getString("multiBitFrame.title"); if (this.bitcoinController.getModel().getActiveWallet() != null) { titleText = titleText + SEPARATOR + this.bitcoinController.getModel().getActivePerWalletModelData().getWalletDescription() + SEPARATOR + this.bitcoinController.getModel().getActivePerWalletModelData().getWalletFilename(); } setTitle(titleText); } } // Macify application methods. @Override @Deprecated public void handleAbout(ApplicationEvent event) { controller.displayView(View.HELP_ABOUT_VIEW); event.setHandled(true); } @Override @Deprecated public void handleOpenApplication(ApplicationEvent event) { // Ok, we know our application started. // Not much to do about that.. } @Override @Deprecated public void handleOpenFile(ApplicationEvent event) { // TODO i18n required. JOptionPane.showMessageDialog(this, "Sorry, opening of files with double click is not yet implemented. Wallet was " + event.getFilename()); } @Override @Deprecated public void handlePreferences(ApplicationEvent event) { controller.displayView(View.PREFERENCES_VIEW); } @Override @Deprecated public void handlePrintFile(ApplicationEvent event) { // TODO i18n required. JOptionPane.showMessageDialog(this, "Sorry, printing not implemented"); } @Override @Deprecated public void handleQuit(ApplicationEvent event) { throw new UnsupportedOperationException("Deprecated."); } @Override public void handleReOpenApplication(ApplicationEvent event) { setVisible(true); } public void setUpdatesStoppedTooltip(JComponent component) { // Multiline tool tip text. String toolTipText = "<html><font face=\"sansserif\">"; toolTipText = toolTipText + controller.getLocaliser().getString("singleWalletPanel.dataHasChanged.tooltip.1") + "<br>"; toolTipText = toolTipText + controller.getLocaliser().getString("singleWalletPanel.dataHasChanged.tooltip.2") + "<br>"; toolTipText = toolTipText + "</font></html>"; component.setToolTipText(toolTipText); } public void bringToFront() { java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { toFront(); repaint(); } }); } public void calculateDividerPosition() { int dividerPosition = SingleWalletPanel.calculateNormalWidth((JComponent) (walletsView)) + WALLET_WIDTH_DELTA; if (((WalletListPanel) walletsView).getScrollPane().getVerticalScrollBar().isVisible()) { dividerPosition += SCROLL_BAR_DELTA; } if (walletsView != null && walletsView.getPreferredSize() != null && walletsView.getPreferredSize().width > dividerPosition) { dividerPosition = walletsView.getPreferredSize().width; } if (ComponentOrientation.RIGHT_TO_LEFT == ComponentOrientation.getOrientation(controller.getLocaliser().getLocale())) { int width = getWidth(); if (width == 0) { width = (int) this.getPreferredSize().getWidth(); } dividerPosition = width - dividerPosition; // - WalletListPanel.LEFT_BORDER - WalletListPanel.RIGHT_BORDER - 2; } splitPane.setEnabled(true); splitPane.setDividerLocation(dividerPosition); splitPane.setEnabled(ComponentOrientation.LEFT_TO_RIGHT.equals(ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()))); } public WalletListPanel getWalletsView() { return walletsView; } public void onDeadTransaction(Wallet wallet, Transaction deadTx, Transaction replacementTx) { } public JPanel getHeaderPanel() { return headerPanel; } public TickerTablePanel getTickerTablePanel() { return tickerTablePanel; } public JSplitPane getSplitPane() { return splitPane; } @Override public void onWalletChanged(Wallet wallet) { fireDataChangedUpdateNow(DisplayHint.WALLET_TRANSACTIONS_HAVE_CHANGED); } @Override public void lostExchangeRate(ExchangeRate exchangeRate) { updateHeader(); } @Override public void foundExchangeRate(ExchangeRate exchangeRate) { updateHeader(); } @Override public void updatedExchangeRate(ExchangeRate exchangeRate) { updateHeader(); } public Timer getTickerTimer1() { return tickerTimer1; } public void setTickerTimer1(Timer tickerTimer1) { this.tickerTimer1 = tickerTimer1; } public Timer getTickerTimer2() { return tickerTimer2; } public void setTickerTimer2(Timer tickerTimer2) { this.tickerTimer2 = tickerTimer2; } public TickerTimerTask getTickerTimerTask1() { return tickerTimerTask1; } public TickerTimerTask getTickerTimerTask2() { return tickerTimerTask2; } public void setTickerTimerTask1(TickerTimerTask tickerTimerTask1) { this.tickerTimerTask1 = tickerTimerTask1; } public void setTickerTimerTask2(TickerTimerTask tickerTimerTask2) { this.tickerTimerTask2 = tickerTimerTask2; } public HealthCheckTimerTask getHealthCheckTimerTask() { return healthCheckTimerTask; } @Override public void onKeysAdded(Wallet wallet, List<ECKey> keys) { } @Override public void onScriptsAdded(Wallet wallet, List<Script> scripts) { } }