bitcoingui.cpp 28.7 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
#include "optionsdialog.h"
#include "aboutdialog.h"
12
#include "clientmodel.h"
13
#include "walletmodel.h"
14
#include "walletframe.h"
15
#include "optionsmodel.h"
16
#include "bitcoinunits.h"
17
#include "guiconstants.h"
18
#include "notificator.h"
19
#include "guiutil.h"
20
#include "rpcconsole.h"
21
#include "ui_interface.h"
22
#include "wallet.h"
23
#include "init.h"
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
24

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

29
#include <QApplication>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
30
31
32
33
34
35
36
#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
37
#include <QMessageBox>
38
#include <QProgressBar>
39
#include <QStackedWidget>
40
#include <QDateTime>
41
#include <QMovie>
42
#include <QTimer>
43
#include <QDragEnterEvent>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
44
#if QT_VERSION < 0x050000
45
#include <QUrl>
46
#include <QTextDocument>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
47
#endif
48
#include <QMimeData>
49
#include <QStyle>
50
#include <QListWidget>
51

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
52
53
#include <iostream>

54
55
const QString BitcoinGUI::DEFAULT_WALLET = "~Default";

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

Philip Kaufmann's avatar
Philip Kaufmann committed
69
#ifndef Q_OS_MAC
70
71
72
73
74
75
76
77
78
79
80
81
    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"));
    }
82
83
84
#else
    setUnifiedTitleAndToolBarOnMac(true);
    QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
85
86
87
88
89

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

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

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

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

103
104
    // Create application menu bar
    createMenuBar();
105

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

109
    // Create system tray icon and notification
110
    createTrayIcon(fIsTestnet);
111

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

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

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

141
142
143
    // 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
144
    QString curStyle = QApplication::style()->metaObject()->className();
145
146
147
148
149
    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; }");
    }

150
151
    statusBar()->addWidget(progressBarLabel);
    statusBar()->addWidget(progressBar);
152
    statusBar()->addPermanentWidget(frameBlocks);
153

154
155
    syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);

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

161
162
    // 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
163
164
}

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

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

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

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

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

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

208
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
209
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
Philip Kaufmann's avatar
Philip Kaufmann committed
210
211
212
213
    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()));
214
    connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
215
    connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
216

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

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

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

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

263
264
    connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
265
    connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
Philip Kaufmann's avatar
Philip Kaufmann committed
266
    connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
267
    connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
268
269
270
    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
271
272
    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab()));
273
274
    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
275
276
}

277
278
void BitcoinGUI::createMenuBar()
{
Philip Kaufmann's avatar
Philip Kaufmann committed
279
#ifdef Q_OS_MAC
280
281
282
283
284
285
286
287
288
    // 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"));
289
    file->addAction(backupWalletAction);
290
    file->addAction(signMessageAction);
291
    file->addAction(verifyMessageAction);
292
    file->addSeparator();
293
294
295
    file->addAction(usedSendingAddressesAction);
    file->addAction(usedReceivingAddressesAction);
    file->addSeparator();
296
297
298
299
300
301
302
303
304
    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"));
305
306
    help->addAction(openRPCConsoleAction);
    help->addSeparator();
307
    help->addAction(aboutAction);
308
    help->addAction(aboutQtAction);
309
310
311
312
313
314
315
316
317
318
319
320
}

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);
}

321
void BitcoinGUI::setClientModel(ClientModel *clientModel)
322
{
323
    this->clientModel = clientModel;
324
    if(clientModel)
325
    {
326
327
        // 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
328
        createTrayIconMenu();
329

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

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

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

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

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

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

void BitcoinGUI::removeAllWallets()
{
    walletFrame->removeAllWallets();
358
359
}

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

365
366
367
368
369
370
371
372
373
374
375
    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"));
    }

376
377
378
    trayIcon->show();
#endif

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

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

390
391
392
    trayIconMenu = new QMenu(this);
    trayIcon->setContextMenu(trayIconMenu);

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

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

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

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

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

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

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

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

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

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

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

480
481
void BitcoinGUI::setNumConnections(int count)
{
482
483
484
    QString icon;
    switch(count)
    {
485
486
487
488
489
    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;
490
    }
491
    labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
492
    labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
493
494
}

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

500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
    // 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;
516
517
    }

518
519
    QString tooltip;

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

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

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

539
        walletFrame->showOutOfSyncWarning(false);
540
541
542

        progressBarLabel->setVisible(false);
        progressBar->setVisible(false);
543
    }
544
545
    else
    {
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
        // 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));
563
564
        progressBar->setMaximum(1000000000);
        progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5);
565
566
        progressBar->setVisible(true);

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

573
        walletFrame->showOutOfSyncWarning(true);
574

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

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

584
    labelBlocksIcon->setToolTip(tooltip);
585
586
    progressBarLabel->setToolTip(tooltip);
    progressBar->setToolTip(tooltip);
587
588
}

589
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
590
{
591
    QString strTitle = tr("Bitcoin"); // default title
592
593
594
595
    // Default to information icon
    int nMBoxIcon = QMessageBox::Information;
    int nNotifyIcon = Notificator::Information;

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

    // 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;
        }
616
    }
617
    // Append title to "Bitcoin - "
618
619
    if (!msgType.isEmpty())
        strTitle += " - " + msgType;
620
621
622
623
624

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

    // Display message
632
    if (style & CClientUIInterface::MODAL) {
633
634
635
636
637
        // 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;

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

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

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

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

688
689
    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. "
690
        "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(clientModel->getOptionsModel()->getDisplayUnit(), nFeeRequired));
691
692
    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
693
694
    *payFee = (retval == QMessageBox::Yes);
}
695

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

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

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

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

    event->acceptProposedAction();
}

741
742
743
744
745
746
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
747
        if (progressBarLabel->isVisible() || progressBar->isVisible())
748
749
750
751
752
            return true;
    }
    return QMainWindow::eventFilter(object, event);
}

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

766
767
768
769
770
771
void BitcoinGUI::setEncryptionStatus(int status)
{
    switch(status)
    {
    case WalletModel::Unencrypted:
        labelEncryptionIcon->hide();
772
773
774
        encryptWalletAction->setChecked(false);
        changePassphraseAction->setEnabled(false);
        encryptWalletAction->setEnabled(true);
775
776
777
778
779
        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>"));
780
781
782
        encryptWalletAction->setChecked(true);
        changePassphraseAction->setEnabled(true);
        encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
783
784
785
786
787
        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>"));
788
789
790
        encryptWalletAction->setChecked(true);
        changePassphraseAction->setEnabled(true);
        encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
791
792
793
        break;
    }
}
794

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

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

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