bitcoingui.cpp 28.8 KB
Newer Older
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
1
/*
2
3
 * Qt4 bitcoin GUI.
 *
Fordy's avatar
Fordy committed
4
 * W.J. van der Laan 2011-2012
Fordy's avatar
Fordy committed
5
 * The Bitcoin Developers 2011-2012
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
6
 */
7

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
8
#include "bitcoingui.h"
9

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
10
11
12
#include "transactiontablemodel.h"
#include "optionsdialog.h"
#include "aboutdialog.h"
13
#include "clientmodel.h"
14
#include "walletmodel.h"
15
#include "walletframe.h"
16
#include "optionsmodel.h"
17
#include "transactiondescdialog.h"
18
#include "bitcoinunits.h"
19
#include "guiconstants.h"
20
#include "notificator.h"
21
#include "guiutil.h"
22
#include "rpcconsole.h"
23
#include "ui_interface.h"
24
#include "wallet.h"
25
#include "init.h"
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
26

Philip Kaufmann's avatar
Philip Kaufmann committed
27
#ifdef Q_OS_MAC
28
29
30
#include "macdockiconhandler.h"
#endif

31
#include <QApplication>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
32
33
34
35
36
37
38
#include <QMenuBar>
#include <QMenu>
#include <QIcon>
#include <QVBoxLayout>
#include <QToolBar>
#include <QStatusBar>
#include <QLabel>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
39
#include <QMessageBox>
40
#include <QProgressBar>
41
#include <QStackedWidget>
42
#include <QDateTime>
43
#include <QMovie>
44
#include <QTimer>
45
#include <QDragEnterEvent>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
46
#if QT_VERSION < 0x050000
47
#include <QUrl>
48
#include <QTextDocument>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
49
#endif
50
#include <QMimeData>
51
#include <QStyle>
52
#include <QListWidget>
53

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
54
55
#include <iostream>

56
57
const QString BitcoinGUI::DEFAULT_WALLET = "~Default";

58
BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
59
60
    QMainWindow(parent),
    clientModel(0),
61
62
    encryptWalletAction(0),
    changePassphraseAction(0),
63
    aboutQtAction(0),
64
    trayIcon(0),
65
    notificator(0),
66
67
    rpcConsole(0),
    prevBlocks(0)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
