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
8
9

#include <QApplication>

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
10
#include "bitcoingui.h"
11

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

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

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>
sje397's avatar
sje397 committed
44
45
#include <QFileDialog>
#include <QDesktopServices>
46
#include <QTimer>
47
48
#include <QDragEnterEvent>
#include <QUrl>
49
#include <QMimeData>
50
#include <QStyle>
51
#include <QSettings>
52
#include <QDesktopWidget>
53
#include <QListWidget>
54

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

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

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
59
BitcoinGUI::BitcoinGUI(QWidget *parent):
60
61
    QMainWindow(parent),
    clientModel(0),
62
63
    encryptWalletAction(0),
    changePassphraseAction(0),
64
    aboutQtAction(0),
65
    trayIcon(0),
66
    notificator(0),
67
68
    rpcConsole(0),
    prevBlocks(0)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
69
{
70
    restoreWindowGeometry();
71
    setWindowTitle(tr("Bitcoin") + " - " + tr("Wallet"));
Philip Kaufmann's avatar
Philip Kaufmann committed
72
#ifndef Q_OS_MAC
73
    qApp->setWindowIcon(QIcon(":icons/bitcoin"));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
74
    setWindowIcon(QIcon(":icons/bitcoin"));
75
76
77
78
#else
    setUnifiedTitleAndToolBarOnMac(true);
    QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
79
80
    // Accept D&D of URIs
    setAcceptDrops(true);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
81

82
    // Create actions for the toolbar, menu bar and tray/dock icon
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
83
    createActions();
84

85
86
    // Create application menu bar
    createMenuBar();
87

88
89
    // Create the toolbars
    createToolBars();
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
90

91
    // Create system tray icon and notification
92
    createTrayIcon();
93

94
95
96
97
    // Create wallet frame and make it the central widget
    walletFrame = new WalletFrame(this);
    setCentralWidget(walletFrame);
    
98
    // Create status bar
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
99
    statusBar();
100

101
    // Status bar notification icons
102
    QFrame *frameBlocks = new QFrame();
103
104
105
    frameBlocks->setContentsMargins(0,0,0,0);
    frameBlocks->setMinimumWidth(56);
    frameBlocks->setMaximumWidth(56);
106
107
108
    QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
    frameBlocksLayout->setContentsMargins(3,0,3,0);
    frameBlocksLayout->setSpacing(3);
109
    labelEncryptionIcon = new QLabel();
110
    labelConnectionsIcon = new QLabel();
111
    labelBlocksIcon = new QLabel();
112
    frameBlocksLayout->addStretch();
113
114
    frameBlocksLayout->addWidget(labelEncryptionIcon);
    frameBlocksLayout->addStretch();
115
116
    frameBlocksLayout->addWidget(labelConnectionsIcon);
    frameBlocksLayout->addStretch();
117
118
    frameBlocksLayout->addWidget(labelBlocksIcon);
    frameBlocksLayout->addStretch();
119

120
121
    // Progress bar and label for blocks download
    progressBarLabel = new QLabel();
122
123
    progressBarLabel->setVisible(false);
    progressBar = new QProgressBar();
124
    progressBar->setAlignment(Qt::AlignCenter);
125
126
    progressBar->setVisible(false);

127
128
129
130
131
132
133
134
135
    // 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
    QString curStyle = qApp->style()->metaObject()->className();
    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; }");
    }

136
137
    statusBar()->addWidget(progressBarLabel);
    statusBar()->addWidget(progressBar);
138
    statusBar()->addPermanentWidget(frameBlocks);
139

140
141
    syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);

142
143
144
    rpcConsole = new RPCConsole(this);
    connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show()));

145
146
    // 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
147
148
}

149
150
BitcoinGUI::~BitcoinGUI()
{
151
    saveWindowGeometry();
152
153
    if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
        trayIcon->hide();
Philip Kaufmann's avatar
Philip Kaufmann committed
154
#ifdef Q_OS_MAC
155
156
157
158
    delete appMenuBar;
#endif
}

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
159
160
void BitcoinGUI::createActions()
{
161
    QActionGroup *tabGroup = new QActionGroup(this);
162

163
    overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this);
