bitcoingui.cpp 28.9 KB
Newer Older
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
1
/*
2
3
 * Qt4 bitcoin GUI.
 *
Gavin Andresen's avatar
Gavin Andresen committed
4
5
 * W.J. van der Laan 20011-2012
 * The Bitcoin Developers 20011-2012
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
6
 */
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
7
8
#include "bitcoingui.h"
#include "transactiontablemodel.h"
9
#include "addressbookpage.h"
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
10
#include "sendcoinsdialog.h"
11
#include "messagepage.h"
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
12
13
#include "optionsdialog.h"
#include "aboutdialog.h"
14
#include "clientmodel.h"
15
#include "walletmodel.h"
16
#include "editaddressdialog.h"
17
#include "optionsmodel.h"
18
#include "transactiondescdialog.h"
19
#include "addresstablemodel.h"
20
#include "transactionview.h"
21
#include "overviewpage.h"
22
#include "bitcoinunits.h"
23
#include "guiconstants.h"
24
#include "askpassphrasedialog.h"
25
#include "notificator.h"
26
#include "guiutil.h"
27
#include "rpcconsole.h"
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
28

29
30
31
32
#ifdef Q_WS_MAC
#include "macdockiconhandler.h"
#endif

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
33
34
35
36
37
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QIcon>
38
#include <QTabWidget>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
39
40
41
42
43
44
#include <QVBoxLayout>
#include <QToolBar>
#include <QStatusBar>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
45
#include <QLocale>
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
46
#include <QMessageBox>
47
#include <QProgressBar>
48
#include <QStackedWidget>
49
#include <QDateTime>
50
#include <QMovie>
sje397's avatar
sje397 committed
51
52
#include <QFileDialog>
#include <QDesktopServices>
53
#include <QTimer>
54

55
56
57
#include <QDragEnterEvent>
#include <QUrl>

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
58
59
60
#include <iostream>

BitcoinGUI::BitcoinGUI(QWidget *parent):
61
62
63
    QMainWindow(parent),
    clientModel(0),
    walletModel(0),
64
65
    encryptWalletAction(0),
    changePassphraseAction(0),
66
    aboutQtAction(0),
67
    trayIcon(0),
68
69
    notificator(0),
    rpcConsole(0)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
70
71
{
    resize(850, 550);
72
    setWindowTitle(tr("Bitcoin Wallet"));
73
#ifndef Q_WS_MAC
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
92
    // Create the tray icon (or setup the dock icon)
    createTrayIcon();
93

94
    // Create tabs
95
    overviewPage = new OverviewPage();
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
96

97
98
    transactionsPage = new QWidget(this);
    QVBoxLayout *vbox = new QVBoxLayout();
99
100
    transactionView = new TransactionView(this);
    vbox->addWidget(transactionView);
101
102
    transactionsPage->setLayout(vbox);

103
104
105
106
107
108
    addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab);

    receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab);

    sendCoinsPage = new SendCoinsDialog(this);

109
110
    messagePage = new MessagePage(this);

111
112
113
    centralWidget = new QStackedWidget(this);
    centralWidget->addWidget(overviewPage);
    centralWidget->addWidget(transactionsPage);
114
115
116
    centralWidget->addWidget(addressBookPage);
    centralWidget->addWidget(receiveCoinsPage);
    centralWidget->addWidget(sendCoinsPage);
117
118
119
#ifdef FIRST_CLASS_MESSAGING
    centralWidget->addWidget(messagePage);
#endif
120
    setCentralWidget(centralWidget);
121

122
    // Create status bar
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
123
    statusBar();
124

125
    // Status bar notification icons
126
    QFrame *frameBlocks = new QFrame();
127
128
129
    frameBlocks->setContentsMargins(0,0,0,0);
    frameBlocks->setMinimumWidth(56);
    frameBlocks->setMaximumWidth(56);
130
131
132
    QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
    frameBlocksLayout->setContentsMargins(3,0,3,0);
    frameBlocksLayout->setSpacing(3);
133
    labelEncryptionIcon = new QLabel();
134
    labelConnectionsIcon = new QLabel();
135
    labelBlocksIcon = new QLabel();
136
    frameBlocksLayout->addStretch();
137
138
    frameBlocksLayout->addWidget(labelEncryptionIcon);
    frameBlocksLayout->addStretch();
139
140
    frameBlocksLayout->addWidget(labelConnectionsIcon);
    frameBlocksLayout->addStretch();
