From db7f023417eeeb96eed35c9d06541544abcd7033 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Sun, 7 Aug 2011 16:04:48 +0200 Subject: [PATCH] Accept "bitcoin:" URL drops from browsers --- src/qt/bitcoingui.cpp | 35 ++++++++++++++++++++++++++-- src/qt/bitcoingui.h | 3 +++ src/qt/guiutil.cpp | 22 ++++++++++++++++++ src/qt/guiutil.h | 8 ++++++- src/qt/sendcoinsdialog.cpp | 47 ++++++++++++++++++++++++++++++-------- src/qt/sendcoinsdialog.h | 10 +++++++- src/qt/sendcoinsentry.cpp | 14 +++++++++++- src/qt/sendcoinsentry.h | 6 +++++ 8 files changed, 130 insertions(+), 15 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 84d8fe46..b20f633f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -38,6 +38,9 @@ #include #include +#include +#include + #include #include @@ -143,7 +146,9 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // Clicking on a transaction simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); - gotoOverviewPage(); + setAcceptDrops(true); + + gotoOverviewPage(); } void BitcoinGUI::createActions() @@ -502,10 +507,36 @@ void BitcoinGUI::gotoReceiveCoinsPage() void BitcoinGUI::gotoSendCoinsPage() { sendCoinsAction->setChecked(true); - sendCoinsPage->clear(); + if(centralWidget->currentWidget() != sendCoinsPage) + { + // Clear the current contents if we arrived from another tab + sendCoinsPage->clear(); + } centralWidget->setCurrentWidget(sendCoinsPage); exportAction->setEnabled(false); disconnect(exportAction, SIGNAL(triggered()), 0, 0); } +void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only URLs + if(event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void BitcoinGUI::dropEvent(QDropEvent *event) +{ + if(event->mimeData()->hasUrls()) + { + gotoSendCoinsPage(); + QList urls = event->mimeData()->urls(); + foreach(const QUrl &url, urls) + { + sendCoinsPage->handleURL(&url); + } + } + + event->acceptProposedAction(); +} + diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c48fa8cf..377da726 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -20,6 +20,7 @@ class QAbstractItemModel; class QModelIndex; class QProgressBar; class QStackedWidget; +class QUrl; QT_END_NAMESPACE class BitcoinGUI : public QMainWindow @@ -41,6 +42,8 @@ public: protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); + void dragEnterEvent(QDragEnterEvent *event); + void dropEvent(QDropEvent *event); private: ClientModel *clientModel; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index ece06907..3516d4f8 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,5 +1,7 @@ #include "guiutil.h" #include "bitcoinaddressvalidator.h" +#include "walletmodel.h" +#include "bitcoinunits.h" #include "headers.h" @@ -8,6 +10,7 @@ #include #include #include +#include QString GUIUtil::DateTimeStr(qint64 nTime) { @@ -41,3 +44,22 @@ void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent) widget->setValidator(amountValidator); widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter); } + +bool GUIUtil::parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out) +{ + if(url->scheme() != QString("bitcoin")) + return false; + + SendCoinsRecipient rv; + rv.address = url->path(); + rv.label = url->queryItemValue("label"); + if(!BitcoinUnits::parse(BitcoinUnits::BTC, url->queryItemValue("amount"), &rv.amount)) + { + return false; + } + if(out) + { + *out = rv; + } + return true; +} diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index fb5c575a..012e4975 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -8,20 +8,26 @@ class QFont; class QLineEdit; class QWidget; class QDateTime; +class QUrl; QT_END_NAMESPACE +class SendCoinsRecipient; class GUIUtil { public: + // Create human-readable string from date static QString DateTimeStr(qint64 nTime); static QString DateTimeStr(const QDateTime &datetime); // Render bitcoin addresses in monospace font static QFont bitcoinAddressFont(); + // Set up widgets for address and amounts static void setupAddressWidget(QLineEdit *widget, QWidget *parent); - static void setupAmountWidget(QLineEdit *widget, QWidget *parent); + + // Parse "bitcoin:" URL into recipient object, return true on succesful parsing + static bool parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out); }; #endif // GUIUTIL_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 54cae21a..4d315fb7 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -5,11 +5,12 @@ #include "addressbookpage.h" #include "optionsmodel.h" #include "sendcoinsentry.h" - +#include "guiutil.h" #include #include #include +#include SendCoinsDialog::SendCoinsDialog(QWidget *parent) : QDialog(parent), @@ -133,17 +134,11 @@ void SendCoinsDialog::on_sendButton_clicked() void SendCoinsDialog::clear() { // Remove entries until only one left - while(ui->entries->count() > 1) + while(ui->entries->count()) { delete ui->entries->takeAt(0)->widget(); } - - // Reset the entry that is left to empty - SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(0)->widget()); - if(entry) - { - entry->clear(); - } + addEntry(); updateRemoveEnabled(); @@ -160,7 +155,7 @@ void SendCoinsDialog::accept() clear(); } -void SendCoinsDialog::addEntry() +SendCoinsEntry *SendCoinsDialog::addEntry() { SendCoinsEntry *entry = new SendCoinsEntry(this); entry->setModel(model); @@ -171,6 +166,7 @@ void SendCoinsDialog::addEntry() // Focus the field, so that entry can start immediately entry->clear(); + return entry; } void SendCoinsDialog::updateRemoveEnabled() @@ -208,3 +204,34 @@ QWidget *SendCoinsDialog::setupTabChain(QWidget *prev) QWidget::setTabOrder(ui->addButton, ui->sendButton); return ui->sendButton; } + +void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) +{ + SendCoinsEntry *entry = 0; + // Replace the first entry if it is still unused + if(ui->entries->count() == 1) + { + SendCoinsEntry *first = qobject_cast(ui->entries->itemAt(0)->widget()); + if(first->isClear()) + { + entry = first; + } + } + if(!entry) + { + entry = addEntry(); + } + + entry->setValue(rv); +} + + +void SendCoinsDialog::handleURL(const QUrl *url) +{ + SendCoinsRecipient rv; + if(!GUIUtil::parseBitcoinURL(url, &rv)) + { + return; + } + pasteEntry(rv); +} diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 0f90be81..9c56e518 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -8,6 +8,11 @@ namespace Ui { } class WalletModel; class SendCoinsEntry; +class SendCoinsRecipient; + +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE class SendCoinsDialog : public QDialog { @@ -23,11 +28,14 @@ public: // Hence we have to set it up manually QWidget *setupTabChain(QWidget *prev); + void pasteEntry(const SendCoinsRecipient &rv); + void handleURL(const QUrl *url); + public slots: void clear(); void reject(); void accept(); - void addEntry(); + SendCoinsEntry *addEntry(); void updateRemoveEnabled(); private: diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index abdbc81b..e97f675f 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -102,7 +102,6 @@ bool SendCoinsEntry::validate() } } - if(!ui->payTo->hasAcceptableInput() || (model && !model->validateAddress(ui->payTo->text()))) { @@ -133,3 +132,16 @@ QWidget *SendCoinsEntry::setupTabChain(QWidget *prev) QWidget::setTabOrder(ui->deleteButton, ui->addAsLabel); return ui->payAmount->setupTabChain(ui->addAsLabel); } + +void SendCoinsEntry::setValue(const SendCoinsRecipient &value) +{ + ui->payTo->setText(value.address); + ui->addAsLabel->setText(value.label); + ui->payAmount->setValue(value.amount); +} + +bool SendCoinsEntry::isClear() +{ + return ui->payTo->text().isEmpty(); +} + diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 55fd12a1..ccc223b5 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -20,6 +20,12 @@ public: void setModel(WalletModel *model); bool validate(); SendCoinsRecipient getValue(); + + // Return true if the entry is still empty and unedited + bool isClear(); + + void setValue(const SendCoinsRecipient &value); + // Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907) // Hence we have to set it up manually QWidget *setupTabChain(QWidget *prev);