164
165
    overviewAction->setStatusTip(tr("Show general overview of wallet"));
    overviewAction->setToolTip(overviewAction->statusTip());
166
    overviewAction->setCheckable(true);
167
    overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1));
168
    tabGroup->addAction(overviewAction);
169

Philip Kaufmann's avatar
Philip Kaufmann committed
170
    sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this);
171
172
    sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
    sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
Philip Kaufmann's avatar
Philip Kaufmann committed
173
174
175
176
177
    sendCoinsAction->setCheckable(true);
    sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
    tabGroup->addAction(sendCoinsAction);

    receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this);
178
179
    receiveCoinsAction->setStatusTip(tr("Show the list of addresses for receiving payments"));
    receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
Philip Kaufmann's avatar
Philip Kaufmann committed
180
181
182
183
    receiveCoinsAction->setCheckable(true);
    receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
    tabGroup->addAction(receiveCoinsAction);

184
    historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this);
185
186
    historyAction->setStatusTip(tr("Browse transaction history"));
    historyAction->setToolTip(historyAction->statusTip());
187
    historyAction->setCheckable(true);
188
    historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
189
190
    tabGroup->addAction(historyAction);

191
    addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this);
192
193
    addressBookAction->setStatusTip(tr("Edit the list of stored addresses and labels"));
    addressBookAction->setToolTip(addressBookAction->statusTip());
194
    addressBookAction->setCheckable(true);
195
    addressBookAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5));
196
197
    tabGroup->addAction(addressBookAction);

198
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
199
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
Philip Kaufmann's avatar
Philip Kaufmann committed
200
201
202
203
    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()));
204
    connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
205
    connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
206
    connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
207
    connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage()));
208
    
209
    quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
210
    quitAction->setStatusTip(tr("Quit application"));
211
    quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
212
    quitAction->setMenuRole(QAction::QuitRole);
213
    aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin"), this);
214
    aboutAction->setStatusTip(tr("Show information about Bitcoin"));
215
    aboutAction->setMenuRole(QAction::AboutRole);
216
    aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this);
217
    aboutQtAction->setStatusTip(tr("Show information about Qt"));
218
    aboutQtAction->setMenuRole(QAction::AboutQtRole);
219
    optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
220
    optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin"));
221
    optionsAction->setMenuRole(QAction::PreferencesRole);
222
    toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this);
223
    toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
224
    encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
225
    encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
226
    encryptWalletAction->setCheckable(true);
227
    backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
228
    backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
229
    changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this);
230
    changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
Philip Kaufmann's avatar
Philip Kaufmann committed
231
    signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this);
232
    signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
Philip Kaufmann's avatar
Philip Kaufmann committed
233
    verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this);
234
    verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
Philip Kaufmann's avatar
Philip Kaufmann committed
235
236

    exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
237
238
    exportAction->setStatusTip(tr("Export the data in the current tab to a file"));
    exportAction->setToolTip(exportAction->statusTip());
239
    openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this);
240
    openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console"));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
241

242
243
    connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
244
    connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
Philip Kaufmann's avatar
Philip Kaufmann committed
245
    connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
246
    connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
247
    connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool)));
sje397's avatar
sje397 committed
248
    connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet()));
249
    connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
Philip Kaufmann's avatar
Philip Kaufmann committed
250
251
    connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab()));
    connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab()));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
252
253
}

254
255
void BitcoinGUI::createMenuBar()
{
Philip Kaufmann's avatar
Philip Kaufmann committed
256
#ifdef Q_OS_MAC
257
258
259
260
261
262
263
264
265
    // 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"));
266
    file->addAction(backupWalletAction);
267
    file->addAction(exportAction);
268
    file->addAction(signMessageAction);
269
    file->addAction(verifyMessageAction);
270
    file->addSeparator();
271
272
273
274
275
276
277
278
279
    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"));
280
281
    help->addAction(openRPCConsoleAction);
    help->addSeparator();
282
    help->addAction(aboutAction);
283
    help->addAction(aboutQtAction);
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
}

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);
    toolbar->addAction(addressBookAction);

    QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
    toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    toolbar2->addAction(exportAction);
}