68
{
69
    GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
70

Philip Kaufmann's avatar
Philip Kaufmann committed
71
#ifndef Q_OS_MAC
72
73
74
75
76
77
78
79
80
81
82
83
    if (!fIsTestnet)
    {
        setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet"));
        QApplication::setWindowIcon(QIcon(":icons/bitcoin"));
        setWindowIcon(QIcon(":icons/bitcoin"));
    }
    else
    {
        setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet") + " " + tr("[testnet]"));
        QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet"));
        setWindowIcon(QIcon(":icons/bitcoin_testnet"));
    }
84
85
86
#else
    setUnifiedTitleAndToolBarOnMac(true);
    QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
87
88
89
90
91

    if (!fIsTestnet)
        MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin"));
    else
        MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
92
#endif
93

94
95
96
97
    // Create wallet frame and make it the central widget
    walletFrame = new WalletFrame(this);
    setCentralWidget(walletFrame);

98
99
    // Accept D&D of URIs
    setAcceptDrops(true);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
100

101
    // Create actions for the toolbar, menu bar and tray/dock icon
102
    // Needs walletFrame to be initialized
103
    createActions(fIsTestnet);
104

105
106
    // Create application menu bar
    createMenuBar();
107

108
109
    // Create the toolbars
    createToolBars();
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
110

111
    // Create system tray icon and notification
112
    createTrayIcon(fIsTestnet);
113

114
    // Create status bar
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
115
    statusBar();
116

117
    // Status bar notification icons
118
    QFrame *frameBlocks = new QFrame();
119
120
121
    frameBlocks->setContentsMargins(0,0,0,0);
    frameBlocks->setMinimumWidth(56);
    frameBlocks->setMaximumWidth(56);
122
123
124
    QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
    frameBlocksLayout->setContentsMargins(3,0,3,0);
    frameBlocksLayout->setSpacing(3);
125
    labelEncryptionIcon = new QLabel();
126
    labelConnectionsIcon = new QLabel();
127
    labelBlocksIcon = new QLabel();
128
    frameBlocksLayout->addStretch();
129
130
    frameBlocksLayout->addWidget(labelEncryptionIcon);
    frameBlocksLayout->addStretch();
131
132
    frameBlocksLayout->addWidget(labelConnectionsIcon);
    frameBlocksLayout->addStretch();
133
134
    frameBlocksLayout->addWidget(labelBlocksIcon);
    frameBlocksLayout->addStretch();
135

136
137
    // Progress bar and label for blocks download
    progressBarLabel = new QLabel();
138
139
    progressBarLabel->setVisible(false);
    progressBar = new QProgressBar();
140
    progressBar->setAlignment(Qt::AlignCenter);
141
142
    progressBar->setVisible(false);

143
144
145
    // Override style sheet for progress bar for styles that have a segmented progress bar,
    // as they make the text unreadable (workaround for issue #1071)
    // See https://qt-project.org/doc/qt-4.8/gallery.html
146
    QString curStyle = QApplication::style()->metaObject()->className();
147
148
149
150
151
    if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle")
    {
        progressBar->setStyleSheet("QProgressBar { background-color: #e8e8e8; border: 1px solid grey; border-radius: 7px; padding: 1px; text-align: center; } QProgressBar::chunk { background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #FF8000, stop: 1 orange); border-radius: 7px; margin: 0px; }");
    }

152
153
    statusBar()->addWidget(progressBarLabel);
    statusBar()->addWidget(progressBar);
154
    statusBar()->addPermanentWidget(frameBlocks);
155

156
157
    syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);

158
159
    rpcConsole = new RPCConsole(this);
    connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show()));
160
161
    // prevents an oben debug window from becoming stuck/unusable on client shutdown
    connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide()));
162

163
164
    // Install event filter to be able to catch status tip events (QEvent::StatusTip)
    this->installEventFilter(this);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
165
166
}

167
168
BitcoinGUI::~BitcoinGUI()
{
169
    GUIUtil::saveWindowGeometry("nWindow", this);
170
171
    if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
        trayIcon->hide();
Philip Kaufmann's avatar
Philip Kaufmann committed
172
#ifdef Q_OS_MAC
173
    delete appMenuBar;
174
    MacDockIconHandler::instance()->setMainWindow(NULL);
175
176
177
#endif
}

178
void BitcoinGUI::createActions(bool fIsTestnet)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
179
{
180
    QActionGroup *tabGroup = new QActionGroup(this);
181

182
    overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this);
183
184
    overviewAction->setStatusTip(tr("Show general overview of wallet"));
    overviewAction->setToolTip(overviewAction->statusTip());
185
    overviewAction->setCheckable(true);
186
    overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1));
187
    tabGroup->addAction(overviewAction);
188

189
    sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send"), this);
190
191
    sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
    sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
Philip Kaufmann's avatar
Philip Kaufmann committed
192
193
194
195
    sendCoinsAction->setCheckable(true);
    sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
    tabGroup->addAction(sendCoinsAction);

196
    receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive"), this);
197
    receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)"));
198
    receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
Philip Kaufmann's avatar
Philip Kaufmann committed
199
200
201
202
    receiveCoinsAction->setCheckable(true);
    receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
    tabGroup->addAction(receiveCoinsAction);

203
    historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this);
204
205
    historyAction->setStatusTip(tr("Browse transaction history"));
    historyAction->setToolTip(historyAction->statusTip());
206
    historyAction->setCheckable(true);
207
    historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
208
209
    tabGroup->addAction(historyAction);

210
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
211
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
Philip Kaufmann's avatar
Philip Kaufmann committed
212
213
214
215
    connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
    connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage()));
    connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
    connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
216
    connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
217
    connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
218

219
    quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
