Added QRCode generation functions via libqrencode. Switch on with USE_QRENCODE=1.
authorsje397 <sje397@gmail.com>
Thu, 10 Nov 2011 14:20:17 +0000 (01:20 +1100)
committersje397 <sje397@gmail.com>
Tue, 20 Dec 2011 11:11:24 +0000 (22:11 +1100)
Amended build docs for Linux and OSX, and OSX makefile.
Added package 'qrencode' to gitian.yml

13 files changed:
bitcoin-qt.pro
contrib/gitian-descriptors/gitian.yml
doc/build-osx.txt
doc/build-unix.txt
src/makefile.osx
src/qt/addressbookpage.cpp
src/qt/addressbookpage.h
src/qt/bitcoin.qrc
src/qt/forms/addressbookpage.ui
src/qt/forms/qrcodedialog.ui [new file with mode: 0644]
src/qt/qrcodedialog.cpp [new file with mode: 0644]
src/qt/qrcodedialog.h [new file with mode: 0644]
src/qt/res/images/qrcode.png [new file with mode: 0644]

index 853f2fa..4a8dedc 100644 (file)
@@ -19,6 +19,14 @@ OBJECTS_DIR = build
 MOC_DIR = build
 UI_DIR = build
 
+# use: qmake "USE_QRCODE=1"
+# libqrencode (http://fukuchi.org/works/qrencode/index.en.html) must be installed for support
+contains(USE_QRCODE, 1) {
+    message(Building with QRCode support)
+    DEFINES += USE_QRCODE
+    LIBS += -lqrencode
+}
+
 # use: qmake "RELEASE=1"
 contains(RELEASE, 1) {
     # Mac: compile for maximum compatibility (10.5, 32-bit)
@@ -199,6 +207,12 @@ FORMS += \
     src/qt/forms/sendcoinsentry.ui \
     src/qt/forms/askpassphrasedialog.ui
 
+contains(USE_QRCODE, 1) {
+HEADERS += src/qt/qrcodedialog.h
+SOURCES += src/qt/qrcodedialog.cpp
+FORMS += src/qt/forms/qrcodedialog.ui
+}
+
 CODECFORTR = UTF-8
 
 # for lrelease/lupdate
index 65005c6..6f503ac 100644 (file)
@@ -16,6 +16,7 @@ packages:
 - "libssl-dev"
 - "git-core"
 - "unzip"
+- "qrencode"
 reference_datetime: "2011-01-30 00:00:00"
 remotes:
 - "url": "https://github.com/bitcoin/bitcoin.git"
index 8002441..d47febe 100644 (file)
@@ -43,6 +43,9 @@ pushd bitcoin/contrib/minipupnpc; sudo port install; popd
 (this will be unnecessary soon, you will just port install miniupnpc
 along with the rest of the dependencies).
 
+Optionally install qrencode (and set USE_QRCODE=1):
+sudo port install qrencode
+
 4.  Now you should be able to build bitcoind:
 
 cd bitcoin/src
index f4178ca..5389339 100644 (file)
@@ -23,12 +23,13 @@ the graphical bitcoin.
 Dependencies
 ------------
 
- Library    Purpose       Description
- -------    -------       -----------
- libssl     SSL Support   Secure communications
- libdb4.8   Berkeley DB   Blockchain & wallet storage
- libboost   Boost         C++ Library
- miniupnpc  UPnP Support  Optional firewall-jumping support
+ Library     Purpose           Description
+ -------     -------           -----------
+ libssl      SSL Support       Secure communications
+ libdb4.8    Berkeley DB       Blockchain & wallet storage
+ libboost    Boost             C++ Library
+ miniupnpc   UPnP Support      Optional firewall-jumping support
+ libqrencode QRCode generation Optional QRCode generation
 
 miniupnpc may be used for UPnP port mapping.  It can be downloaded from
 http://miniupnp.tuxfamily.org/files/.  UPnP support is compiled in and
@@ -37,6 +38,12 @@ turned off by default.  Set USE_UPNP to a different value to control this:
  USE_UPNP=0    (the default) UPnP support turned off by default at runtime
  USE_UPNP=1    UPnP support turned on by default at runtime
 
+libqrencode may be used for QRCode image generation. It can be downloaded
+from http://fukuchi.org/works/qrencode/index.html.en, or installed via
+your package manager. Set USE_QRCODE to control this:
+ USE_QRCODE=0   (the default) No QRCode support - libarcode not required
+ USE_QRCODE=1   QRCode support enabled
+
 Licenses of statically linked libraries:
  Berkeley DB   New BSD license with additional requirement that linked
                software must be free open source
@@ -50,7 +57,6 @@ Versions used in this release:
  Boost         1.37
  miniupnpc     1.6
 
-
 Dependency Build Instructions: Ubuntu & Debian
 ----------------------------------------------
 sudo apt-get install build-essential
index 4b0b521..2bb8980 100644 (file)
@@ -95,6 +95,10 @@ else
 endif
 endif
 
+ifdef USE_QRCODE
+       DEFS += -DUSE_QRCODE=$(USE_QRCODE)
+       LIBS += -lqrencode
+endif
 
 all: bitcoind
 
index 0a147c9..d207fe3 100644 (file)
 #include <QFileDialog>
 #include <QMessageBox>
 
+#ifdef USE_QRCODE
+#include "qrcodedialog.h"
+#endif
+
 AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
     QDialog(parent),
     ui(new Ui::AddressBookPage),
@@ -25,6 +29,10 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
     ui->deleteButton->setIcon(QIcon());
 #endif
 
+#ifndef USE_QRCODE
+    ui->showQRCode->setVisible(false);
+#endif
+
     switch(mode)
     {
     case ForSending:
@@ -169,10 +177,12 @@ void AddressBookPage::selectionChanged()
             break;
         }
         ui->copyToClipboard->setEnabled(true);
+        ui->showQRCode->setEnabled(true);
     }
     else
     {
         ui->deleteButton->setEnabled(false);
+        ui->showQRCode->setEnabled(false);
         ui->copyToClipboard->setEnabled(false);
     }
 }