301
void BitcoinGUI::setClientModel(ClientModel *clientModel)
302
{
303
    this->clientModel = clientModel;
304
    if(clientModel)
305
    {
306
        // Replace some strings and icons, when using the testnet
307
308
        if(clientModel->isTestNet())
        {
309
            setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]"));
Philip Kaufmann's avatar
Philip Kaufmann committed
310
#ifndef Q_OS_MAC
311
            qApp->setWindowIcon(QIcon(":icons/bitcoin_testnet"));
312
            setWindowIcon(QIcon(":icons/bitcoin_testnet"));
313
#else
314
            MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
315
#endif
316
317
            if(trayIcon)
            {
318
319
                // Just attach " [testnet]" to the existing tooltip
                trayIcon->setToolTip(trayIcon->toolTip() + QString(" ") + tr("[testnet]"));
320
321
                trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
            }
322

323
            toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet"));
324
            aboutAction->setIcon(QIcon(":/icons/toolbar_testnet"));
325
326
        }

327
328
        // 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
329
        createTrayIconMenu();
330

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

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

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

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
}

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
360
361
void BitcoinGUI::createTrayIcon()
{
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
    trayIcon->setToolTip(tr("Bitcoin client"));
366
    trayIcon->setIcon(QIcon(":/icons/toolbar"));
367
368
369
370
371
372
373
374
375
376
    trayIcon->show();
#endif

    notificator = new Notificator(qApp->applicationName(), trayIcon);
}

void BitcoinGUI::createTrayIconMenu()
{
    QMenu *trayIconMenu;
#ifndef Q_OS_MAC
377
378
379
380
    // return if trayIcon is unset (only on non-Mac OSes)
    if (!trayIcon)
        return;

381
382
383
    trayIconMenu = new QMenu(this);
    trayIcon->setContextMenu(trayIconMenu);

384
385
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
386
387
388
389
390
391
392
#else
    // Note: On Mac, the dock icon is used to provide the tray's functionality.
    MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
    trayIconMenu = dockIconHandler->dockMenu();
#endif

    // Configuration of the tray icon (or dock icon) icon menu
393
    trayIconMenu->addAction(toggleHideAction);
394
    trayIconMenu->addSeparator();
395
396
    trayIconMenu->addAction(sendCoinsAction);
    trayIconMenu->addAction(receiveCoinsAction);
397
    trayIconMenu->addSeparator();
398
    trayIconMenu->addAction(signMessageAction);
399
    trayIconMenu->addAction(verifyMessageAction);
400
401
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(optionsAction);
402
    trayIconMenu->addAction(openRPCConsoleAction);
Philip Kaufmann's avatar
Philip Kaufmann committed
403
#ifndef Q_OS_MAC // This is built-in on Mac
404
405
406
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(quitAction);
#endif
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
407
408
}

Philip Kaufmann's avatar
Philip Kaufmann committed
409
#ifndef Q_OS_MAC
410
411
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
412
    if(reason == QSystemTrayIcon::Trigger)
413
    {
414
        // Click on system tray icon triggers show/hide of the main window
415
        toggleHideAction->trigger();
416
417
    }
}
418
#endif
419

420
421
422
423
424
425
426
427
428
429
430
431
void BitcoinGUI::saveWindowGeometry()
{
    QSettings settings;
    settings.setValue("nWindowPos", pos());
    settings.setValue("nWindowSize", size());
}

void BitcoinGUI::restoreWindowGeometry()
{
    QSettings settings;
    QPoint pos = settings.value("nWindowPos").toPoint();
    QSize size = settings.value("nWindowSize", QSize(850, 550)).toSize();
432
433
434
435
436
437
    if (!pos.x() && !pos.y())
    {
        QRect screen = qApp->desktop()->screenGeometry();
        pos.setX((screen.width()-size.width())/2);
        pos.setY((screen.height()-size.height())/2);
    }
438
439
440
441
    resize(size);
    move(pos);
}

442
443
void BitcoinGUI::optionsClicked()
{
444
445
    if(!clientModel || !clientModel->getOptionsModel())
        return;
446
    OptionsDialog dlg;
447
    dlg.setModel(clientModel->getOptionsModel());
448
    dlg.exec();
449
450
}

