eeca18eff31e24ecf5239938154162a55cee0625
[novacoin.git] / src / qt / bitcoingui.cpp
1 /*
2  * Qt4 bitcoin GUI.
3  *
4  * W.J. van der Laan 2011
5  */
6 #include "bitcoingui.h"
7 #include "transactiontablemodel.h"
8 #include "addressbookdialog.h"
9 #include "sendcoinsdialog.h"
10 #include "optionsdialog.h"
11 #include "aboutdialog.h"
12 #include "clientmodel.h"
13 #include "walletmodel.h"
14 #include "guiutil.h"
15 #include "editaddressdialog.h"
16 #include "optionsmodel.h"
17 #include "transactiondescdialog.h"
18 #include "addresstablemodel.h"
19 #include "transactionview.h"
20
21 #include <QApplication>
22 #include <QMainWindow>
23 #include <QMenuBar>
24 #include <QMenu>
25 #include <QIcon>
26 #include <QTabWidget>
27 #include <QVBoxLayout>
28 #include <QWidget>
29 #include <QToolBar>
30 #include <QStatusBar>
31 #include <QLabel>
32 #include <QLineEdit>
33 #include <QPushButton>
34 #include <QLocale>
35 #include <QClipboard>
36 #include <QMessageBox>
37 #include <QProgressBar>
38
39 #include <QDebug>
40
41 #include <iostream>
42
43 BitcoinGUI::BitcoinGUI(QWidget *parent):
44     QMainWindow(parent),
45     clientModel(0),
46     walletModel(0),
47     trayIcon(0)
48 {
49     resize(850, 550);
50     setWindowTitle(tr("Bitcoin"));
51     setWindowIcon(QIcon(":icons/bitcoin"));
52
53     createActions();
54
55     // Menus
56     QMenu *file = menuBar()->addMenu("&File");
57     file->addAction(sendcoins);
58     file->addSeparator();
59     file->addAction(quit);
60     
61     QMenu *settings = menuBar()->addMenu("&Settings");
62     settings->addAction(receivingAddresses);
63     settings->addAction(options);
64
65     QMenu *help = menuBar()->addMenu("&Help");
66     help->addAction(about);
67     
68     // Toolbar
69     QToolBar *toolbar = addToolBar("Main toolbar");
70     toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
71     toolbar->addAction(sendcoins);
72     toolbar->addAction(addressbook);
73     toolbar->addAction(receivingAddresses);
74
75     // Balance: <balance>
76     QHBoxLayout *hbox_balance = new QHBoxLayout();
77     hbox_balance->addWidget(new QLabel(tr("Balance:")));
78     hbox_balance->addSpacing(5);/* Add some spacing between the label and the text */
79
80     labelBalance = new QLabel();
81     labelBalance->setFont(QFont("Monospace", -1, QFont::Bold));
82     labelBalance->setToolTip(tr("Your current balance"));
83     hbox_balance->addWidget(labelBalance);
84     hbox_balance->addStretch(1);
85     
86     QVBoxLayout *vbox = new QVBoxLayout();
87     vbox->addLayout(hbox_balance);
88
89     transactionView = new TransactionView(this);
90     connect(transactionView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(transactionDetails(const QModelIndex&)));
91     vbox->addWidget(transactionView);
92
93     QWidget *centralwidget = new QWidget(this);
94     centralwidget->setLayout(vbox);
95     setCentralWidget(centralwidget);
96     
97     // Create status bar
98     statusBar();
99
100     labelConnections = new QLabel();
101     labelConnections->setFrameStyle(QFrame::Panel | QFrame::Sunken);
102     labelConnections->setMinimumWidth(150);
103     labelConnections->setToolTip(tr("Number of connections to other clients"));
104
105     labelBlocks = new QLabel();
106     labelBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken);
107     labelBlocks->setMinimumWidth(130);
108     labelBlocks->setToolTip(tr("Number of blocks in the block chain"));
109
110     labelTransactions = new QLabel();
111     labelTransactions->setFrameStyle(QFrame::Panel | QFrame::Sunken);
112     labelTransactions->setMinimumWidth(130);
113     labelTransactions->setToolTip(tr("Number of transactions in your wallet"));
114
115     // Progress bar for blocks download
116     progressBarLabel = new QLabel(tr("Synchronizing with network..."));
117     progressBarLabel->setVisible(false);
118     progressBar = new QProgressBar();
119     progressBar->setToolTip(tr("Block chain synchronization in progress"));
120     progressBar->setVisible(false);
121
122     statusBar()->addWidget(progressBarLabel);
123     statusBar()->addWidget(progressBar);
124     statusBar()->addPermanentWidget(labelConnections);
125     statusBar()->addPermanentWidget(labelBlocks);
126     statusBar()->addPermanentWidget(labelTransactions);
127
128     createTrayIcon();
129 }
130
131 void BitcoinGUI::createActions()
132 {
133     quit = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this);
134     quit->setToolTip(tr("Quit application"));
135     sendcoins = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this);
136     sendcoins->setToolTip(tr("Send coins to a bitcoin address"));
137     addressbook = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this);
138     addressbook->setToolTip(tr("Edit the list of stored addresses and labels"));
139     about = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this);
140     about->setToolTip(tr("Show information about Bitcoin"));
141     receivingAddresses = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receiving Addresses..."), this);
142     receivingAddresses->setToolTip(tr("Show the list of addresses for receiving payments"));
143     options = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
144     options->setToolTip(tr("Modify configuration options for bitcoin"));
145     openBitcoin = new QAction(QIcon(":/icons/bitcoin"), "Open &Bitcoin", this);
146     openBitcoin->setToolTip(tr("Show the Bitcoin window"));
147
148     connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
149     connect(sendcoins, SIGNAL(triggered()), this, SLOT(sendcoinsClicked()));
150     connect(addressbook, SIGNAL(triggered()), this, SLOT(addressbookClicked()));
151     connect(receivingAddresses, SIGNAL(triggered()), this, SLOT(receivingAddressesClicked()));
152     connect(options, SIGNAL(triggered()), this, SLOT(optionsClicked()));
153     connect(about, SIGNAL(triggered()), this, SLOT(aboutClicked()));
154     connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show()));
155 }
156
157 void BitcoinGUI::setClientModel(ClientModel *clientModel)
158 {
159     this->clientModel = clientModel;
160
161     if(clientModel->isTestNet())
162     {
163         setWindowTitle(tr("Bitcoin [testnet]"));
164         setWindowIcon(QIcon(":icons/bitcoin_testnet"));
165         if(trayIcon)
166         {
167             trayIcon->setToolTip(tr("Bitcoin [testnet]"));
168             trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
169         }
170     }
171
172     // Keep up to date with client
173     setNumConnections(clientModel->getNumConnections());
174     connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
175
176     setNumBlocks(clientModel->getNumBlocks());
177     connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
178
179     // Report errors from network/worker thread
180     connect(clientModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString)));
181 }
182
183 void BitcoinGUI::setWalletModel(WalletModel *walletModel)
184 {
185     this->walletModel = walletModel;
186
187     // Keep up to date with wallet
188     setBalance(walletModel->getBalance());
189     connect(walletModel, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64)));
190
191     setNumTransactions(walletModel->getNumTransactions());
192     connect(walletModel, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int)));
193
194     // Report errors from wallet thread
195     connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString)));
196
197     // Put transaction list in tabs
198     transactionView->setModel(walletModel->getTransactionTableModel());
199
200     // Balloon popup for new transaction
201     connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
202             this, SLOT(incomingTransaction(const QModelIndex &, int, int)));
203 }
204
205 void BitcoinGUI::createTrayIcon()
206 {
207     QMenu *trayIconMenu = new QMenu(this);
208     trayIconMenu->addAction(openBitcoin);
209     trayIconMenu->addAction(sendcoins);
210     trayIconMenu->addAction(options);
211     trayIconMenu->addSeparator();
212     trayIconMenu->addAction(quit);
213
214     trayIcon = new QSystemTrayIcon(this);
215     trayIcon->setContextMenu(trayIconMenu);
216     trayIcon->setToolTip("Bitcoin client");
217     trayIcon->setIcon(QIcon(":/icons/toolbar"));
218     connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
219             this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
220     trayIcon->show();
221 }
222
223 void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
224 {
225     if(reason == QSystemTrayIcon::DoubleClick)
226     {
227         // Doubleclick on system tray icon triggers "open bitcoin"
228         openBitcoin->trigger();
229     }
230 }
231
232 void BitcoinGUI::sendcoinsClicked()
233 {
234     SendCoinsDialog dlg;
235     dlg.setModel(walletModel);
236     dlg.exec();
237 }
238
239 void BitcoinGUI::addressbookClicked()
240 {
241     AddressBookDialog dlg(AddressBookDialog::ForEditing);
242     dlg.setModel(walletModel->getAddressTableModel());
243     dlg.setTab(AddressBookDialog::SendingTab);
244     dlg.exec();
245 }
246
247 void BitcoinGUI::receivingAddressesClicked()
248 {
249     AddressBookDialog dlg(AddressBookDialog::ForEditing);
250     dlg.setModel(walletModel->getAddressTableModel());
251     dlg.setTab(AddressBookDialog::ReceivingTab);
252     dlg.exec();
253 }
254
255 void BitcoinGUI::optionsClicked()
256 {
257     OptionsDialog dlg;
258     dlg.setModel(clientModel->getOptionsModel());
259     dlg.exec();
260 }
261
262 void BitcoinGUI::aboutClicked()
263 {
264     AboutDialog dlg;
265     dlg.setModel(clientModel);
266     dlg.exec();
267 }
268
269 void BitcoinGUI::setBalance(qint64 balance)
270 {
271     labelBalance->setText(GUIUtil::formatMoney(balance) + QString(" BTC"));
272 }
273
274 void BitcoinGUI::setNumConnections(int count)
275 {
276     QString icon;
277     switch(count)
278     {
279     case 0: icon = ":/icons/connect_0"; break;
280     case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
281     case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
282     case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
283     default: icon = ":/icons/connect_4"; break;
284     }
285     labelConnections->setTextFormat(Qt::RichText);
286     labelConnections->setText("<img src=\""+icon+"\"> " + tr("%n connection(s)", "", count));
287 }
288
289 void BitcoinGUI::setNumBlocks(int count)
290 {
291     int total = clientModel->getTotalBlocksEstimate();
292     if(count < total)
293     {
294         progressBarLabel->setVisible(true);
295         progressBar->setVisible(true);
296         progressBar->setMaximum(total);
297         progressBar->setValue(count);
298     }
299     else
300     {
301         progressBarLabel->setVisible(false);
302         progressBar->setVisible(false);
303     }
304
305     labelBlocks->setText(tr("%n block(s)", "", count));
306 }
307
308 void BitcoinGUI::setNumTransactions(int count)
309 {
310     labelTransactions->setText(tr("%n transaction(s)", "", count));
311 }
312
313 void BitcoinGUI::error(const QString &title, const QString &message)
314 {
315     // Report errors from network/worker thread
316     if(trayIcon->supportsMessages())
317     {
318         // Show as "balloon" message if possible
319         trayIcon->showMessage(title, message, QSystemTrayIcon::Critical);
320     }
321     else
322     {
323         // Fall back to old fashioned popup dialog if not
324         QMessageBox::critical(this, title,
325             message,
326             QMessageBox::Ok, QMessageBox::Ok);
327     }
328 }
329
330 void BitcoinGUI::changeEvent(QEvent *e)
331 {
332     if (e->type() == QEvent::WindowStateChange)
333     {
334         if(clientModel->getOptionsModel()->getMinimizeToTray())
335         {
336             if (isMinimized())
337             {
338                 hide();
339                 e->ignore();
340             }
341             else
342             {
343                 e->accept();
344             }
345         }
346     }
347     QMainWindow::changeEvent(e);
348 }
349
350 void BitcoinGUI::closeEvent(QCloseEvent *event)
351 {
352     if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
353        !clientModel->getOptionsModel()->getMinimizeOnClose())
354     {
355         qApp->quit();
356     }
357     QMainWindow::closeEvent(event);
358 }
359
360 void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
361 {
362     QString strMessage =
363         tr("This transaction is over the size limit.  You can still send it for a fee of %1, "
364           "which goes to the nodes that process your transaction and helps to support the network.  "
365           "Do you want to pay the fee?").arg(GUIUtil::formatMoney(nFeeRequired));
366     QMessageBox::StandardButton retval = QMessageBox::question(
367           this, tr("Sending..."), strMessage,
368           QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes);
369     *payFee = (retval == QMessageBox::Yes);
370 }
371
372 void BitcoinGUI::transactionDetails(const QModelIndex& idx)
373 {
374     // A transaction is doubleclicked
375     TransactionDescDialog dlg(idx);
376     dlg.exec();
377 }
378
379 void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end)
380 {
381     TransactionTableModel *ttm = walletModel->getTransactionTableModel();
382     qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent)
383                     .data(Qt::EditRole).toULongLong();
384     if(amount>0 && !clientModel->inInitialBlockDownload())
385     {
386         // On incoming transaction, make an info balloon
387         // Unless the initial block download is in progress, to prevent balloon-spam
388         QString date = ttm->index(start, TransactionTableModel::Date, parent)
389                         .data().toString();
390         QString type = ttm->index(start, TransactionTableModel::Type, parent)
391                         .data().toString();
392         QString address = ttm->index(start, TransactionTableModel::ToAddress, parent)
393                         .data().toString();
394
395         trayIcon->showMessage(tr("Incoming transaction"),
396                               tr("Date: ") + date + "\n" +
397                               tr("Amount: ") + GUIUtil::formatMoney(amount, true) + "\n" +
398                               tr("Type: ") + type + "\n" +
399                               tr("Address: ") + address + "\n",
400                               QSystemTrayIcon::Information);
401     }
402 }