220
    quitAction->setStatusTip(tr("Quit application"));
221
    quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
222
    quitAction->setMenuRole(QAction::QuitRole);
223
224
225
226
    if (!fIsTestnet)
        aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this);
    else
        aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin"), this);
227
    aboutAction->setStatusTip(tr("Show information about Bitcoin"));
228
    aboutAction->setMenuRole(QAction::AboutRole);
229
#if QT_VERSION < 0x050000
230
    aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this);
231
232
#else
    aboutQtAction = new QAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this);
233
#endif
234
    aboutQtAction->setStatusTip(tr("Show information about Qt"));
235
    aboutQtAction->setMenuRole(QAction::AboutQtRole);
236
    optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
237
    optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin"));
238
    optionsAction->setMenuRole(QAction::PreferencesRole);
239
240
241
242
    if (!fIsTestnet)
        toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this);
    else
        toggleHideAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&Show / Hide"), this);
243
    toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
244

245
    encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
246
    encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
247
    encryptWalletAction->setCheckable(true);
248
    backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
249
    backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
250
    changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this);
251
    changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
Philip Kaufmann's avatar
Philip Kaufmann committed
252
    signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this);
253
    signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
Philip Kaufmann's avatar
Philip Kaufmann committed
254
    verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this);
255
    verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
Philip Kaufmann's avatar
Philip Kaufmann committed
256

257
    openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this);
258
    openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console"));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
259

260
261
262
263
264
    usedSendingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Used sending addresses..."), this);
    usedSendingAddressesAction->setStatusTip(tr("Edit the list of used sending addresses and labels"));
    usedReceivingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("Used &receiving addresses..."), this);
    usedReceivingAddressesAction->setStatusTip(tr("Edit the list of used receiving addresses and labels"));

265
266
    connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
267
    connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
Philip Kaufmann's avatar
Philip Kaufmann committed
268
    connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
269
    connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
270
271
272
    connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame, SLOT(encryptWallet(bool)));
    connect(backupWalletAction, SIGNAL(triggered()), walletFrame, SLOT(backupWallet()));
    connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase()));
Philip Kaufmann's avatar
Philip Kaufmann committed
273
274
    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab()));
275
276
    connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses()));
    connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses()));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
277
278
}

279
280
void BitcoinGUI::createMenuBar()
{
Philip Kaufmann's avatar
Philip Kaufmann committed
281
#ifdef Q_OS_MAC
282
283
284
285
286
287
288
289
290
    // Create a decoupled menu bar on Mac which stays even if the window is closed
    appMenuBar = new QMenuBar();
#else
    // Get the main window's menu bar on other platforms
    appMenuBar = menuBar();
#endif

    // Configure the menus
    QMenu *file = appMenuBar->addMenu(tr("&File"));
291
    file->addAction(backupWalletAction);
292
    file->addAction(signMessageAction);
293
    file->addAction(verifyMessageAction);
294
    file->addSeparator();
295
296
297
    file->addAction(usedSendingAddressesAction);
    file->addAction(usedReceivingAddressesAction);
    file->addSeparator();
298
299
300
301
302
303
304
305
306
    file->addAction(quitAction);

    QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
    settings->addAction(encryptWalletAction);
    settings->addAction(changePassphraseAction);
    settings->addSeparator();
    settings->addAction(optionsAction);

    QMenu *help = appMenuBar->addMenu(tr("&Help"));
307
308
    help->addAction(openRPCConsoleAction);
    help->addSeparator();
309
    help->addAction(aboutAction);
310
    help->addAction(aboutQtAction);
311
312
313
314
315
316
317
318
319
320
321
322
}

void BitcoinGUI::createToolBars()
{
    QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
    toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    toolbar->addAction(overviewAction);
    toolbar->addAction(sendCoinsAction);
    toolbar->addAction(receiveCoinsAction);
    toolbar->addAction(historyAction);
}