141
142
    frameBlocksLayout->addWidget(labelBlocksIcon);
    frameBlocksLayout->addStretch();
143

144
145
    // Progress bar and label for blocks download
    progressBarLabel = new QLabel();
146
147
    progressBarLabel->setVisible(false);
    progressBar = new QProgressBar();
148
    progressBar->setAlignment(Qt::AlignCenter);
149
150
151
152
    progressBar->setVisible(false);

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

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

157
    // Clicking on a transaction on the overview page simply sends you to transaction history page
158
159
    connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage()));

160
161
    // Doubleclicking on a transaction on the transaction history page shows details
    connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
162

163
164
165
    rpcConsole = new RPCConsole(this);
    connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show()));

166
    gotoOverviewPage();
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
167
168
}

169
170
BitcoinGUI::~BitcoinGUI()
{
171
172
    if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
        trayIcon->hide();
173
174
175
176
177
#ifdef Q_WS_MAC
    delete appMenuBar;
#endif
}

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

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

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

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

    receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this);
    receiveCoinsAction->setToolTip(tr("Show the list of addresses for receiving payments"));
    receiveCoinsAction->setCheckable(true);
203
    receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
204
205
206
207
208
    tabGroup->addAction(receiveCoinsAction);

    sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this);
    sendCoinsAction->setToolTip(tr("Send coins to a bitcoin address"));
    sendCoinsAction->setCheckable(true);
209
    sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
210
211
    tabGroup->addAction(sendCoinsAction);

212
    messageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this);
213
214
215
216
217
218
    messageAction->setToolTip(tr("Prove you control an address"));
#ifdef FIRST_CLASS_MESSAGING
    messageAction->setCheckable(true);
#endif
    tabGroup->addAction(messageAction);

219
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
220
    connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
221
    connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
222
    connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
223
    connect(addressBookAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
224
    connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage()));
225
    connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
226
    connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
227
    connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
228
    connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage()));
229
    connect(messageAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
230
    connect(messageAction, SIGNAL(triggered()), this, SLOT(gotoMessagePage()));
231

232
    quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
233
    quitAction->setToolTip(tr("Quit application"));
234
    quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
235
236
    quitAction->setMenuRole(QAction::QuitRole);
    aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About %1").arg(qApp->applicationName()), this);
237
    aboutAction->setToolTip(tr("Show information about Bitcoin"));
238
239
240
241
    aboutAction->setMenuRole(QAction::AboutRole);
    aboutQtAction = new QAction(tr("About &Qt"), this);
    aboutQtAction->setToolTip(tr("Show information about Qt"));
    aboutQtAction->setMenuRole(QAction::AboutQtRole);
242
243
    optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
    optionsAction->setToolTip(tr("Modify configuration options for bitcoin"));
244
    optionsAction->setMenuRole(QAction::PreferencesRole);
245
    toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("Show/Hide &Bitcoin"), this);
246
    toggleHideAction->setToolTip(tr("Show or hide the Bitcoin window"));
247
    exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
248
    exportAction->setToolTip(tr("Export the data in the current tab to a file"));
249
    encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
250
251
    encryptWalletAction->setToolTip(tr("Encrypt or decrypt wallet"));
    encryptWalletAction->setCheckable(true);
252
    backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
sje397's avatar
sje397 committed
253
    backupWalletAction->setToolTip(tr("Backup wallet to another location"));
254
    changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this);
255
    changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption"));
256
257
    openRPCConsoleAction = new QAction(tr("&Debug window"), this);
    openRPCConsoleAction->setToolTip(tr("Open debugging and diagnostic console"));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
258

259
260
261
    connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
    connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
262
    connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
263
    connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
264
    connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool)));
sje397's avatar
sje397 committed
265
    connect(backupWalletAction, SIGNAL(triggered()), this, SLOT(backupWallet()));
266
    connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
267
268
}

269
270
271
272
273
274
275
276
277
278
279
280
void BitcoinGUI::createMenuBar()
{
#ifdef Q_WS_MAC
    // 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"));
281
    file->addAction(backupWalletAction);
282
    file->addAction(exportAction);
283
284
285
#ifndef FIRST_CLASS_MESSAGING
    file->addAction(messageAction);
#endif
286
    file->addSeparator();
287
288
289
290
291
292
293
294
295
    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"));
296
297
    help->addAction(openRPCConsoleAction);
    help->addSeparator();
298
    help->addAction(aboutAction);
299
    help->addAction(aboutQtAction);
300
301
302
303
304
305
306
307
308
309
310
}

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);
311
312
313
#ifdef FIRST_CLASS_MESSAGING
    toolbar->addAction(messageAction);
