update QRCodeDialog

- remove unused #include <QDebug> and lblBTC label
- update Bitcoin input field to a BitcoinAmountField to allow Bitcoin unit selection
- use BitcoinUnits::format for the resulting amount parameter in the generated URI (always use BTC as per BIP21)
- move MAX_URI_LENGTH and EXPORT_IMAGE_SIZE to guiconstants.h
- add OptionsModel in AddressBookPage and use it in on_showQRCode_clicked() to pass it to QRCodeDialog
- add OptionsModel in QRCodeDialog to enable display unit updates
- add updateDisplayUnit() slot to be able to imediately update currently set bitcoin unit
- make all labels in the UI-file plain text
- resize dialog to match for an updated layout (fields are now stacked and new field)
- remove unused parameters from private slots
- only enable save button, when QR Code was generated
- show message when entered amound is invalid
- add read-only QPlainTextEdit field to output generated URI
This commit is contained in:
Philip Kaufmann 2012-06-24 18:28:05 +02:00
parent 2a919e396d
commit 5c83f797c5
7 changed files with 217 additions and 154 deletions

View File

@ -2,6 +2,7 @@
#include "ui_addressbookpage.h" #include "ui_addressbookpage.h"
#include "addresstablemodel.h" #include "addresstablemodel.h"
#include "optionsmodel.h"
#include "bitcoingui.h" #include "bitcoingui.h"
#include "editaddressdialog.h" #include "editaddressdialog.h"
#include "csvmodelwriter.h" #include "csvmodelwriter.h"
@ -20,6 +21,7 @@ AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::AddressBookPage), ui(new Ui::AddressBookPage),
model(0), model(0),
optionsModel(0),
mode(mode), mode(mode),
tab(tab) tab(tab)
{ {
@ -139,6 +141,11 @@ void AddressBookPage::setModel(AddressTableModel *model)
selectionChanged(); selectionChanged();
} }
void AddressBookPage::setOptionsModel(OptionsModel *optionsModel)
{
this->optionsModel = optionsModel;
}
void AddressBookPage::on_copyToClipboard_clicked() void AddressBookPage::on_copyToClipboard_clicked()
{ {
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address); GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
@ -314,6 +321,8 @@ void AddressBookPage::on_showQRCode_clicked()
QString address = index.data().toString(), label = index.sibling(index.row(), 0).data(Qt::EditRole).toString(); QString address = index.data().toString(), label = index.sibling(index.row(), 0).data(Qt::EditRole).toString();
QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this); QRCodeDialog *dialog = new QRCodeDialog(address, label, tab == ReceivingTab, this);
if(optionsModel)
dialog->setModel(optionsModel);
dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show(); dialog->show();
} }

View File

@ -7,6 +7,7 @@ namespace Ui {
class AddressBookPage; class AddressBookPage;
} }
class AddressTableModel; class AddressTableModel;
class OptionsModel;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QTableView; class QTableView;
@ -37,6 +38,7 @@ public:
~AddressBookPage(); ~AddressBookPage();
void setModel(AddressTableModel *model); void setModel(AddressTableModel *model);
void setOptionsModel(OptionsModel *optionsModel);
const QString &getReturnValue() const { return returnValue; } const QString &getReturnValue() const { return returnValue; }
public slots: public slots:
@ -46,6 +48,7 @@ public slots:
private: private:
Ui::AddressBookPage *ui; Ui::AddressBookPage *ui;
AddressTableModel *model; AddressTableModel *model;
OptionsModel *optionsModel;
Mode mode; Mode mode;
Tabs tab; Tabs tab;
QString returnValue; QString returnValue;

View File