323
void BitcoinGUI::setClientModel(ClientModel *clientModel)
324
{
325
    this->clientModel = clientModel;
326
    if(clientModel)
327
    {
328
329
        // Create system tray menu (or setup the dock menu) that late to prevent users from calling actions,
        // while the client has not yet fully loaded
330
        createTrayIconMenu();
331

332
333
334
        // Keep up to date with client
        setNumConnections(clientModel->getNumConnections());
        connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
335

336
337
        setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers());
        connect(clientModel, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
338

339
        // Receive and report messages from network/worker thread
340
        connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int)));
341

Philip Kaufmann's avatar
Philip Kaufmann committed
342
        rpcConsole->setClientModel(clientModel);
343
        walletFrame->setClientModel(clientModel);
344
    }
345
346
}

347
bool BitcoinGUI::addWallet(const QString& name, WalletModel *walletModel)
348
{
349
350
351
352
353
354
355
356
357
358
359
    return walletFrame->addWallet(name, walletModel);
}

bool BitcoinGUI::setCurrentWallet(const QString& name)
{
    return walletFrame->setCurrentWallet(name);
}

void BitcoinGUI::removeAllWallets()
{
    walletFrame->removeAllWallets();
360
361
}

362
void BitcoinGUI::createTrayIcon(bool fIsTestnet)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
363
{
Philip Kaufmann's avatar
Philip Kaufmann committed
364
#ifndef Q_OS_MAC
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
365
    trayIcon = new QSystemTrayIcon(this);
366

367
368
369
370
371
372
373
374
375
376
377
    if (!fIsTestnet)
    {
        trayIcon->setToolTip(tr("Bitcoin client"));
        trayIcon->setIcon(QIcon(":/icons/toolbar"));
    }
    else
    {
        trayIcon->setToolTip(tr("Bitcoin client") + " " + tr("[testnet]"));
        trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
    }

378
379
380
    trayIcon->show();
#endif

381
    notificator = new Notificator(QApplication::applicationName(), trayIcon);
382
383
384
385
386
387
}

void BitcoinGUI::createTrayIconMenu()
{
    QMenu *trayIconMenu;
#ifndef Q_OS_MAC
388
389
390
391
    // return if trayIcon is unset (only on non-Mac OSes)
    if (!trayIcon)
        return;

392
393
394
    trayIconMenu = new QMenu(this);
    trayIcon->setContextMenu(trayIconMenu);

395
396
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
397
398
399
#else
    // Note: On Mac, the dock icon is used to provide the tray's functionality.
    MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
400
    dockIconHandler->setMainWindow((QMainWindow *)this);
401
402
403
404
    trayIconMenu = dockIconHandler->dockMenu();
#endif

    // Configuration of the tray icon (or dock icon) icon menu
405
    trayIconMenu->addAction(toggleHideAction);
406
    trayIconMenu->addSeparator();
407
408
    trayIconMenu->addAction(sendCoinsAction);
    trayIconMenu->addAction(receiveCoinsAction);
409
    trayIconMenu->addSeparator();
410
    trayIconMenu->addAction(signMessageAction);
411
    trayIconMenu->addAction(verifyMessageAction);
412
413
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(optionsAction);
414
    trayIconMenu->addAction(openRPCConsoleAction);
Philip Kaufmann's avatar
Philip Kaufmann committed
415
#ifndef Q_OS_MAC // This is built-in on Mac
416
417
418
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(quitAction);
#endif
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
419
420
}

Philip Kaufmann's avatar
Philip Kaufmann committed
421
#ifndef Q_OS_MAC
422
423
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
424
    if(reason == QSystemTrayIcon::Trigger)
425
    {
426
        // Click on system tray icon triggers show/hide of the main window
427
        toggleHideAction->trigger();
428
429
    }
}
430
#endif
431

432
433
void BitcoinGUI::optionsClicked()
{
434
435
    if(!clientModel || !clientModel->getOptionsModel())
        return;
436
    OptionsDialog dlg;
437
    dlg.setModel(clientModel->getOptionsModel());
438
    dlg.exec();
439
440
}