#endif
314
315
316
317
318
319

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

320
void BitcoinGUI::setClientModel(ClientModel *clientModel)
321
{
322
    this->clientModel = clientModel;
323
    if(clientModel)
324
    {
325
326
        if(clientModel->isTestNet())
        {
327
            setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]"));
328
#ifndef Q_WS_MAC
329
            setWindowIcon(QIcon(":icons/bitcoin_testnet"));
330
#else
331
            MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
332
#endif
333
334
            if(trayIcon)
            {
335
                trayIcon->setToolTip(tr("Bitcoin client") + QString(" ") + tr("[testnet]"));
336
                trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
337
                toggleHideAction->setIcon(QIcon(":/icons/toolbar_testnet"));
338
            }
339
340
        }

341
342
343
        // Keep up to date with client
        setNumConnections(clientModel->getNumConnections());
        connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
344

345
346
        setNumBlocks(clientModel->getNumBlocks());
        connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
347

348
        // Report errors from network/worker thread
349
        connect(clientModel, SIGNAL(error(QString,QString, bool)), this, SLOT(error(QString,QString,bool)));
350
351

        rpcConsole->setClientModel(clientModel);
352
    }
353
354
355
356
357
}

void BitcoinGUI::setWalletModel(WalletModel *walletModel)
{
    this->walletModel = walletModel;
358
359
360
    if(walletModel)
    {
        // Report errors from wallet thread
361
        connect(walletModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool)));
362

363
364
        // Put transaction list in tabs
        transactionView->setModel(walletModel);
365

366
367
368
369
        overviewPage->setModel(walletModel);
        addressBookPage->setModel(walletModel->getAddressTableModel());
        receiveCoinsPage->setModel(walletModel->getAddressTableModel());
        sendCoinsPage->setModel(walletModel);
370
        messagePage->setModel(walletModel);
371

372
373
        setEncryptionStatus(walletModel->getEncryptionStatus());
        connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int)));
374

375
376
377
        // Balloon popup for new transaction
        connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
                this, SLOT(incomingTransaction(QModelIndex,int,int)));
378

379
380
381
        // Ask for passphrase if needed
        connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));
    }
382
383
}

Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
384
385
void BitcoinGUI::createTrayIcon()
{
386
387
    QMenu *trayIconMenu;
#ifndef Q_WS_MAC
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
388
    trayIcon = new QSystemTrayIcon(this);
389
    trayIconMenu = new QMenu(this);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
390
    trayIcon->setContextMenu(trayIconMenu);
391
    trayIcon->setToolTip(tr("Bitcoin client"));
392
    trayIcon->setIcon(QIcon(":/icons/toolbar"));
393
394
    connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
395
    trayIcon->show();
396
397
398
#else
    // Note: On Mac, the dock icon is used to provide the tray's functionality.
    MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
Gavin Andresen's avatar
Gavin Andresen committed
399
    connect(dockIconHandler, SIGNAL(dockIconClicked()), toggleHideAction, SLOT(trigger()));
400
401
402
403
    trayIconMenu = dockIconHandler->dockMenu();
#endif

    // Configuration of the tray icon (or dock icon) icon menu
404
    trayIconMenu->addAction(toggleHideAction);
405
    trayIconMenu->addSeparator();
406
407
408
409
    trayIconMenu->addAction(messageAction);
#ifndef FIRST_CLASS_MESSAGING
    trayIconMenu->addSeparator();
#endif
410
411
412
413
414
415
416
417
    trayIconMenu->addAction(receiveCoinsAction);
    trayIconMenu->addAction(sendCoinsAction);
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(optionsAction);
#ifndef Q_WS_MAC // This is built-in on Mac
    trayIconMenu->addSeparator();
    trayIconMenu->addAction(quitAction);
#endif
418
419

    notificator = new Notificator(tr("bitcoin-qt"), trayIcon);
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
420
421
}

422
#ifndef Q_WS_MAC
423
424
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
425
    if(reason == QSystemTrayIcon::Trigger)
426
    {
427
        // Click on system tray icon triggers "show/hide bitcoin"
428
        toggleHideAction->trigger();
429
430
    }
}
431
#endif
432