@@ -227,3 +237,23 @@ void AddressBookPage::exportClicked()
                               QMessageBox::Abort, QMessageBox::Abort);
     }
 }
+
+void AddressBookPage::on_showQRCode_clicked()
+{
+#ifdef USE_QRCODE
+    QTableView *table = ui->tableView;
+    QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
+
+
+    QRCodeDialog *d;
+    foreach (QModelIndex index, indexes)
+    {
+        QString address = index.data().toString(),
+            label = index.sibling(index.row(), 0).data().toString(),
+            title = QString("%1 << %2 >>").arg(label).arg(address);
+
+        QRCodeDialog *d = new QRCodeDialog(title, address, label, tab == ReceivingTab, this);
+        d->show();
+    }
+#endif
+}
index 1a97f3d..2538f31 100644 (file)
@@ -54,6 +54,7 @@ private slots:
     void on_newAddressButton_clicked();
     void on_copyToClipboard_clicked();
     void selectionChanged();
+    void on_showQRCode_clicked();
 };
 
 #endif // ADDRESSBOOKDIALOG_H
index aea61d6..5693ae1 100644 (file)
@@ -41,6 +41,7 @@
     <qresource prefix="/images">
         <file alias="about">res/images/about.png</file>
         <file alias="splash">res/images/splash2.jpg</file>
+        <file alias="qrcode">res/images/qrcode.png</file>
     </qresource>
     <qresource prefix="/movies">
         <file alias="update_spinner">res/movies/update_spinner.mng</file>
index fb098c8..9b301cb 100644 (file)
       </widget>
      </item>
      <item>
+      <widget class="QPushButton" name="showQRCode">
+       <property name="text">
+        <string>Show &amp;QR Code</string>
+       </property>
+       <property name="icon">
+        <iconset resource="../bitcoin.qrc">
+         <normaloff>:/images/qrcode</normaloff>:/images/qrcode</iconset>
+       </property>
+      </widget>
+     </item>
+     <item>
       <widget class="QPushButton" name="deleteButton">
        <property name="toolTip">
         <string>Delete the currently selected address from the list. Only sending addresses can be deleted.</string>
diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui
new file mode 100644 (file)
index 0000000..fa21f60
--- /dev/null
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QRCodeDialog</class>
+ <widget class="QDialog" name="QRCodeDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>320</width>
+    <height>404</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_3">
+   <item>
+    <widget class="QLabel" name="lblQRCode">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>300</width>
+       <height>300</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>QR Code</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QWidget" name="widget" native="true">
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <layout class="QVBoxLayout" name="verticalLayout">
+          <item>
+           <widget class="QCheckBox" name="chkReq">
+            <property name="enabled">
+             <bool>true</bool>
+            </property>
+            <property name="text">
+             <string>Request Payment</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="horizontalLayout">
+            <item>
+             <widget class="QLabel" name="lblAm1">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+                <horstretch>0</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="text">
+               <string>Amount:</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+              <property name="buddy">
+               <cstring>lnReqAmount</cstring>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLineEdit" name="lnReqAmount">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="minimumSize">
+               <size>
+                <width>60</width>
+                <height>0</height>
+               </size>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QLabel" name="lblAm2">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+                <horstretch>0</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="text">
+               <string>BTC</string>
+              </property>
+              <property name="buddy">
+               <cstring>lnReqAmount</cstring>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QGridLayout" name="gridLayout">
+          <item row="0" column="0">
+           <widget class="QLabel" name="label_3">
+            <property name="text">
+             <string>Label:</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+            </property>
+            <property name="buddy">
+             <cstring>lnLabel</cstring>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLineEdit" name="lnLabel">
+            <property name="minimumSize">
+             <size>
+              <width>100</width>
+              <height>0</height>
+             </size>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_4">
+            <property name="text">
+             <string>Message:</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+            </property>
+            <property name="buddy">
+             <cstring>lnMessage</cstring>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLineEdit" name="lnMessage">
+            <property name="minimumSize">
+             <size>
+              <width>100</width>
+              <height>0</height>
+             </size>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="btnSaveAs">
+          <property name="text">
+           <string>&amp;Save As...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>chkReq</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>lnReqAmount</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>92</x>
+     <y>285</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>98</x>
+     <y>311</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp
new file mode 100644 (file)
index 0000000..ed4c758
--- /dev/null
@@ -0,0 +1,106 @@
+#include "qrcodedialog.h"
+#include "ui_qrcodedialog.h"
+#include <QPixmap>
+#include <QUrl>
+#include <QFileDialog>
+#include <QDesktopServices>
+#include <QDebug>
+
+#include <qrencode.h>
+
+#define EXPORT_IMAGE_SIZE   256
+
+QRCodeDialog::QRCodeDialog(const QString &title, const QString &addr, const QString &label, bool enableReq, QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::QRCodeDialog),
+    address(addr)
+{
+    ui->setupUi(this);
+    setWindowTitle(title);
+    setAttribute(Qt::WA_DeleteOnClose);
+
+    ui->chkReq->setVisible(enableReq);
+    ui->lnReqAmount->setVisible(enableReq);
+    ui->lblAm1->setVisible(enableReq);
+    ui->lblAm2->setVisible(enableReq);
+
+    ui->lnLabel->setText(label);
+
+    genCode();
+}
+
+QRCodeDialog::~QRCodeDialog()
+{
+    delete ui;
+}
+
+void QRCodeDialog::genCode() {
+
+    QString uri = getURI();
+    //qDebug() << "Encoding:" << uri.toUtf8().constData();
+    QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);
+    myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32);
+    myImage.fill(0xffffff);
+    unsigned char *p = code->data;
+    for(int y = 0; y < code->width; y++) {
+        for(int x = 0; x < code->width; x++) {
+            myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff));
+            p++;
+        }
+    }
+    QRcode_free(code);
+    ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300));
+}
+
+QString QRCodeDialog::getURI() {
+    QString ret = QString("bitcoin:%1").arg(address);
+
+    int paramCount = 0;
+    if(ui->chkReq->isChecked() && ui->lnReqAmount->text().isEmpty() == false) {
+        bool ok= false;
+        double amount = ui->lnReqAmount->text().toDouble(&ok);
+        if(ok) {
+            ret += QString("?amount=%1X8").arg(ui->lnReqAmount->text());
+            paramCount++;
+        }
+    }
+
+    if(ui->lnLabel->text().isEmpty() == false) {
+        QString lbl(QUrl::toPercentEncoding(ui->lnLabel->text()));
+        ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
+        paramCount++;
+    }
+
+    if(ui->lnMessage->text().isEmpty() == false) {
+        QString msg(QUrl::toPercentEncoding(ui->lnMessage->text()));
+        ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
+        paramCount++;
+    }
+
+    return ret;
+}
+
+void QRCodeDialog::on_lnReqAmount_textChanged(const QString &) {
+    genCode();
+}
+
+void QRCodeDialog::on_lnLabel_textChanged(const QString &) {
+    genCode();
+}
+
+void QRCodeDialog::on_lnMessage_textChanged(const QString &) {
+    genCode();
+}
+
+void QRCodeDialog::on_btnSaveAs_clicked()
+{
+    QString fn = QFileDialog::getSaveFileName(this, "Save Image...", QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation), "Images (*.png)");
+    if(!fn.isEmpty()) {
+        myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn);
+    }
+}
+
+void QRCodeDialog::on_chkReq_toggled(bool)
+{
+    genCode();
+}
diff --git a/src/qt/qrcodedialog.h b/src/qt/qrcodedialog.h
new file mode 100644 (file)
index 0000000..7463a88
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef QRCODEDIALOG_H
+#define QRCODEDIALOG_H
+
+#include <QDialog>
+#include <QImage>
+
+namespace Ui {
+    class QRCodeDialog;
+}
+
+class QRCodeDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit QRCodeDialog(const QString &title, const QString &address, const QString &label, bool allowReq, QWidget *parent = 0);
+    ~QRCodeDialog();
+
+private slots:
+    void on_lnReqAmount_textChanged(const QString &arg1);
+    void on_lnLabel_textChanged(const QString &arg1);
+    void on_lnMessage_textChanged(const QString &arg1);
+    void on_btnSaveAs_clicked();
+
+    void on_chkReq_toggled(bool checked);
+
+private:
+    Ui::QRCodeDialog *ui;
+    QImage myImage;
+
+    QString getURI();
+    QString address;
+
+    void genCode();
+};
+
+#endif // QRCODEDIALOG_H
diff --git a/src/qt/res/images/qrcode.png b/src/qt/res/images/qrcode.png
new file mode 100644 (file)
index 0000000..c89a49b
Binary files /dev/null and b/src/qt/res/images/qrcode.png differ