441
void BitcoinGUI::aboutClicked()
442
{
443
    AboutDialog dlg;
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
444
    dlg.setModel(clientModel);
445
    dlg.exec();
446
447
}

448
449
void BitcoinGUI::gotoOverviewPage()
{
450
    overviewAction->setChecked(true);
451
452
453
454
455
    if (walletFrame) walletFrame->gotoOverviewPage();
}

void BitcoinGUI::gotoHistoryPage()
{
456
    historyAction->setChecked(true);
457
458
459
460
461
    if (walletFrame) walletFrame->gotoHistoryPage();
}

void BitcoinGUI::gotoReceiveCoinsPage()
{
462
    receiveCoinsAction->setChecked(true);
463
464
465
    if (walletFrame) walletFrame->gotoReceiveCoinsPage();
}

466
void BitcoinGUI::gotoSendCoinsPage(QString addr)
467
{
468
    sendCoinsAction->setChecked(true);
469
    if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
470
471
472
473
474
475
476
477
478
}

void BitcoinGUI::gotoSignMessageTab(QString addr)
{
    if (walletFrame) walletFrame->gotoSignMessageTab(addr);
}

void BitcoinGUI::gotoVerifyMessageTab(QString addr)
{
479
    if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
480
481
}

482
483
void BitcoinGUI::setNumConnections(int count)
{
484
485
486
    QString icon;
    switch(count)
    {
487
488
489
490
491
    case 0: icon = ":/icons/connect_0"; break;
    case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
    case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
    case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
    default: icon = ":/icons/connect_4"; break;
492
    }
493
    labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
494
    labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
495
496
}

497
void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks)
498
{
499
500
501
    // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text)
    statusBar()->clearMessage();

502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
    // Acquire current block source
    enum BlockSource blockSource = clientModel->getBlockSource();
    switch (blockSource) {
        case BLOCK_SOURCE_NETWORK:
            progressBarLabel->setText(tr("Synchronizing with network..."));
            break;
        case BLOCK_SOURCE_DISK:
            progressBarLabel->setText(tr("Importing blocks from disk..."));
            break;
        case BLOCK_SOURCE_REINDEX:
            progressBarLabel->setText(tr("Reindexing blocks on disk..."));
            break;
        case BLOCK_SOURCE_NONE:
            // Case: not Importing, not Reindexing and no network connection
            progressBarLabel->setText(tr("No block source available..."));
            break;
518
519
    }

520
521
    QString tooltip;

522
    QDateTime lastBlockDate = clientModel->getLastBlockDate();
523
524
    QDateTime currentDate = QDateTime::currentDateTime();
    int secs = lastBlockDate.secsTo(currentDate);
525

526
    if(count < nTotalBlocks)
527
    {
528
        tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks);
529
530
531
    }
    else
    {
532
        tooltip = tr("Processed %1 blocks of transaction history.").arg(count);
533
    }
534

535
    // Set icon state: spinning if catching up, tick otherwise
536
    if(secs < 90*60 && count >= nTotalBlocks)
537
    {
538
        tooltip = tr("Up to date") + QString(".<br>") + tooltip;
539
        labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
540

541
        walletFrame->showOutOfSyncWarning(false);
542
543
544

        progressBarLabel->setVisible(false);
        progressBar->setVisible(false);
545
    }
546
547
    else
    {
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
        // Represent time from last generated block in human readable text
        QString timeBehindText;
        if(secs < 48*60*60)
        {
            timeBehindText = tr("%n hour(s)","",secs/(60*60));
        }
        else if(secs < 14*24*60*60)
        {
            timeBehindText = tr("%n day(s)","",secs/(24*60*60));
        }
        else
        {
            timeBehindText = tr("%n week(s)","",secs/(7*24*60*60));
        }

        progressBarLabel->setVisible(true);
        progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
565
566
        progressBar->setMaximum(1000000000);
        progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5);
567
568
        progressBar->setVisible(true);

569
        tooltip = tr("Catching up...") + QString("<br>") + tooltip;
570
        labelBlocksIcon->setMovie(syncIconMovie);