433
434
435
void BitcoinGUI::toggleHidden()
{
    // activateWindow() (sometimes) helps with keyboard focus on Windows
436
    if (isHidden())
437
438
439
440
    {
        show();
        activateWindow();
    }
441
    else if (isMinimized())
442
443
444
445
    {
        showNormal();
        activateWindow();
    }
446
    else if (GUIUtil::isObscured(this))
447
448
449
450
451
452
453
454
    {
        raise();
        activateWindow();
    }
    else
        hide();
}

455
456
void BitcoinGUI::optionsClicked()
{
457
458
    if(!clientModel || !clientModel->getOptionsModel())
        return;
459
    OptionsDialog dlg;
460
    dlg.setModel(clientModel->getOptionsModel());
461
    dlg.exec();
462
463
}

464
void BitcoinGUI::aboutClicked()
465
{
466
    AboutDialog dlg;
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
467
    dlg.setModel(clientModel);
468
    dlg.exec();
469
470
}

471
472
void BitcoinGUI::setNumConnections(int count)
{
473
474
475
    QString icon;
    switch(count)
    {
476
477
478
479
480
    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;
481
    }
482
    labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
483
    labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
484
485
486
487
}

void BitcoinGUI::setNumBlocks(int count)
{
488
489
490
491
492
493
    // don't show / hide progressBar and it's label if we have no connection(s) to the network
    if (!clientModel || clientModel->getNumConnections() == 0)
    {
        progressBarLabel->setVisible(false);
        progressBar->setVisible(false);

494
        return;
495
496
    }

497
    int nTotalBlocks = clientModel->getNumBlocksOfPeers();
498
499
    QString tooltip;

500
    if(count < nTotalBlocks)
501
    {
502
        int nRemainingBlocks = nTotalBlocks - count;
503
        float nPercentageDone = count / (nTotalBlocks * 0.01f);
504

505
506
507
        if (clientModel->getStatusBarWarnings() == "")
        {
            progressBarLabel->setText(tr("Synchronizing with network..."));
508
            progressBarLabel->setVisible(true);
509
            progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks));
510
            progressBar->setMaximum(nTotalBlocks);
511
            progressBar->setValue(count);
512
            progressBar->setVisible(true);
513
514
515
516
517
518
519
        }
        else
        {
            progressBarLabel->setText(clientModel->getStatusBarWarnings());
            progressBarLabel->setVisible(true);
            progressBar->setVisible(false);
        }
520
        tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2);
521
522
523
    }
    else
    {
524
525
526
527
528
529
530
        if (clientModel->getStatusBarWarnings() == "")
            progressBarLabel->setVisible(false);
        else
        {
            progressBarLabel->setText(clientModel->getStatusBarWarnings());
            progressBarLabel->setVisible(true);
        }
531
        progressBar->setVisible(false);
532
        tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count);
533
534
    }

535
536
537
538
539
    QDateTime now = QDateTime::currentDateTime();
    QDateTime lastBlockDate = clientModel->getLastBlockDate();
    int secs = lastBlockDate.secsTo(now);
    QString text;

540
    // Represent time from last generated block in human readable text
541
542
543
544
545
    if(secs <= 0)
    {
        // Fully up to date. Leave text empty.
    }
    else if(secs < 60)
546
    {
547
        text = tr("%n second(s) ago","",secs);
548
549
550
551
552
553
554
555
556
557
558
559
560
    }
    else if(secs < 60*60)
    {
        text = tr("%n minute(s) ago","",secs/60);
    }
    else if(secs < 24*60*60)
    {
        text = tr("%n hour(s) ago","",secs/(60*60));
    }
    else
    {
        text = tr("%n day(s) ago","",secs/(60*60*24));
    }
561

562
    // Set icon state: spinning if catching up, tick otherwise
563
    if(secs < 90*60 && count >= nTotalBlocks)
564
    {
565
        tooltip = tr("Up to date") + QString(".\n") + tooltip;
566
        labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
567
    }
568
569
570
    else
    {
        tooltip = tr("Catching up...") + QString("\n") + tooltip;
571
572
        labelBlocksIcon->setMovie(syncIconMovie);
        syncIconMovie->start();
573
    }
574

575
576
577
578
579
    if(!text.isEmpty())
    {
        tooltip += QString("\n");
        tooltip += tr("Last received block was generated %1.").arg(text);
    }
580
581

    labelBlocksIcon->setToolTip(tooltip);