451
void BitcoinGUI::aboutClicked()
452
{
453
    AboutDialog dlg;
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
454
    dlg.setModel(clientModel);
455
    dlg.exec();
456
457
}

458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
void BitcoinGUI::gotoOverviewPage()
{
    if (walletFrame) walletFrame->gotoOverviewPage();
}

void BitcoinGUI::gotoHistoryPage()
{
    if (walletFrame) walletFrame->gotoHistoryPage();
}

void BitcoinGUI::gotoAddressBookPage()
{
    if (walletFrame) walletFrame->gotoAddressBookPage();
}

void BitcoinGUI::gotoReceiveCoinsPage()
{
    if (walletFrame) walletFrame->gotoReceiveCoinsPage();
}

void BitcoinGUI::gotoSendCoinsPage()
{
    if (walletFrame) walletFrame->gotoSendCoinsPage();
}

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

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

493
494
void BitcoinGUI::setNumConnections(int count)
{
495
496
497
    QString icon;
    switch(count)
    {
498
499
500
501
502
    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;
503
    }
504
    labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
505
    labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
506
507
}

508
void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks)
509
{
510
511
512
    // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text)
    statusBar()->clearMessage();

513
    // don't show / hide progress bar and its label if we have no connection to the network
514
515
    enum BlockSource blockSource = clientModel ? clientModel->getBlockSource() : BLOCK_SOURCE_NONE;
    if (blockSource == BLOCK_SOURCE_NONE || (blockSource == BLOCK_SOURCE_NETWORK && clientModel->getNumConnections() == 0))
516
517
518
519
    {
        progressBarLabel->setVisible(false);
        progressBar->setVisible(false);

520
        return;
521
522
    }

523
524
    QString tooltip;

525
526
527
528
529
    QString importText;
    switch (blockSource) {
    case BLOCK_SOURCE_NONE:
    case BLOCK_SOURCE_NETWORK:
        importText = tr("Synchronizing with network...");
Pieter Wuille's avatar
Pieter Wuille committed
530
        break;
531
532
    case BLOCK_SOURCE_DISK:
        importText = tr("Importing blocks from disk...");
Pieter Wuille's avatar
Pieter Wuille committed
533
        break;
534
535
536
537
    case BLOCK_SOURCE_REINDEX:
        importText = tr("Reindexing blocks on disk...");
    }

538
    QDateTime lastBlockDate = clientModel->getLastBlockDate();
539
540
    QDateTime currentDate = QDateTime::currentDateTime();
    int secs = lastBlockDate.secsTo(currentDate);
541

542
    if(count < nTotalBlocks)
543
    {
544
        tooltip = tr("Processed %1 of %2 (estimated) blocks of transaction history.").arg(count).arg(nTotalBlocks);
545
546
547
    }
    else
    {
548
        tooltip = tr("Processed %1 blocks of transaction history.").arg(count);
549
    }
550

551
    // Set icon state: spinning if catching up, tick otherwise
552
    if(secs < 90*60 && count >= nTotalBlocks)
553
    {
554
        tooltip = tr("Up to date") + QString(".<br>") + tooltip;
555
        labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
556

557
        walletFrame->showOutOfSyncWarning(false);
558
559
560

        progressBarLabel->setVisible(false);
        progressBar->setVisible(false);
561
    }
562
563
    else
    {
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
        // 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->setText(importText);
        progressBarLabel->setVisible(true);
        progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
582
583
        progressBar->setMaximum(1000000000);
        progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5);
584
585
        progressBar->setVisible(true);

586
        tooltip = tr("Catching up...") + QString("<br>") + tooltip;
587
        labelBlocksIcon->setMovie(syncIconMovie);
588
589
590
        if(count != prevBlocks)
            syncIconMovie->jumpToNextFrame();
        prevBlocks = count;
591

592
        walletFrame->showOutOfSyncWarning(true);
593

594
        tooltip += QString("<br>");
595
596
597
        tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText);
        tooltip += QString("<br>");
        tooltip += tr("Transactions after this will not yet be visible.");
598
    }
599