571
572
573
        if(count != prevBlocks)
            syncIconMovie->jumpToNextFrame();
        prevBlocks = count;
574

575
        walletFrame->showOutOfSyncWarning(true);
576

577
        tooltip += QString("<br>");
578
579
580
        tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText);
        tooltip += QString("<br>");
        tooltip += tr("Transactions after this will not yet be visible.");
581
    }
582

583
584
585
    // Don't word-wrap this (fixed-width) tooltip
    tooltip = QString("<nobr>") + tooltip + QString("</nobr>");

586
    labelBlocksIcon->setToolTip(tooltip);
587
588
    progressBarLabel->setToolTip(tooltip);
    progressBar->setToolTip(tooltip);
589
590
}

591
void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
592
{
593
    QString strTitle = tr("Bitcoin"); // default title
594
595
596
597
    // Default to information icon
    int nMBoxIcon = QMessageBox::Information;
    int nNotifyIcon = Notificator::Information;

598
    QString msgType;
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617

    // Prefer supplied title over style based title
    if (!title.isEmpty()) {
        msgType = title;
    }
    else {
        switch (style) {
        case CClientUIInterface::MSG_ERROR:
            msgType = tr("Error");
            break;
        case CClientUIInterface::MSG_WARNING:
            msgType = tr("Warning");
            break;
        case CClientUIInterface::MSG_INFORMATION:
            msgType = tr("Information");
            break;
        default:
            break;
        }
618
    }
619
    // Append title to "Bitcoin - "
620
621
    if (!msgType.isEmpty())
        strTitle += " - " + msgType;
622
623
624
625
626

    // Check for error/warning icon
    if (style & CClientUIInterface::ICON_ERROR) {
        nMBoxIcon = QMessageBox::Critical;
        nNotifyIcon = Notificator::Critical;
627
    }
628
629
630
631
632
633
    else if (style & CClientUIInterface::ICON_WARNING) {
        nMBoxIcon = QMessageBox::Warning;
        nNotifyIcon = Notificator::Warning;
    }

    // Display message
634
    if (style & CClientUIInterface::MODAL) {
635
636
637
638
639
        // Check for buttons, use OK as default, if none was supplied
        QMessageBox::StandardButton buttons;
        if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
            buttons = QMessageBox::Ok;

640
641
        // Ensure we get users attention
        showNormalIfMinimized();
642
        QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this);
643
644
645
        int r = mBox.exec();
        if (ret != NULL)
            *ret = r == QMessageBox::Ok;
646
647
648
    }
    else
        notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
649
}
650
651
652

void BitcoinGUI::changeEvent(QEvent *e)
{
653
    QMainWindow::changeEvent(e);
Philip Kaufmann's avatar
Philip Kaufmann committed
654
#ifndef Q_OS_MAC // Ignored on Mac
655
    if(e->type() == QEvent::WindowStateChange)
656
    {
657
        if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray())
658
        {
659
            QWindowStateChangeEvent *wsevt = static_cast<QWindowStateChangeEvent*>(e);
660
            if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized())
661
            {
662
663
                QTimer::singleShot(0, this, SLOT(hide()));
                e->ignore();
664
665
666
            }
        }
    }
667
#endif
668
669
670
671
}

void BitcoinGUI::closeEvent(QCloseEvent *event)
{
672
    if(clientModel)
673
    {
Philip Kaufmann's avatar
Philip Kaufmann committed
674
#ifndef Q_OS_MAC // Ignored on Mac
675
676
677
        if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
           !clientModel->getOptionsModel()->getMinimizeOnClose())
        {
678
            QApplication::quit();
679
        }
680
#endif
681
    }
682
683
    QMainWindow::closeEvent(event);
}
Wladimir J. van der Laan's avatar
ask fee    
Wladimir J. van der Laan committed
684
685
686

void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
{
687
688
689
    if (!clientModel || !clientModel->getOptionsModel())
        return;

690
691
    QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, "
        "which goes to the nodes that process your transaction and helps to support the network. "
692
        "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(clientModel->getOptionsModel()->getDisplayUnit(), nFeeRequired));
693
694
    QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm transaction fee"), strMessage,
        QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes);