582
583
    progressBarLabel->setToolTip(tooltip);
    progressBar->setToolTip(tooltip);
584
585
}

586
void BitcoinGUI::error(const QString &title, const QString &message, bool modal)
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
587
{
588
    // Report errors from network/worker thread
589
590
591
592
593
594
    if(modal)
    {
        QMessageBox::critical(this, title, message, QMessageBox::Ok, QMessageBox::Ok);
    } else {
        notificator->notify(Notificator::Critical, title, message);
    }
Wladimir J. van der Laan's avatar
Wladimir J. van der Laan committed
595
}
596
597
598

void BitcoinGUI::changeEvent(QEvent *e)
{
599
    QMainWindow::changeEvent(e);
600
#ifndef Q_WS_MAC // Ignored on Mac
601
    if(e->type() == QEvent::WindowStateChange)
602
    {
603
        if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray())
604
        {
605
            QWindowStateChangeEvent *wsevt = static_cast<QWindowStateChangeEvent*>(e);
606
            if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized())
607
            {
608
609
                QTimer::singleShot(0, this, SLOT(hide()));
                e->ignore();
610
611
612
            }
        }
    }
613
#endif
614
615
616
617
}

void BitcoinGUI::closeEvent(QCloseEvent *event)
{
618
    if(clientModel)
619
    {
620
621
622
623
624
625
#ifndef Q_WS_MAC // Ignored on Mac
        if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
           !clientModel->getOptionsModel()->getMinimizeOnClose())
        {
            qApp->quit();
        }
626
#endif
627
    }
628
629
    QMainWindow::closeEvent(event);
}
Wladimir J. van der Laan's avatar
ask fee    
Wladimir J. van der Laan committed
630
631
632
633
634
635

void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
{
    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.  "
636
637
          "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
638
    QMessageBox::StandardButton retval = QMessageBox::question(
639
          this, tr("Confirm transaction fee"), strMessage,
Wladimir J. van der Laan's avatar
ask fee    
Wladimir J. van der Laan committed
640
641
642
          QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes);
    *payFee = (retval == QMessageBox::Yes);
}
643

644
645
void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end)
{
646
647
    if(!walletModel || !clientModel)
        return;
648
    TransactionTableModel *ttm = walletModel->getTransactionTableModel();
649
    qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent)
650
                    .data(Qt::EditRole).toULongLong();
651
    if(!clientModel->inInitialBlockDownload())
652
    {
653
        // On new transaction, make an info balloon
654
        // Unless the initial block download is in progress, to prevent balloon-spam
655
656
        QString date = ttm->index(start, TransactionTableModel::Date, parent)
                        .data().toString();
657
658
659
        QString type = ttm->index(start, TransactionTableModel::Type, parent)
                        .data().toString();
        QString address = ttm->index(start, TransactionTableModel::ToAddress, parent)
660
                        .data().toString();
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
        QIcon icon = qvariant_cast<QIcon>(ttm->index(start,
                            TransactionTableModel::ToAddress, parent)
                        .data(Qt::DecorationRole));

        notificator->notify(Notificator::Information,
                            (amount)<0 ? tr("Sent transaction") :
                                         tr("Incoming transaction"),
                              tr("Date: %1\n"
                                 "Amount: %2\n"
                                 "Type: %3\n"
                                 "Address: %4\n")
                              .arg(date)
                              .arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true))
                              .arg(type)
                              .arg(address), icon);
676
677
    }
}
678

679
void BitcoinGUI::gotoOverviewPage()
680
681
682
{
    overviewAction->setChecked(true);
    centralWidget->setCurrentWidget(overviewPage);
683

684
    exportAction->setEnabled(false);
685
    disconnect(exportAction, SIGNAL(triggered()), 0, 0);
686
687
}

688
void BitcoinGUI::gotoHistoryPage()
689
690
691
{
    historyAction->setChecked(true);
    centralWidget->setCurrentWidget(transactionsPage);
692

693
    exportAction->setEnabled(true);
694
695
    disconnect(exportAction, SIGNAL(triggered()), 0, 0);
    connect(exportAction, SIGNAL(triggered()), transactionView, SLOT(exportClicked()));
696
697
}

698
699
700
701
void BitcoinGUI::gotoAddressBookPage()
{
    addressBookAction->setChecked(true);
    centralWidget->setCurrentWidget(addressBookPage);
702
703
704
705

    exportAction->setEnabled(true);
    disconnect(exportAction, SIGNAL(triggered()), 0, 0);
    connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked()));