600
601
602
    // Don't word-wrap this (fixed-width) tooltip
    tooltip = QString("<nobr>") + tooltip + QString("</nobr>");

603
    labelBlocksIcon->setToolTip(tooltip);
604
605
    progressBarLabel->setToolTip(tooltip);
    progressBar->setToolTip(tooltip);
606
607
}

608
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
609
{
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
    QString strTitle = tr("Bitcoin") + " - ";
    // Default to information icon
    int nMBoxIcon = QMessageBox::Information;
    int nNotifyIcon = Notificator::Information;

    // Check for usage of predefined title
    switch (style) {
    case CClientUIInterface::MSG_ERROR:
        strTitle += tr("Error");
        break;
    case CClientUIInterface::MSG_WARNING:
        strTitle += tr("Warning");
        break;
    case CClientUIInterface::MSG_INFORMATION:
        strTitle += tr("Information");
        break;
    default:
        strTitle += title; // Use supplied title
    }

    // Check for error/warning icon
    if (style & CClientUIInterface::ICON_ERROR) {
        nMBoxIcon = QMessageBox::Critical;
        nNotifyIcon = Notificator::Critical;
634
    }
635
636
637
638
639
640
    else if (style & CClientUIInterface::ICON_WARNING) {
        nMBoxIcon = QMessageBox::Warning;
        nNotifyIcon = Notificator::Warning;
    }

    // Display message
641
    if (style & CClientUIInterface::MODAL) {
642
643
644
645
646
647
        // 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;

        QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons);
648
649
650
        int r = mBox.exec();
        if (ret != NULL)
            *ret = r == QMessageBox::Ok;
651
652
653
    }
    else
        notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
654
}
655
656
657

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

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

void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
{
692
693
694
    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. "
        "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired));
Wladimir J. van der Laan's avatar
ask fee    
Wladimir J. van der Laan committed
695
    QMessageBox::StandardButton retval = QMessageBox::question(
696
          this, tr("Confirm transaction fee"), strMessage,
Wladimir J. van der Laan's avatar
ask fee    
Wladimir J. van der Laan committed
697
698
699
          QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes);
    *payFee = (retval == QMessageBox::Yes);
}
700

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

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

void BitcoinGUI::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasUrls())
    {
726
        int nValidUrisFound = 0;
727
728
        QList<QUrl> uris = event->mimeData()->urls();
        foreach(const QUrl &uri, uris)
729
        {
730
            if (walletFrame->handleURI(uri.toString()))
731
                nValidUrisFound++;
732
        }
733
734
735

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

    event->acceptProposedAction();
}

745
746
747
748
749
750
751
752
753
754
755
756
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
        if (progressBarLabel->isVisible() && progressBar->isVisible())
            return true;
    }
    return QMainWindow::eventFilter(object, event);
}

757
void BitcoinGUI::handleURI(QString strURI)
758
{
759
    // URI has to be valid
760
    if (!walletFrame->handleURI(strURI))
761
762
        message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
                  CClientUIInterface::ICON_WARNING);
763
764
}

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

void BitcoinGUI::encryptWallet(bool status)
{
796
    walletFrame->encryptWallet(status);
797
798
}

sje397's avatar
sje397 committed
799
800
void BitcoinGUI::backupWallet()
{
801
    walletFrame->backupWallet();
sje397's avatar
sje397 committed
802
803
}

804
805
void BitcoinGUI::changePassphrase()
{
806
    walletFrame->changePassphrase();
807
808
809
810
}

void BitcoinGUI::unlockWallet()
{
811
    walletFrame->unlockWallet();
812
}
813

814
void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
815
{
816
817
818
    // activateWindow() (sometimes) helps with keyboard focus on Windows
    if (isHidden())
    {
819
        show();
820
821
822
823
        activateWindow();
    }
    else if (isMinimized())
    {
824
        showNormal();
825
826
827
828
829
830
831
832
833
834
835
836
837
838
        activateWindow();
    }
    else if (GUIUtil::isObscured(this))
    {
        raise();
        activateWindow();
    }
    else if(fToggleHidden)
        hide();
}

void BitcoinGUI::toggleHidden()
{
    showNormalIfMinimized(true);
839
}