Wladimir J. van der Laan's avatar
ask fee    
Wladimir J. van der Laan committed
695
696
    *payFee = (retval == QMessageBox::Yes);
}
697

698
void BitcoinGUI::incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address)
699
{
700
    // On new transaction, make an info balloon
701
702
703
704
705
706
    message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"),
             tr("Date: %1\n"
                "Amount: %2\n"
                "Type: %3\n"
                "Address: %4\n")
                  .arg(date)
707
                  .arg(BitcoinUnits::formatWithUnit(unit, amount, true))
708
709
                  .arg(type)
                  .arg(address), CClientUIInterface::MSG_INFORMATION);
710
}
711

712
713
void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
{
714
    // Accept only URIs
715
716
717
718
719
720
721
722
    if(event->mimeData()->hasUrls())
        event->acceptProposedAction();
}

void BitcoinGUI::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasUrls())
    {
723
        int nValidUrisFound = 0;
724
725
        QList<QUrl> uris = event->mimeData()->urls();
        foreach(const QUrl &uri, uris)
726
        {
727
728
            SendCoinsRecipient r;
            if (GUIUtil::parseBitcoinURI(uri, &r) && walletFrame->handlePaymentRequest(r))
729
                nValidUrisFound++;
730
        }
731
732
733

        // if valid URIs were found
        if (nValidUrisFound)
734
            walletFrame->gotoSendCoinsPage();
735
        else
736
            message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
737
                CClientUIInterface::ICON_WARNING);
738
739
740
741
742
    }

    event->acceptProposedAction();
}

743
744
745
746
747
748
bool BitcoinGUI::eventFilter(QObject *object, QEvent *event)
{
    // Catch status tip events
    if (event->type() == QEvent::StatusTip)
    {
        // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff
749
        if (progressBarLabel->isVisible() || progressBar->isVisible())
750
751
752
753
754
            return true;
    }
    return QMainWindow::eventFilter(object, event);
}

755
bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
756
{
757
758
759
760
761
762
763
764
765
    // URI has to be valid
    if (walletFrame->handlePaymentRequest(recipient))
    {
        showNormalIfMinimized();
        gotoSendCoinsPage();
        return true;
    }
    else
        return false;
766
767
}

768
769
770
771
772
773
void BitcoinGUI::setEncryptionStatus(int status)
{
    switch(status)
    {
    case WalletModel::Unencrypted:
        labelEncryptionIcon->hide();
774
775
776
        encryptWalletAction->setChecked(false);
        changePassphraseAction->setEnabled(false);
        encryptWalletAction->setEnabled(true);
777
778
779
780
781
        break;
    case WalletModel::Unlocked:
        labelEncryptionIcon->show();
        labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
        labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
782
783
784
        encryptWalletAction->setChecked(true);
        changePassphraseAction->setEnabled(true);
        encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
785
786
787
788
789
        break;
    case WalletModel::Locked:
        labelEncryptionIcon->show();
        labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
        labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
790
791
792
        encryptWalletAction->setChecked(true);
        changePassphraseAction->setEnabled(true);
        encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
793
794
795
        break;
    }
}
796

797
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
798
{
799
800
801
    // activateWindow() (sometimes) helps with keyboard focus on Windows
    if (isHidden())
    {
802
        show();
803
804
805
806
        activateWindow();
    }
    else if (isMinimized())
    {
807
        showNormal();
808
809
810
811
812
813
814
815
816
817
818
819
820
821
        activateWindow();
    }
    else if (GUIUtil::isObscured(this))
    {
        raise();
        activateWindow();
    }
    else if(fToggleHidden)
        hide();
}

void BitcoinGUI::toggleHidden()
{
    showNormalIfMinimized(true);
822
}
823
824
825
826
827
828

void BitcoinGUI::detectShutdown()
{
    if (ShutdownRequested())
        QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}