706
707
708
709
710
711
}

void BitcoinGUI::gotoReceiveCoinsPage()
{
    receiveCoinsAction->setChecked(true);
    centralWidget->setCurrentWidget(receiveCoinsPage);
712
713
714
715

    exportAction->setEnabled(true);
    disconnect(exportAction, SIGNAL(triggered()), 0, 0);
    connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked()));
716
717
718
719
720
721
722
}

void BitcoinGUI::gotoSendCoinsPage()
{
    sendCoinsAction->setChecked(true);
    centralWidget->setCurrentWidget(sendCoinsPage);

723
724
    exportAction->setEnabled(false);
    disconnect(exportAction, SIGNAL(triggered()), 0, 0);
725
}
726

727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
void BitcoinGUI::gotoMessagePage()
{
#ifdef FIRST_CLASS_MESSAGING
    messageAction->setChecked(true);
    centralWidget->setCurrentWidget(messagePage);

    exportAction->setEnabled(false);
    disconnect(exportAction, SIGNAL(triggered()), 0, 0);
#else
    messagePage->show();
    messagePage->setFocus();
#endif
}

void BitcoinGUI::gotoMessagePage(QString addr)
{
    gotoMessagePage();
    messagePage->setAddress(addr);
}

747
748
void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
{
749
    // Accept only URIs
750
751
752
753
754
755
756
757
758
    if(event->mimeData()->hasUrls())
        event->acceptProposedAction();
}

void BitcoinGUI::dropEvent(QDropEvent *event)
{
    if(event->mimeData()->hasUrls())
    {
        gotoSendCoinsPage();
759
760
        QList<QUrl> uris = event->mimeData()->urls();
        foreach(const QUrl &uri, uris)
761
        {
762
            sendCoinsPage->handleURI(uri.toString());
763
764
765
766
767
768
        }
    }

    event->acceptProposedAction();
}

769
void BitcoinGUI::handleURI(QString strURI)
770
771
{
    gotoSendCoinsPage();
772
    sendCoinsPage->handleURI(strURI);
773
774
775
776
777

    if(!isActiveWindow())
        activateWindow();

    showNormalIfMinimized();
778
779
}

780
781
782
783
784
785
void BitcoinGUI::setEncryptionStatus(int status)
{
    switch(status)
    {
    case WalletModel::Unencrypted:
        labelEncryptionIcon->hide();
786
787
788
        encryptWalletAction->setChecked(false);
        changePassphraseAction->setEnabled(false);
        encryptWalletAction->setEnabled(true);
789
790
791
792
793
        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>"));
794
795
796
        encryptWalletAction->setChecked(true);
        changePassphraseAction->setEnabled(true);
        encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
797
798
799
800
801
        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>"));
802
803
804
        encryptWalletAction->setChecked(true);
        changePassphraseAction->setEnabled(true);
        encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
805
806
807
        break;
    }
}
808
809
810

void BitcoinGUI::encryptWallet(bool status)
{
811
812
    if(!walletModel)
        return;
813
814
815
816
817
818
819
820
    AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt:
                                     AskPassphraseDialog::Decrypt, this);
    dlg.setModel(walletModel);
    dlg.exec();

    setEncryptionStatus(walletModel->getEncryptionStatus());
}

sje397's avatar
sje397 committed
821
822
823
824
825
826
827
828
829
830
831
void BitcoinGUI::backupWallet()
{
    QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
    QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)"));
    if(!filename.isEmpty()) {
        if(!walletModel->backupWallet(filename)) {
            QMessageBox::warning(this, tr("Backup Failed"), tr("There was an error trying to save the wallet data to the new location."));
        }
    }
}

832
833
834
835
836
837
838
839
840
void BitcoinGUI::changePassphrase()
{
    AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
    dlg.setModel(walletModel);
    dlg.exec();
}

void BitcoinGUI::unlockWallet()
{
841
842
    if(!walletModel)
        return;
843
    // Unlock wallet when requested by wallet model
844
845
846
847
848
849
850
    if(walletModel->getEncryptionStatus() == WalletModel::Locked)
    {
        AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
        dlg.setModel(walletModel);
        dlg.exec();
    }
}
851
852
853
854
855
856
857
858

void BitcoinGUI::showNormalIfMinimized()
{
    if(!isVisible()) // Show, if hidden
        show();
    if(isMinimized()) // Unminimize, if minimized
        showNormal();
}