dogecoin/src/qt/utilitydialog.cpp
Andymeows be602d7ce4 Demoting QRImageWidget to simpler QLabels.
Disabling wallet interaction when the wallet isn't enabled
2014-09-01 00:13:59 -04:00

477 lines
16 KiB
C++

// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "utilitydialog.h"
#include "ui_aboutdialog.h"
#include "ui_paperwalletdialog.h"
#include "ui_helpmessagedialog.h"
#include "bitcoinunits.h"
#ifdef ENABLE_WALLET
#include "sendcoinsdialog.h"
#include "sendcoinsentry.h"
#include "coincontrol.h"
#include "coincontroldialog.h"
#endif
#include "optionsmodel.h"
#include "bitcoingui.h"
#include "clientmodel.h"
#include "guiutil.h"
#include "clientversion.h"
#include "init.h"
#include "util.h"
#include <QLabel>
#include <QFont>
#include <QVBoxLayout>
#include <QInputDialog>
#ifdef USE_QRCODE
#include <qrencode.h>
#endif
#if QT_VERSION < 0x050000
#include <QPrinter>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#else
// Use QT5's new modular classes
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>
#include <QtPrintSupport/QPrintPreviewDialog>
#endif
#include <QPainter>
#include "walletmodel.h"
/** "About" dialog box */
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
// Set current copyright year
ui->copyrightLabel->setText(tr("Copyright") + QString(" &copy; 2009-%1 ").arg(COPYRIGHT_YEAR) + tr("The Bitcoin Core developers") + QString("<br>") + tr("Copyright") + QString(" &copy; 2013-%1 ").arg(COPYRIGHT_YEAR) + tr("The Dogecoin developers"));
}
void AboutDialog::setModel(ClientModel *model)
{
if(model)
{
QString version = model->formatFullVersion();
/* On x86 add a bit specifier to the version so that users can distinguish between
* 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambigious.
*/
#if defined(__x86_64__)
version += " " + tr("(%1-bit)").arg(64);
#elif defined(__i386__ )
version += " " + tr("(%1-bit)").arg(32);
#endif
ui->versionLabel->setText(version);
}
}
AboutDialog::~AboutDialog()
{
delete ui;
}
void AboutDialog::on_buttonBox_accepted()
{
close();
}
/** "PaperWallet" dialog box */
PaperWalletDialog::PaperWalletDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::PaperWalletDialog)
{
ui->setupUi(this);
ui->buttonBox->addButton(tr("Close"), QDialogButtonBox::RejectRole);
// Begin with a small bold monospace font for the textual version of the key and address.
QFont font("Monospace");
font.setBold(true);
font.setStyleHint(QFont::TypeWriter);
font.setPixelSize(1);
ui->addressText->setFont(font);
ui->privateKeyText->setFont(font);
ui->addressText->setAlignment(Qt::AlignJustify);
ui->privateKeyText->setAlignment(Qt::AlignJustify);
}
void PaperWalletDialog::setModel(WalletModel *model)
{
RandAddSeed();
this->model = model;
this->on_getNewAddress_clicked();
}
PaperWalletDialog::~PaperWalletDialog()
{
delete ui;
}
void PaperWalletDialog::on_getNewAddress_clicked()
{
// Create a new private key
CKey privKey;
privKey.MakeNewKey(true);
// Derive the public key
CPubKey pubkey = privKey.GetPubKey();
// Derive the public key hash
CBitcoinAddress pubkeyhash;
pubkeyhash.Set(pubkey.GetID());
// Create String versions of each
string myPrivKey = CBitcoinSecret(privKey).ToString();
string myPubKey = HexStr(pubkey.begin(), pubkey.end());
string myAddress = pubkeyhash.ToString();
#ifdef USE_QRCODE
// Generate the address QR code
QRcode *code = QRcode_encodeString(myAddress.c_str(), 0, QR_ECLEVEL_M, QR_MODE_8, 1);
if (!code)
{
ui->addressQRCode->setText(tr("Error encoding Address into QR Code."));
return;
}
QImage myImage = QImage(code->width, code->width, QImage::Format_ARGB32);
myImage.fill(QColor(0,0,0,0));
unsigned char *p = code->data;
for (int y = 0; y < code->width; y++)
{
for (int x = 0; x < code->width; x++)
{
myImage.setPixel(x, y, ((*p & 1) ? 0xff000000 : 0x0));
p++;
}
}
QRcode_free(code);
// Generate the private key QR code
code = QRcode_encodeString(myPrivKey.c_str(), 0, QR_ECLEVEL_M, QR_MODE_8, 1);
if (!code)
{
ui->privateKeyQRCode->setText(tr("Error encoding private key into QR Code."));
return;
}
QImage myImagePriv = QImage(code->width, code->width, QImage::Format_ARGB32);
myImagePriv.fill(QColor(0,0,0,0));
p = code->data;
for (int y = 0; y < code->width; y++)
{
for (int x = 0; x < code->width; x++)
{
myImagePriv.setPixel(x, y, ((*p & 1) ? 0xff000000 : 0x0));
p++;
}
}
QRcode_free(code);
// Populate the QR Codes
ui->addressQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(ui->addressQRCode->width(), ui->addressQRCode->height()));
ui->privateKeyQRCode->setPixmap(QPixmap::fromImage(myImagePriv).scaled(ui->privateKeyQRCode->width(), ui->privateKeyQRCode->height()));
#endif
// Populate the Texts
ui->addressText->setText(myAddress.c_str());
ui->privateKeyText->setText(tr(myPrivKey.c_str()));
ui->publicKey->setHtml(myPubKey.c_str());
// Update the fonts to fit the height of the wallet.
// This should only really trigger the first time since the font size persists.
double paperHeight = (double) ui->paperTemplate->height();
double maxTextWidth = paperHeight * 0.99;
double minTextWidth = paperHeight * 0.95;
int pixelSizeStep = 1;
int addressTextLength = ui->addressText->fontMetrics().boundingRect(ui->addressText->text()).width();
QFont font = ui->addressText->font();
for(int i = 0; i < 20; i++) {
if ( addressTextLength < minTextWidth) {
font.setPixelSize(font.pixelSize() + pixelSizeStep);
ui->addressText->setFont(font);
addressTextLength = ui->addressText->fontMetrics().boundingRect(ui->addressText->text()).width();
}
}
if ( addressTextLength > maxTextWidth ) {
font.setPixelSize(font.pixelSize() - pixelSizeStep);
ui->addressText->setFont(font);
addressTextLength = ui->addressText->fontMetrics().boundingRect(ui->addressText->text()).width();
}
int privateKeyTextLength = ui->privateKeyText->fontMetrics().boundingRect(ui->privateKeyText->text()).width();
font = ui->privateKeyText->font();
for(int i = 0; i < 20; i++) {
if ( privateKeyTextLength < minTextWidth) {
font.setPixelSize(font.pixelSize() + pixelSizeStep);
ui->privateKeyText->setFont(font);
privateKeyTextLength = ui->privateKeyText->fontMetrics().boundingRect(ui->privateKeyText->text()).width();
}
}
if ( privateKeyTextLength > maxTextWidth ) {
font.setPixelSize(font.pixelSize() - pixelSizeStep);
ui->privateKeyText->setFont(font);
privateKeyTextLength = ui->privateKeyText->fontMetrics().boundingRect(ui->privateKeyText->text()).width();
}
}
void PaperWalletDialog::on_printButton_clicked()
{
QPrinter printer(QPrinter::HighResolution);
QPrintDialog *qpd = new QPrintDialog(&printer, this);
qpd->setPrintRange(QAbstractPrintDialog::AllPages);
QList<QString> recipientPubKeyHashes;
if ( qpd->exec() != QDialog::Accepted ) {
return;
}
// Hardcode these values
printer.setOrientation(QPrinter::Portrait);
printer.setPaperSize(QPrinter::A4);
printer.setFullPage(true);
QPainter painter;
if (! painter.begin(&printer)) { // failed to open file
QMessageBox::critical(this, "Printing Error", tr("failed to open file, is it writable?"), QMessageBox::Ok, QMessageBox::Ok);
return;
}
int walletCount = ui->walletCount->currentIndex() + 1;
int walletsPerPage = 4;
int pageHeight = printer.pageRect().height() - 50;
int walletHeight = ui->paperTemplate->height();
double computedWalletHeight = 0.9 * pageHeight / walletsPerPage;
double scale = computedWalletHeight / walletHeight;
double walletPadding = pageHeight * 0.05 / (walletsPerPage - 1) / scale;
QRegion walletRegion = QRegion(ui->paperTemplate->x(), ui->paperTemplate->y(),
ui->paperTemplate->width(), ui->paperTemplate->height());
painter.scale(scale, scale);
for(int i = 0; i < walletCount; i++) {
this->on_getNewAddress_clicked();
QPoint point = QPoint(50, 25 + ( i % walletsPerPage ) * (walletHeight + walletPadding));
this->render(&painter, point, walletRegion);
recipientPubKeyHashes.append(ui->addressText->text());
if ( i % walletsPerPage == ( walletsPerPage - 1 ) ) {
printer.newPage();
}
}
painter.end();
#ifdef ENABLE_WALLET
QStringList formatted;
WalletModelTransaction *tx;
while( true ) {
bool ok;
QString amountInput = QInputDialog::getText(this, tr("Load Paper Wallets"), "Please wait for wallets to print and verify readability.<br/>Enter the number of DOGE you wish to send to each wallet:", QLineEdit::Normal, QString(), &ok);
if(!ok) {
return;
}
WalletModel::UnlockContext ctx(this->model->requestUnlock());
if(!ctx.isValid())
{
return;
}
QList<SendCoinsRecipient> recipients;
quint64 amount = amountInput.toULongLong() * COIN;
foreach(const QString &dest, recipientPubKeyHashes)
{
recipients.append(SendCoinsRecipient(dest,tr("Paper wallet %1").arg(dest), amount,""));
formatted.append(tr("<b>%1</b> to Paper Wallet <span style='font-family: monospace;'>%2</span>").arg(amountInput,GUIUtil::HtmlEscape(dest)));
}
tx = new WalletModelTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
if (this->model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
prepareStatus = this->model->prepareTransaction(*tx, CoinControlDialog::coinControl);
else
prepareStatus = this->model->prepareTransaction(*tx);
if (prepareStatus.status == WalletModel::InvalidAddress) {
QMessageBox::critical(this, tr("Send Coins"), tr("The recipient address is not valid, please recheck."), QMessageBox::Ok, QMessageBox::Ok);
} else if (prepareStatus.status == WalletModel::InvalidAmount) {
QMessageBox::critical(this, tr("Send Coins"), tr("The amount to pay must be larger than 0"), QMessageBox::Ok, QMessageBox::Ok);
} else if (prepareStatus.status == WalletModel::AmountExceedsBalance) {
QMessageBox::critical(this, tr("Send Coins"), tr("The amount exceeds your balance."), QMessageBox::Ok, QMessageBox::Ok);
} else if (prepareStatus.status == WalletModel::AmountWithFeeExceedsBalance) {
QMessageBox::critical(this, tr("Send Coins"), tr("The total exceeds your balance when the transaction fee is included"), QMessageBox::Ok, QMessageBox::Ok);
} else if (prepareStatus.status == WalletModel::DuplicateAddress) {
QMessageBox::critical(this, tr("Send Coins"), tr("Duplicate address found, can only send to each address once per send operation."), QMessageBox::Ok, QMessageBox::Ok);
} else if (prepareStatus.status == WalletModel::TransactionCreationFailed) {
QMessageBox::critical(this, tr("Send Coins"), tr("Transaction creation failed!"), QMessageBox::Ok, QMessageBox::Ok);
} else if (prepareStatus.status == WalletModel::OK) {
break;
} else {
delete tx;
return;
}
}
// Stolen from sendcoinsdialog.cpp
qint64 txFee = tx->getTransactionFee();
QString questionString = tr("Are you sure you want to send?");
questionString.append("<br /><br />%1");
if(txFee > 0)
{
// append fee string if a fee is required
questionString.append("<hr /><span style='color:#aa0000;'>");
questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append("</span> ");
questionString.append(tr("added as transaction fee"));
}
// add total amount in all subdivision units
questionString.append("<hr />");
qint64 totalAmount = tx->getTotalTransactionAmount() + txFee;
QStringList alternativeUnits;
foreach(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
{
if(u != model->getOptionsModel()->getDisplayUnit())
alternativeUnits.append(BitcoinUnits::formatWithUnit(u, totalAmount));
}
questionString.append(tr("Total Amount %1 (= %2)")
.arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
.arg(alternativeUnits.join(" " + tr("or") + " ")));
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
questionString.arg(formatted.join("<br />")),
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel);
if(retval != QMessageBox::Yes)
{
delete tx;
return;
}
WalletModel::SendCoinsReturn sendStatus = this->model->sendCoins(*tx);
if (sendStatus.status == WalletModel::TransactionCommitFailed) {
QMessageBox::critical(this, tr("Send Coins"), tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), QMessageBox::Ok, QMessageBox::Ok);
}
delete tx;
#endif
return;
}
/** "Help message" dialog box */
HelpMessageDialog::HelpMessageDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::HelpMessageDialog)
{
ui->setupUi(this);
GUIUtil::restoreWindowGeometry("nHelpMessageDialogWindow", this->size(), this);
header = tr("Dogecoin Core") + " " + tr("version") + " " +
QString::fromStdString(FormatFullVersion()) + "\n\n" +
tr("Usage:") + "\n" +
" dogecoin-qt [" + tr("command-line options") + "] " + "\n";
coreOptions = QString::fromStdString(HelpMessage(HMM_BITCOIN_QT));
uiOptions = tr("UI options") + ":\n" +
" -choosedatadir " + tr("Choose data directory on startup (default: 0)") + "\n" +
" -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
" -min " + tr("Start minimized") + "\n" +
" -rootcertificates=<file> " + tr("Set SSL root certificates for payment request (default: -system-)") + "\n" +
" -splash " + tr("Show splash screen on startup (default: 1)");
ui->helpMessageLabel->setFont(GUIUtil::bitcoinAddressFont());
// Set help message text
ui->helpMessageLabel->setText(header + "\n" + coreOptions + "\n" + uiOptions);
}
HelpMessageDialog::~HelpMessageDialog()
{
GUIUtil::saveWindowGeometry("nHelpMessageDialogWindow", this);
delete ui;
}
void HelpMessageDialog::printToConsole()
{
// On other operating systems, the expected action is to print the message to the console.
QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions + "\n";
fprintf(stdout, "%s", strUsage.toStdString().c_str());
}
void HelpMessageDialog::showOrPrint()
{
#if defined(WIN32)
// On Windows, show a message box, as there is no stderr/stdout in windowed applications
exec();
#else
// On other operating systems, print help text to console
printToConsole();
#endif
}
void HelpMessageDialog::on_okButton_accepted()
{
close();
}
/** "Shutdown" window */
void ShutdownWindow::showShutdownWindow(BitcoinGUI *window)
{
if (!window)
return;
// Show a simple window indicating shutdown status
QWidget *shutdownWindow = new QWidget();
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(new QLabel(
tr("Dogecoin Core is shutting down...") + "<br /><br />" +
tr("Do not shut down the computer until this window disappears.")));
shutdownWindow->setLayout(layout);
// Center shutdown window at where main window was
const QPoint global = window->mapToGlobal(window->rect().center());
shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2);
shutdownWindow->show();
}