@ -357,6 +357,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel)
connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool))); connect(clientModel, SIGNAL(error(QString,QString,bool)), this, SLOT(error(QString,QString,bool)));
rpcConsole->setClientModel(clientModel); rpcConsole->setClientModel(clientModel);
addressBookPage->setOptionsModel(clientModel->getOptionsModel());
receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel());
} }
} }

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>334</width> <width>340</width>
<height>425</height> <height>530</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -28,8 +28,8 @@
<height>300</height> <height>300</height>
</size> </size>
</property> </property>
<property name="text"> <property name="textFormat">
<string>QR Code</string> <enum>Qt::PlainText</enum>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
@ -39,134 +39,123 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPlainTextEdit" name="outUri">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item> <item>
<widget class="QWidget" name="widget" native="true"> <widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2"> <widget class="QCheckBox" name="chkReqPayment">
<item> <property name="enabled">
<layout class="QVBoxLayout" name="verticalLayout"> <bool>true</bool>
<item> </property>
<widget class="QCheckBox" name="chkReqPayment"> <property name="text">
<property name="enabled"> <string>Request Payment</string>
<bool>true</bool> </property>
</property> </widget>
<property name="text"> </item>
<string>Request Payment</string> <item>
</property> <layout class="QFormLayout" name="formLayout">
</widget> <property name="fieldGrowthPolicy">
</item> <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
<item> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> <item row="1" column="0">
<item> <widget class="QLabel" name="lblLabel">
<widget class="QLabel" name="lblAmount"> <property name="text">
<property name="sizePolicy"> <string>Label:</string>
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> </property>
<horstretch>0</horstretch> <property name="textFormat">
<verstretch>0</verstretch> <enum>Qt::PlainText</enum>
</sizepolicy> </property>
</property> <property name="alignment">
<property name="text"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<string>Amount:</string> </property>
</property> <property name="buddy">
<property name="alignment"> <cstring>lnLabel</cstring>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property>
</property> </widget>
<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="lblBTC">
<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>
<item> <item row="1" column="1">
<layout class="QGridLayout" name="gridLayout"> <widget class="QLineEdit" name="lnLabel"/>
<item row="0" column="0"> </item>
<widget class="QLabel" name="lblLabel"> <item row="2" column="0">
<property name="text"> <widget class="QLabel" name="lblMessage">
<string>Label:</string> <property name="text">
</property> <string>Message:</string>
<property name="alignment"> </property>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <property name="textFormat">
</property> <enum>Qt::PlainText</enum>
<property name="buddy"> </property>
<cstring>lnLabel</cstring> <property name="alignment">
</property> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</widget> </property>
</item> <property name="buddy">
<item row="0" column="1"> <cstring>lnMessage</cstring>
<widget class="QLineEdit" name="lnLabel"> </property>
<property name="minimumSize"> </widget>
<size> </item>
<width>100</width> <item row="2" column="1">
<height>0</height> <widget class="QLineEdit" name="lnMessage"/>
</size> </item>
</property> <item row="0" column="0">
</widget> <widget class="QLabel" name="lblAmount">
</item> <property name="sizePolicy">
<item row="1" column="0"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<widget class="QLabel" name="lblMessage"> <horstretch>0</horstretch>
<property name="text"> <verstretch>0</verstretch>
<string>Message:</string> </sizepolicy>
</property> </property>
<property name="alignment"> <property name="text">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <string>Amount:</string>
</property> </property>
<property name="buddy"> <property name="textFormat">
<cstring>lnMessage</cstring> <enum>Qt::PlainText</enum>
</property> </property>
</widget> <property name="alignment">
</item> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<item row="1" column="1"> </property>
<widget class="QLineEdit" name="lnMessage"> <property name="buddy">
<property name="minimumSize"> <cstring>lnReqAmount</cstring>
<size> </property>
<width>100</width> </widget>
<height>0</height> </item>
</size> <item row="0" column="1">
</property> <widget class="BitcoinAmountField" name="lnReqAmount">
</widget> <property name="enabled">
</item> <bool>false</bool>
</layout> </property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
@ -194,6 +183,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QSpinBox</extends>
<header>bitcoinamountfield.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -25,4 +25,10 @@ static const int STATUSBAR_ICONSIZE = 16;
*/ */
static const int TOOLTIP_WRAP_THRESHOLD = 80; static const int TOOLTIP_WRAP_THRESHOLD = 80;
/* Maximum allowed URI length */
static const int MAX_URI_LENGTH = 255;
/* QRCodeDialog -- size of exported QR Code image */
#define EXPORT_IMAGE_SIZE 256
#endif // GUICONSTANTS_H #endif // GUICONSTANTS_H

View File

