You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bitcoingui.cpp 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. // Copyright (c) 2011-2014 The Bitcoin developers
  2. // Distributed under the MIT/X11 software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "bitcoingui.h"
  5. #include "bitcoinunits.h"
  6. #include "clientmodel.h"
  7. #include "guiconstants.h"
  8. #include "guiutil.h"
  9. #include "notificator.h"
  10. #include "openuridialog.h"
  11. #include "optionsdialog.h"
  12. #include "optionsmodel.h"
  13. #include "rpcconsole.h"
  14. #include "utilitydialog.h"
  15. #ifdef ENABLE_WALLET
  16. #include "walletframe.h"
  17. #include "walletmodel.h"
  18. #endif
  19. #ifdef Q_OS_MAC
  20. #include "macdockiconhandler.h"
  21. #endif
  22. #include "init.h"
  23. #include "ui_interface.h"
  24. #include <iostream>
  25. #include <QAction>
  26. #include <QApplication>
  27. #include <QDateTime>
  28. #include <QDesktopWidget>
  29. #include <QDragEnterEvent>
  30. #include <QIcon>
  31. #include <QListWidget>
  32. #include <QMenuBar>
  33. #include <QMessageBox>
  34. #include <QMimeData>
  35. #include <QProgressBar>
  36. #include <QProgressDialog>
  37. #include <QSettings>
  38. #include <QStackedWidget>
  39. #include <QStatusBar>
  40. #include <QStyle>
  41. #include <QTimer>
  42. #include <QToolBar>
  43. #include <QVBoxLayout>
  44. #if QT_VERSION < 0x050000
  45. #include <QUrl>
  46. #include <QTextDocument>
  47. #else
  48. #include <QUrlQuery>
  49. #endif
  50. const QString BitcoinGUI::DEFAULT_WALLET = "~Default";
  51. BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) :
  52. QMainWindow(parent),
  53. clientModel(0),
  54. walletFrame(0),
  55. encryptWalletAction(0),
  56. changePassphraseAction(0),
  57. aboutQtAction(0),
  58. trayIcon(0),
  59. notificator(0),
  60. rpcConsole(0),
  61. prevBlocks(0),
  62. spinnerFrame(0)
  63. {
  64. GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
  65. QString windowTitle = tr("Bitcoin Core") + " - ";
  66. #ifdef ENABLE_WALLET
  67. /* if compiled with wallet support, -disablewallet can still disable the wallet */
  68. bool enableWallet = !GetBoolArg("-disablewallet", false);
  69. #else
  70. bool enableWallet = false;
  71. #endif
  72. if(enableWallet)
  73. {
  74. windowTitle += tr("Wallet");
  75. } else {
  76. windowTitle += tr("Node");
  77. }
  78. if (!fIsTestnet)
  79. {
  80. #ifndef Q_OS_MAC
  81. QApplication::setWindowIcon(QIcon(":icons/bitcoin"));
  82. setWindowIcon(QIcon(":icons/bitcoin"));
  83. #else
  84. MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin"));
  85. #endif
  86. }
  87. else
  88. {
  89. windowTitle += " " + tr("[testnet]");
  90. #ifndef Q_OS_MAC
  91. QApplication::setWindowIcon(QIcon(":icons/bitcoin_testnet"));
  92. setWindowIcon(QIcon(":icons/bitcoin_testnet"));
  93. #else
  94. MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet"));
  95. #endif
  96. }
  97. setWindowTitle(windowTitle);
  98. #if defined(Q_OS_MAC) && QT_VERSION < 0x050000
  99. // This property is not implemented in Qt 5. Setting it has no effect.
  100. // A replacement API (QtMacUnifiedToolBar) is available in QtMacExtras.
  101. setUnifiedTitleAndToolBarOnMac(true);
  102. #endif
  103. rpcConsole = new RPCConsole(enableWallet ? this : 0);
  104. #ifdef ENABLE_WALLET
  105. if(enableWallet)
  106. {
  107. /** Create wallet frame and make it the central widget */
  108. walletFrame = new WalletFrame(this);
  109. setCentralWidget(walletFrame);
  110. } else
  111. #endif
  112. {
  113. /* When compiled without wallet or -disablewallet is provided,
  114. * the central widget is the rpc console.
  115. */
  116. setCentralWidget(rpcConsole);
  117. }
  118. // Accept D&D of URIs
  119. setAcceptDrops(true);
  120. // Create actions for the toolbar, menu bar and tray/dock icon
  121. // Needs walletFrame to be initialized
  122. createActions(fIsTestnet);
  123. // Create application menu bar
  124. createMenuBar();
  125. // Create the toolbars
  126. createToolBars();
  127. // Create system tray icon and notification
  128. createTrayIcon(fIsTestnet);
  129. // Create status bar
  130. statusBar();
  131. // Status bar notification icons
  132. QFrame *frameBlocks = new QFrame();
  133. frameBlocks->setContentsMargins(0,0,0,0);
  134. frameBlocks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
  135. QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
  136. frameBlocksLayout->setContentsMargins(3,0,3,0);
  137. frameBlocksLayout->setSpacing(3);
  138. unitDisplayControl = new UnitDisplayStatusBarControl();
  139. labelEncryptionIcon = new QLabel();
  140. labelConnectionsIcon = new QLabel();
  141. labelBlocksIcon = new QLabel();
  142. if(enableWallet)
  143. {
  144. frameBlocksLayout->addStretch();
  145. frameBlocksLayout->addWidget(unitDisplayControl);
  146. frameBlocksLayout->addStretch();
  147. frameBlocksLayout->addWidget(labelEncryptionIcon);
  148. }
  149. frameBlocksLayout->addStretch();
  150. frameBlocksLayout->addWidget(labelConnectionsIcon);
  151. frameBlocksLayout->addStretch();
  152. frameBlocksLayout->addWidget(labelBlocksIcon);
  153. frameBlocksLayout->addStretch();
  154. // Progress bar and label for blocks download
  155. progressBarLabel = new QLabel();
  156. progressBarLabel->setVisible(false);
  157. progressBar = new QProgressBar();
  158. progressBar->setAlignment(Qt::AlignCenter);
  159. progressBar->setVisible(false);
  160. // Override style sheet for progress bar for styles that have a segmented progress bar,
  161. // as they make the text unreadable (workaround for issue #1071)
  162. // See https://qt-project.org/doc/qt-4.8/gallery.html
  163. QString curStyle = QApplication::style()->metaObject()->className();
  164. if(curStyle == "QWindowsStyle" || curStyle == "QWindowsXPStyle")
  165. {
  166. 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; }");
  167. }
  168. statusBar()->addWidget(progressBarLabel);
  169. statusBar()->addWidget(progressBar);
  170. statusBar()->addPermanentWidget(frameBlocks);
  171. connect(openRPCConsoleAction, SIGNAL(triggered()), rpcConsole, SLOT(show()));
  172. // prevents an open debug window from becoming stuck/unusable on client shutdown
  173. connect(quitAction, SIGNAL(triggered()), rpcConsole, SLOT(hide()));
  174. // Install event filter to be able to catch status tip events (QEvent::StatusTip)
  175. this->installEventFilter(this);
  176. // Initially wallet actions should be disabled
  177. setWalletActionsEnabled(false);
  178. // Subscribe to notifications from core
  179. subscribeToCoreSignals();
  180. }
  181. BitcoinGUI::~BitcoinGUI()
  182. {
  183. // Unsubscribe from notifications from core
  184. unsubscribeFromCoreSignals();
  185. GUIUtil::saveWindowGeometry("nWindow", this);
  186. if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu)
  187. trayIcon->hide();
  188. #ifdef Q_OS_MAC
  189. delete appMenuBar;
  190. MacDockIconHandler::instance()->setMainWindow(NULL);
  191. #endif
  192. }
  193. void BitcoinGUI::createActions(bool fIsTestnet)
  194. {
  195. QActionGroup *tabGroup = new QActionGroup(this);
  196. overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this);
  197. overviewAction->setStatusTip(tr("Show general overview of wallet"));
  198. overviewAction->setToolTip(overviewAction->statusTip());
  199. overviewAction->setCheckable(true);
  200. overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1));
  201. tabGroup->addAction(overviewAction);
  202. sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send"), this);
  203. sendCoinsAction->setStatusTip(tr("Send coins to a Bitcoin address"));
  204. sendCoinsAction->setToolTip(sendCoinsAction->statusTip());
  205. sendCoinsAction->setCheckable(true);
  206. sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2));
  207. tabGroup->addAction(sendCoinsAction);
  208. receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive"), this);
  209. receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)"));
  210. receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
  211. receiveCoinsAction->setCheckable(true);
  212. receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
  213. tabGroup->addAction(receiveCoinsAction);
  214. historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this);
  215. historyAction->setStatusTip(tr("Browse transaction history"));
  216. historyAction->setToolTip(historyAction->statusTip());
  217. historyAction->setCheckable(true);
  218. historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4));
  219. tabGroup->addAction(historyAction);
  220. // These showNormalIfMinimized are needed because Send Coins and Receive Coins
  221. // can be triggered from the tray menu, and need to show the GUI to be useful.
  222. connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
  223. connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
  224. connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
  225. connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage()));
  226. connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
  227. connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
  228. connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized()));
  229. connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
  230. quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this);
  231. quitAction->setStatusTip(tr("Quit application"));
  232. quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
  233. quitAction->setMenuRole(QAction::QuitRole);
  234. if (!fIsTestnet)
  235. aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About Bitcoin Core"), this);
  236. else
  237. aboutAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&About Bitcoin Core"), this);
  238. aboutAction->setStatusTip(tr("Show information about Bitcoin Core"));
  239. aboutAction->setMenuRole(QAction::AboutRole);
  240. #if QT_VERSION < 0x050000
  241. aboutQtAction = new QAction(QIcon(":/trolltech/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this);
  242. #else
  243. aboutQtAction = new QAction(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png"), tr("About &Qt"), this);
  244. #endif
  245. aboutQtAction->setStatusTip(tr("Show information about Qt"));
  246. aboutQtAction->setMenuRole(QAction::AboutQtRole);
  247. optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
  248. optionsAction->setStatusTip(tr("Modify configuration options for Bitcoin"));
  249. optionsAction->setMenuRole(QAction::PreferencesRole);
  250. if (!fIsTestnet)
  251. toggleHideAction = new QAction(QIcon(":/icons/bitcoin"), tr("&Show / Hide"), this);
  252. else
  253. toggleHideAction = new QAction(QIcon(":/icons/bitcoin_testnet"), tr("&Show / Hide"), this);
  254. toggleHideAction->setStatusTip(tr("Show or hide the main Window"));
  255. encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this);
  256. encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet"));
  257. encryptWalletAction->setCheckable(true);
  258. backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this);
  259. backupWalletAction->setStatusTip(tr("Backup wallet to another location"));
  260. changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this);
  261. changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption"));
  262. signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this);
  263. signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
  264. verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this);
  265. verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
  266. openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug window"), this);
  267. openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console"));
  268. usedSendingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Sending addresses..."), this);
  269. usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels"));
  270. usedReceivingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Receiving addresses..."), this);
  271. usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
  272. openAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_FileIcon), tr("Open &URI..."), this);
  273. openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
  274. showHelpMessageAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation), tr("&Command-line options"), this);
  275. showHelpMessageAction->setStatusTip(tr("Show the Bitcoin Core help message to get a list with possible Bitcoin command-line options"));
  276. connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
  277. connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
  278. connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
  279. connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
  280. connect(toggleHideAction, SIGNAL(triggered()), this, SLOT(toggleHidden()));
  281. connect(showHelpMessageAction, SIGNAL(triggered()), this, SLOT(showHelpMessageClicked()));
  282. #ifdef ENABLE_WALLET
  283. if(walletFrame)
  284. {
  285. connect(encryptWalletAction, SIGNAL(triggered(bool)), walletFrame, SLOT(encryptWallet(bool)));
  286. connect(backupWalletAction, SIGNAL(triggered()), walletFrame, SLOT(backupWallet()));
  287. connect(changePassphraseAction, SIGNAL(triggered()), walletFrame, SLOT(changePassphrase()));
  288. connect(signMessageAction, SIGNAL(triggered()), this, SLOT(gotoSignMessageTab()));
  289. connect(verifyMessageAction, SIGNAL(triggered()), this, SLOT(gotoVerifyMessageTab()));
  290. connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses()));
  291. connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses()));
  292. connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked()));
  293. }
  294. #endif
  295. }
  296. void BitcoinGUI::createMenuBar()
  297. {
  298. #ifdef Q_OS_MAC
  299. // Create a decoupled menu bar on Mac which stays even if the window is closed
  300. appMenuBar = new QMenuBar();
  301. #else
  302. // Get the main window's menu bar on other platforms
  303. appMenuBar = menuBar();
  304. #endif
  305. // Configure the menus
  306. QMenu *file = appMenuBar->addMenu(tr("&File"));
  307. if(walletFrame)
  308. {
  309. file->addAction(openAction);
  310. file->addAction(backupWalletAction);
  311. file->addAction(signMessageAction);
  312. file->addAction(verifyMessageAction);
  313. file->addSeparator();
  314. file->addAction(usedSendingAddressesAction);
  315. file->addAction(usedReceivingAddressesAction);
  316. file->addSeparator();
  317. }
  318. file->addAction(quitAction);
  319. QMenu *settings = appMenuBar->addMenu(tr("&Settings"));
  320. if(walletFrame)
  321. {
  322. settings->addAction(encryptWalletAction);
  323. settings->addAction(changePassphraseAction);
  324. settings->addSeparator();
  325. }
  326. settings->addAction(optionsAction);
  327. QMenu *help = appMenuBar->addMenu(tr("&Help"));
  328. if(walletFrame)
  329. {
  330. help->addAction(openRPCConsoleAction);
  331. }
  332. help->addAction(showHelpMessageAction);
  333. help->addSeparator();
  334. help->addAction(aboutAction);
  335. help->addAction(aboutQtAction);
  336. }
  337. void BitcoinGUI::createToolBars()
  338. {
  339. if(walletFrame)
  340. {
  341. QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
  342. toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
  343. toolbar->addAction(overviewAction);
  344. toolbar->addAction(sendCoinsAction);
  345. toolbar->addAction(receiveCoinsAction);
  346. toolbar->addAction(historyAction);
  347. overviewAction->setChecked(true);
  348. }
  349. }
  350. void BitcoinGUI::setClientModel(ClientModel *clientModel)
  351. {
  352. this->clientModel = clientModel;
  353. if(clientModel)
  354. {
  355. // Create system tray menu (or setup the dock menu) that late to prevent users from calling actions,
  356. // while the client has not yet fully loaded
  357. createTrayIconMenu();
  358. // Keep up to date with client
  359. setNumConnections(clientModel->getNumConnections());
  360. connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
  361. setNumBlocks(clientModel->getNumBlocks());
  362. connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
  363. // Receive and report messages from client model
  364. connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int)));
  365. // Show progress dialog
  366. connect(clientModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int)));
  367. rpcConsole->setClientModel(clientModel);
  368. #ifdef ENABLE_WALLET
  369. if(walletFrame)
  370. {
  371. walletFrame->setClientModel(clientModel);
  372. }
  373. #endif
  374. this->unitDisplayControl->setOptionsModel(clientModel->getOptionsModel());
  375. }
  376. }
  377. #ifdef ENABLE_WALLET
  378. bool BitcoinGUI::addWallet(const QString& name, WalletModel *walletModel)
  379. {
  380. if(!walletFrame)
  381. return false;
  382. setWalletActionsEnabled(true);
  383. return walletFrame->addWallet(name, walletModel);
  384. }
  385. bool BitcoinGUI::setCurrentWallet(const QString& name)
  386. {
  387. if(!walletFrame)
  388. return false;
  389. return walletFrame->setCurrentWallet(name);
  390. }
  391. void BitcoinGUI::removeAllWallets()
  392. {
  393. if(!walletFrame)
  394. return;
  395. setWalletActionsEnabled(false);
  396. walletFrame->removeAllWallets();
  397. }
  398. #endif
  399. void BitcoinGUI::setWalletActionsEnabled(bool enabled)
  400. {
  401. overviewAction->setEnabled(enabled);
  402. sendCoinsAction->setEnabled(enabled);
  403. receiveCoinsAction->setEnabled(enabled);
  404. historyAction->setEnabled(enabled);
  405. encryptWalletAction->setEnabled(enabled);
  406. backupWalletAction->setEnabled(enabled);
  407. changePassphraseAction->setEnabled(enabled);
  408. signMessageAction->setEnabled(enabled);
  409. verifyMessageAction->setEnabled(enabled);
  410. usedSendingAddressesAction->setEnabled(enabled);
  411. usedReceivingAddressesAction->setEnabled(enabled);
  412. openAction->setEnabled(enabled);
  413. }
  414. void BitcoinGUI::createTrayIcon(bool fIsTestnet)
  415. {
  416. #ifndef Q_OS_MAC
  417. trayIcon = new QSystemTrayIcon(this);
  418. if (!fIsTestnet)
  419. {
  420. trayIcon->setToolTip(tr("Bitcoin Core client"));
  421. trayIcon->setIcon(QIcon(":/icons/toolbar"));
  422. }
  423. else
  424. {
  425. trayIcon->setToolTip(tr("Bitcoin Core client") + " " + tr("[testnet]"));
  426. trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
  427. }
  428. trayIcon->show();
  429. #endif
  430. notificator = new Notificator(QApplication::applicationName(), trayIcon, this);
  431. }
  432. void BitcoinGUI::createTrayIconMenu()
  433. {
  434. QMenu *trayIconMenu;
  435. #ifndef Q_OS_MAC
  436. // return if trayIcon is unset (only on non-Mac OSes)
  437. if (!trayIcon)
  438. return;
  439. trayIconMenu = new QMenu(this);
  440. trayIcon->setContextMenu(trayIconMenu);
  441. connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
  442. this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
  443. #else
  444. // Note: On Mac, the dock icon is used to provide the tray's functionality.
  445. MacDockIconHandler *dockIconHandler = MacDockIconHandler::instance();
  446. dockIconHandler->setMainWindow((QMainWindow *)this);
  447. trayIconMenu = dockIconHandler->dockMenu();
  448. #endif
  449. // Configuration of the tray icon (or dock icon) icon menu
  450. trayIconMenu->addAction(toggleHideAction);
  451. trayIconMenu->addSeparator();
  452. trayIconMenu->addAction(sendCoinsAction);
  453. trayIconMenu->addAction(receiveCoinsAction);
  454. trayIconMenu->addSeparator();
  455. trayIconMenu->addAction(signMessageAction);
  456. trayIconMenu->addAction(verifyMessageAction);
  457. trayIconMenu->addSeparator();
  458. trayIconMenu->addAction(optionsAction);
  459. trayIconMenu->addAction(openRPCConsoleAction);
  460. #ifndef Q_OS_MAC // This is built-in on Mac
  461. trayIconMenu->addSeparator();
  462. trayIconMenu->addAction(quitAction);
  463. #endif
  464. }
  465. #ifndef Q_OS_MAC
  466. void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
  467. {
  468. if(reason == QSystemTrayIcon::Trigger)
  469. {
  470. // Click on system tray icon triggers show/hide of the main window
  471. toggleHideAction->trigger();
  472. }
  473. }
  474. #endif
  475. void BitcoinGUI::optionsClicked()
  476. {
  477. if(!clientModel || !clientModel->getOptionsModel())
  478. return;
  479. OptionsDialog dlg(this);
  480. dlg.setModel(clientModel->getOptionsModel());
  481. dlg.exec();
  482. }
  483. void BitcoinGUI::aboutClicked()
  484. {
  485. if(!clientModel)
  486. return;
  487. HelpMessageDialog dlg(this, true);
  488. dlg.exec();
  489. }
  490. void BitcoinGUI::showHelpMessageClicked()
  491. {
  492. HelpMessageDialog *help = new HelpMessageDialog(this, false);
  493. help->setAttribute(Qt::WA_DeleteOnClose);
  494. help->show();
  495. }
  496. #ifdef ENABLE_WALLET
  497. void BitcoinGUI::openClicked()
  498. {
  499. OpenURIDialog dlg(this);
  500. if(dlg.exec())
  501. {
  502. emit receivedURI(dlg.getURI());
  503. }
  504. }
  505. void BitcoinGUI::gotoOverviewPage()
  506. {
  507. overviewAction->setChecked(true);
  508. if (walletFrame) walletFrame->gotoOverviewPage();
  509. }
  510. void BitcoinGUI::gotoHistoryPage()
  511. {
  512. historyAction->setChecked(true);
  513. if (walletFrame) walletFrame->gotoHistoryPage();
  514. }
  515. void BitcoinGUI::gotoReceiveCoinsPage()
  516. {
  517. receiveCoinsAction->setChecked(true);
  518. if (walletFrame) walletFrame->gotoReceiveCoinsPage();
  519. }
  520. void BitcoinGUI::gotoSendCoinsPage(QString addr)
  521. {
  522. sendCoinsAction->setChecked(true);
  523. if (walletFrame) walletFrame->gotoSendCoinsPage(addr);
  524. }
  525. void BitcoinGUI::gotoSignMessageTab(QString addr)
  526. {
  527. if (walletFrame) walletFrame->gotoSignMessageTab(addr);
  528. }
  529. void BitcoinGUI::gotoVerifyMessageTab(QString addr)
  530. {
  531. if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
  532. }
  533. #endif
  534. void BitcoinGUI::setNumConnections(int count)
  535. {
  536. QString icon;
  537. switch(count)
  538. {
  539. case 0: icon = ":/icons/connect_0"; break;
  540. case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
  541. case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
  542. case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
  543. default: icon = ":/icons/connect_4"; break;
  544. }
  545. labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
  546. labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
  547. }
  548. void BitcoinGUI::setNumBlocks(int count)
  549. {
  550. // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text)
  551. statusBar()->clearMessage();
  552. // Acquire current block source
  553. enum BlockSource blockSource = clientModel->getBlockSource();
  554. switch (blockSource) {
  555. case BLOCK_SOURCE_NETWORK:
  556. progressBarLabel->setText(tr("Synchronizing with network..."));
  557. break;
  558. case BLOCK_SOURCE_DISK:
  559. progressBarLabel->setText(tr("Importing blocks from disk..."));
  560. break;
  561. case BLOCK_SOURCE_REINDEX:
  562. progressBarLabel->setText(tr("Reindexing blocks on disk..."));
  563. break;
  564. case BLOCK_SOURCE_NONE:
  565. // Case: not Importing, not Reindexing and no network connection
  566. progressBarLabel->setText(tr("No block source available..."));
  567. break;
  568. }
  569. QString tooltip;
  570. QDateTime lastBlockDate = clientModel->getLastBlockDate();
  571. QDateTime currentDate = QDateTime::currentDateTime();
  572. int secs = lastBlockDate.secsTo(currentDate);
  573. tooltip = tr("Processed %1 blocks of transaction history.").arg(count);
  574. // Set icon state: spinning if catching up, tick otherwise
  575. if(secs < 90*60)
  576. {
  577. tooltip = tr("Up to date") + QString(".<br>") + tooltip;
  578. labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
  579. #ifdef ENABLE_WALLET
  580. if(walletFrame)
  581. walletFrame->showOutOfSyncWarning(false);
  582. #endif
  583. progressBarLabel->setVisible(false);
  584. progressBar->setVisible(false);
  585. }
  586. else
  587. {
  588. // Represent time from last generated block in human readable text
  589. QString timeBehindText;
  590. const int HOUR_IN_SECONDS = 60*60;
  591. const int DAY_IN_SECONDS = 24*60*60;
  592. const int WEEK_IN_SECONDS = 7*24*60*60;
  593. const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
  594. if(secs < 2*DAY_IN_SECONDS)
  595. {
  596. timeBehindText = tr("%n hour(s)","",secs/HOUR_IN_SECONDS);
  597. }
  598. else if(secs < 2*WEEK_IN_SECONDS)
  599. {
  600. timeBehindText = tr("%n day(s)","",secs/DAY_IN_SECONDS);
  601. }
  602. else if(secs < YEAR_IN_SECONDS)
  603. {
  604. timeBehindText = tr("%n week(s)","",secs/WEEK_IN_SECONDS);
  605. }
  606. else
  607. {
  608. int years = secs / YEAR_IN_SECONDS;
  609. int remainder = secs % YEAR_IN_SECONDS;
  610. timeBehindText = tr("%1 and %2").arg(tr("%n year(s)", "", years)).arg(tr("%n week(s)","", remainder/WEEK_IN_SECONDS));
  611. }
  612. progressBarLabel->setVisible(true);
  613. progressBar->setFormat(tr("%1 behind").arg(timeBehindText));
  614. progressBar->setMaximum(1000000000);
  615. progressBar->setValue(clientModel->getVerificationProgress() * 1000000000.0 + 0.5);
  616. progressBar->setVisible(true);
  617. tooltip = tr("Catching up...") + QString("<br>") + tooltip;
  618. if(count != prevBlocks)
  619. {
  620. labelBlocksIcon->setPixmap(QIcon(QString(
  621. ":/movies/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
  622. .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
  623. spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
  624. }
  625. prevBlocks = count;
  626. #ifdef ENABLE_WALLET
  627. if(walletFrame)
  628. walletFrame->showOutOfSyncWarning(true);
  629. #endif
  630. tooltip += QString("<br>");
  631. tooltip += tr("Last received block was generated %1 ago.").arg(timeBehindText);
  632. tooltip += QString("<br>");
  633. tooltip += tr("Transactions after this will not yet be visible.");
  634. }
  635. // Don't word-wrap this (fixed-width) tooltip
  636. tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
  637. labelBlocksIcon->setToolTip(tooltip);
  638. progressBarLabel->setToolTip(tooltip);
  639. progressBar->setToolTip(tooltip);
  640. }
  641. void BitcoinGUI::message(const QString &title, const QString &message, unsigned int style, bool *ret)
  642. {
  643. QString strTitle = tr("Bitcoin"); // default title
  644. // Default to information icon
  645. int nMBoxIcon = QMessageBox::Information;
  646. int nNotifyIcon = Notificator::Information;
  647. QString msgType;
  648. // Prefer supplied title over style based title
  649. if (!title.isEmpty()) {
  650. msgType = title;
  651. }
  652. else {
  653. switch (style) {
  654. case CClientUIInterface::MSG_ERROR:
  655. msgType = tr("Error");
  656. break;
  657. case CClientUIInterface::MSG_WARNING:
  658. msgType = tr("Warning");
  659. break;
  660. case CClientUIInterface::MSG_INFORMATION:
  661. msgType = tr("Information");
  662. break;
  663. default:
  664. break;
  665. }
  666. }
  667. // Append title to "Bitcoin - "
  668. if (!msgType.isEmpty())
  669. strTitle += " - " + msgType;
  670. // Check for error/warning icon
  671. if (style & CClientUIInterface::ICON_ERROR) {
  672. nMBoxIcon = QMessageBox::Critical;
  673. nNotifyIcon = Notificator::Critical;
  674. }
  675. else if (style & CClientUIInterface::ICON_WARNING) {
  676. nMBoxIcon = QMessageBox::Warning;
  677. nNotifyIcon = Notificator::Warning;
  678. }
  679. // Display message
  680. if (style & CClientUIInterface::MODAL) {
  681. // Check for buttons, use OK as default, if none was supplied
  682. QMessageBox::StandardButton buttons;
  683. if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK)))
  684. buttons = QMessageBox::Ok;
  685. showNormalIfMinimized();
  686. QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this);
  687. int r = mBox.exec();
  688. if (ret != NULL)
  689. *ret = r == QMessageBox::Ok;
  690. }
  691. else
  692. notificator->notify((Notificator::Class)nNotifyIcon, strTitle, message);
  693. }
  694. void BitcoinGUI::changeEvent(QEvent *e)
  695. {
  696. QMainWindow::changeEvent(e);
  697. #ifndef Q_OS_MAC // Ignored on Mac
  698. if(e->type() == QEvent::WindowStateChange)
  699. {
  700. if(clientModel && clientModel->getOptionsModel()->getMinimizeToTray())
  701. {
  702. QWindowStateChangeEvent *wsevt = static_cast<QWindowStateChangeEvent*>(e);
  703. if(!(wsevt->oldState() & Qt::WindowMinimized) && isMinimized())
  704. {
  705. QTimer::singleShot(0, this, SLOT(hide()));
  706. e->ignore();
  707. }
  708. }
  709. }
  710. #endif
  711. }
  712. void BitcoinGUI::closeEvent(QCloseEvent *event)
  713. {
  714. if(clientModel)
  715. {
  716. #ifndef Q_OS_MAC // Ignored on Mac
  717. if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
  718. !clientModel->getOptionsModel()->getMinimizeOnClose())
  719. {
  720. QApplication::quit();
  721. }
  722. #endif
  723. }
  724. QMainWindow::closeEvent(event);
  725. }
  726. #ifdef ENABLE_WALLET
  727. void BitcoinGUI::incomingTransaction(const QString& date, int unit, qint64 amount, const QString& type, const QString& address)
  728. {
  729. // On new transaction, make an info balloon
  730. message((amount)<0 ? tr("Sent transaction") : tr("Incoming transaction"),
  731. tr("Date: %1\n"
  732. "Amount: %2\n"
  733. "Type: %3\n"
  734. "Address: %4\n")
  735. .arg(date)
  736. .arg(BitcoinUnits::formatWithUnit(unit, amount, true))
  737. .arg(type)
  738. .arg(address), CClientUIInterface::MSG_INFORMATION);
  739. }
  740. #endif
  741. void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
  742. {
  743. // Accept only URIs
  744. if(event->mimeData()->hasUrls())
  745. event->acceptProposedAction();
  746. }
  747. void BitcoinGUI::dropEvent(QDropEvent *event)
  748. {
  749. if(event->mimeData()->hasUrls())
  750. {
  751. foreach(const QUrl &uri, event->mimeData()->urls())
  752. {
  753. emit receivedURI(uri.toString());
  754. }
  755. }
  756. event->acceptProposedAction();
  757. }
  758. bool BitcoinGUI::eventFilter(QObject *object, QEvent *event)
  759. {
  760. // Catch status tip events
  761. if (event->type() == QEvent::StatusTip)
  762. {
  763. // Prevent adding text from setStatusTip(), if we currently use the status bar for displaying other stuff
  764. if (progressBarLabel->isVisible() || progressBar->isVisible())
  765. return true;
  766. }
  767. return QMainWindow::eventFilter(object, event);
  768. }
  769. #ifdef ENABLE_WALLET
  770. bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
  771. {
  772. // URI has to be valid
  773. if (walletFrame && walletFrame->handlePaymentRequest(recipient))
  774. {
  775. showNormalIfMinimized();
  776. gotoSendCoinsPage();
  777. return true;
  778. }
  779. else
  780. return false;
  781. }
  782. void BitcoinGUI::setEncryptionStatus(int status)
  783. {
  784. switch(status)
  785. {
  786. case WalletModel::Unencrypted:
  787. labelEncryptionIcon->hide();
  788. encryptWalletAction->setChecked(false);
  789. changePassphraseAction->setEnabled(false);
  790. encryptWalletAction->setEnabled(true);
  791. break;
  792. case WalletModel::Unlocked:
  793. labelEncryptionIcon->show();
  794. labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
  795. labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
  796. encryptWalletAction->setChecked(true);
  797. changePassphraseAction->setEnabled(true);
  798. encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
  799. break;
  800. case WalletModel::Locked:
  801. labelEncryptionIcon->show();
  802. labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
  803. labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
  804. encryptWalletAction->setChecked(true);
  805. changePassphraseAction->setEnabled(true);
  806. encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
  807. break;
  808. }
  809. }
  810. #endif
  811. void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden)
  812. {
  813. if(!clientModel)
  814. return;
  815. // activateWindow() (sometimes) helps with keyboard focus on Windows
  816. if (isHidden())
  817. {
  818. show();
  819. activateWindow();
  820. }
  821. else if (isMinimized())
  822. {
  823. showNormal();
  824. activateWindow();
  825. }
  826. else if (GUIUtil::isObscured(this))
  827. {
  828. raise();
  829. activateWindow();
  830. }
  831. else if(fToggleHidden)
  832. hide();
  833. }
  834. void BitcoinGUI::toggleHidden()
  835. {
  836. showNormalIfMinimized(true);
  837. }
  838. void BitcoinGUI::detectShutdown()
  839. {
  840. if (ShutdownRequested())
  841. {
  842. if(rpcConsole)
  843. rpcConsole->hide();
  844. qApp->quit();
  845. }
  846. }
  847. void BitcoinGUI::showProgress(const QString &title, int nProgress)
  848. {
  849. if (nProgress == 0)
  850. {
  851. progressDialog = new QProgressDialog(title, "", 0, 100);
  852. progressDialog->setWindowModality(Qt::ApplicationModal);
  853. progressDialog->setMinimumDuration(0);
  854. progressDialog->setCancelButton(0);
  855. progressDialog->setAutoClose(false);
  856. progressDialog->setValue(0);
  857. }
  858. else if (nProgress == 100)
  859. {
  860. if (progressDialog)
  861. {
  862. progressDialog->close();
  863. progressDialog->deleteLater();
  864. }
  865. }
  866. else if (progressDialog)
  867. progressDialog->setValue(nProgress);
  868. }
  869. static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style)
  870. {
  871. bool modal = (style & CClientUIInterface::MODAL);
  872. bool ret = false;
  873. // In case of modal message, use blocking connection to wait for user to click a button
  874. QMetaObject::invokeMethod(gui, "message",
  875. modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection,
  876. Q_ARG(QString, QString::fromStdString(caption)),
  877. Q_ARG(QString, QString::fromStdString(message)),
  878. Q_ARG(unsigned int, style),
  879. Q_ARG(bool*, &ret));
  880. return ret;
  881. }
  882. void BitcoinGUI::subscribeToCoreSignals()
  883. {
  884. // Connect signals to client
  885. uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
  886. }
  887. void BitcoinGUI::unsubscribeFromCoreSignals()
  888. {
  889. // Disconnect signals from client
  890. uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3));
  891. }
  892. UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel()
  893. {
  894. optionsModel = 0;
  895. createContextMenu();
  896. setToolTip(tr("Unit to show amounts in. Click to select another unit."));
  897. }
  898. /** So that it responds to button clicks */
  899. void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
  900. {
  901. onDisplayUnitsClicked(event->pos());
  902. }
  903. /** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
  904. void UnitDisplayStatusBarControl::createContextMenu()
  905. {
  906. menu = new QMenu();
  907. foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
  908. {
  909. QAction *menuAction = new QAction(QString(BitcoinUnits::name(u)), this);
  910. menuAction->setData(QVariant(u));
  911. menu->addAction(menuAction);
  912. }
  913. connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*)));
  914. }
  915. /** Lets the control know about the Options Model (and its signals) */
  916. void UnitDisplayStatusBarControl::setOptionsModel(OptionsModel *optionsModel)
  917. {
  918. if (optionsModel)
  919. {
  920. this->optionsModel = optionsModel;
  921. // be aware of a display unit change reported by the OptionsModel object.
  922. connect(optionsModel,SIGNAL(displayUnitChanged(int)),this,SLOT(updateDisplayUnit(int)));
  923. // initialize the display units label with the current value in the model.
  924. updateDisplayUnit(optionsModel->getDisplayUnit());
  925. }
  926. }
  927. /** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
  928. void UnitDisplayStatusBarControl::updateDisplayUnit(int newUnits)
  929. {
  930. setPixmap(QIcon(":/icons/unit_" + BitcoinUnits::id(newUnits)).pixmap(31,STATUSBAR_ICONSIZE));
  931. }
  932. /** Shows context menu with Display Unit options by the mouse coordinates */
  933. void UnitDisplayStatusBarControl::onDisplayUnitsClicked(const QPoint& point)
  934. {
  935. QPoint globalPos = mapToGlobal(point);
  936. menu->exec(globalPos);
  937. }
  938. /** Tells underlying optionsModel to update its current display unit. */
  939. void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
  940. {
  941. if (action)
  942. {
  943. optionsModel->setDisplayUnit(action->data());
  944. }
  945. }