Accept "bitcoin:" URL drops from browsers
authorWladimir J. van der Laan <laanwj@gmail.com>
Sun, 7 Aug 2011 14:04:48 +0000 (16:04 +0200)
committerWladimir J. van der Laan <laanwj@gmail.com>
Sun, 7 Aug 2011 14:04:48 +0000 (16:04 +0200)
src/qt/bitcoingui.cpp
src/qt/bitcoingui.h
src/qt/guiutil.cpp
src/qt/guiutil.h
src/qt/sendcoinsdialog.cpp
src/qt/sendcoinsdialog.h
src/qt/sendcoinsentry.cpp
src/qt/sendcoinsentry.h

index 84d8fe4..b20f633 100644 (file)
@@ -38,6 +38,9 @@
 #include <QDateTime>
 #include <QMovie>
 
+#include <QDragEnterEvent>
+#include <QUrl>
+
 #include <QDebug>
 
 #include <iostream>
@@ -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<QUrl> urls = event->mimeData()->urls();
+        foreach(const QUrl &url, urls)
+        {
+            sendCoinsPage->handleURL(&url);
+        }
+    }
+
+    event->acceptProposedAction();
+}
+
index c48fa8c..377da72 100644 (file)
@@ -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;
index ece0690..3516d4f 100644 (file)
@@ -1,5 +1,7 @@
 #include "guiutil.h"
 #include "bitcoinaddressvalidator.h"
+#include "walletmodel.h"
+#include "bitcoinunits.h"
 
 #include "headers.h"
 
@@ -8,6 +10,7 @@
 #include <QDoubleValidator>
 #include <QFont>
 #include <QLineEdit>
+#include <QUrl>
 
 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;
+}
index fb5c575..012e497 100644 (file)
@@ -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
index 54cae21..4d315fb 100644 (file)
@@ -5,11 +5,12 @@
 #include "addressbookpage.h"
 #include "optionsmodel.h"
 #include "sendcoinsentry.h"
-
+#include "guiutil.h"
 
 #include <QMessageBox>
 #include <QLocale>
 #include <QTextDocument>
+#include <QDebug>
 
 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<SendCoinsEntry*>(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<SendCoinsEntry*>(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);
+}
index 0f90be8..9c56e51 100644 (file)
@@ -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:
index abdbc81..e97f675 100644 (file)
@@ -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();
+}
+
index 55fd12a..ccc223b 100644 (file)
@@ -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);