@ -1,28 +1,34 @@
#include "qrcodedialog.h" #include "qrcodedialog.h"
#include "ui_qrcodedialog.h" #include "ui_qrcodedialog.h"
#include "bitcoinunits.h"
#include "guiconstants.h"
#include "guiutil.h" #include "guiutil.h"
#include "optionsmodel.h"
#include <QPixmap> #include <QPixmap>
#include <QUrl> #include <QUrl>
#include <QDebug>
#include <qrencode.h> #include <qrencode.h>
#define EXPORT_IMAGE_SIZE 256
QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) : QRCodeDialog::QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent) :
QDialog(parent), ui(new Ui::QRCodeDialog), address(addr) QDialog(parent),
ui(new Ui::QRCodeDialog),
model(0),
address(addr)
{ {
ui->setupUi(this); ui->setupUi(this);
setWindowTitle(QString("%1").arg(address)); setWindowTitle(QString("%1").arg(address));
ui->chkReqPayment->setVisible(enableReq); ui->chkReqPayment->setVisible(enableReq);
ui->lnReqAmount->setVisible(enableReq);
ui->lblAmount->setVisible(enableReq); ui->lblAmount->setVisible(enableReq);
ui->lblBTC->setVisible(enableReq); ui->lnReqAmount->setVisible(enableReq);
ui->lnLabel->setText(label); ui->lnLabel->setText(label);
ui->btnSaveAs->setEnabled(false);
genCode(); genCode();
} }
@ -31,6 +37,17 @@ QRCodeDialog::~QRCodeDialog()
delete ui; delete ui;
} }
void QRCodeDialog::setModel(OptionsModel *model)
{
this->model = model;
if (model)
connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
// update the display unit, to not use the default ("BTC")
updateDisplayUnit();
}
void QRCodeDialog::genCode() void QRCodeDialog::genCode()
{ {
QString uri = getURI(); QString uri = getURI();
@ -57,26 +74,34 @@ void QRCodeDialog::genCode()
} }
} }
QRcode_free(code); QRcode_free(code);
ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300));
ui->outUri->setPlainText(uri);
} }
else
ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message."));
} }
QString QRCodeDialog::getURI() QString QRCodeDialog::getURI()
{ {
QString ret = QString("bitcoin:%1").arg(address); QString ret = QString("bitcoin:%1").arg(address);
int paramCount = 0; int paramCount = 0;
if (ui->chkReqPayment->isChecked() && !ui->lnReqAmount->text().isEmpty())
ui->outUri->clear();
if (ui->chkReqPayment->isChecked())
{ {
bool ok = false; if (ui->lnReqAmount->validate())
ui->lnReqAmount->text().toDouble(&ok);
if (ok)
{ {
ret += QString("?amount=%1").arg(ui->lnReqAmount->text()); // even if we allow a non BTC unit input in lnReqAmount, we generate the URI with BTC as unit (as defined in BIP21)
ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, ui->lnReqAmount->value()));
paramCount++; paramCount++;
} }
else
{
ui->btnSaveAs->setEnabled(false);
ui->lblQRCode->setText(tr("The entered amount is invalid, please check."));
return QString("");
}
} }
if (!ui->lnLabel->text().isEmpty()) if (!ui->lnLabel->text().isEmpty())
@ -93,24 +118,29 @@ QString QRCodeDialog::getURI()
paramCount++; paramCount++;
} }
// limit URI length to 255 chars, to prevent a DoS against the QR-Code dialog // limit URI length to prevent a DoS against the QR-Code dialog
if (ret.length() < 256) if (ret.length() > MAX_URI_LENGTH)
return ret; {
else ui->btnSaveAs->setEnabled(false);
ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message."));
return QString(""); return QString("");
}
ui->btnSaveAs->setEnabled(true);
return ret;
} }
void QRCodeDialog::on_lnReqAmount_textChanged(const QString &arg1) void QRCodeDialog::on_lnReqAmount_textChanged()
{ {
genCode(); genCode();
} }
void QRCodeDialog::on_lnLabel_textChanged(const QString &arg1) void QRCodeDialog::on_lnLabel_textChanged()
{ {
genCode(); genCode();
} }
void QRCodeDialog::on_lnMessage_textChanged(const QString &arg1) void QRCodeDialog::on_lnMessage_textChanged()
{ {
genCode(); genCode();
} }
@ -122,7 +152,20 @@ void QRCodeDialog::on_btnSaveAs_clicked()
myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn); myImage.scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE).save(fn);
} }
void QRCodeDialog::on_chkReqPayment_toggled(bool) void QRCodeDialog::on_chkReqPayment_toggled(bool fChecked)
{ {
if (!fChecked)
// if chkReqPayment is not active, don't display lnReqAmount as invalid
ui->lnReqAmount->setValid(true);
genCode(); genCode();
} }
void QRCodeDialog::updateDisplayUnit()
{
if (model)
{
// Update lnReqAmount with the current unit
ui->lnReqAmount->setDisplayUnit(model->getDisplayUnit());
}
}

View File

@ -7,6 +7,7 @@
namespace Ui { namespace Ui {
class QRCodeDialog; class QRCodeDialog;
} }
class OptionsModel;
class QRCodeDialog : public QDialog class QRCodeDialog : public QDialog
{ {
@ -16,22 +17,25 @@ public:
explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0); explicit QRCodeDialog(const QString &addr, const QString &label, bool enableReq, QWidget *parent = 0);
~QRCodeDialog(); ~QRCodeDialog();
private slots: void setModel(OptionsModel *model);
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_chkReqPayment_toggled(bool checked); private slots:
void on_lnReqAmount_textChanged();
void on_lnLabel_textChanged();
void on_lnMessage_textChanged();
void on_btnSaveAs_clicked();
void on_chkReqPayment_toggled(bool fChecked);
void updateDisplayUnit();
private: private:
Ui::QRCodeDialog *ui; Ui::QRCodeDialog *ui;
OptionsModel *model;
QString address;
QImage myImage; QImage myImage;
QString getURI();
QString address;
void genCode(); void genCode();
QString getURI();
}; };
#endif // QRCODEDIALOG_H #endif // QRCODEDIALOG_H