Page MenuHomePhabricator

No OneTemporary

diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp
index b5a798ca31..76aa87b134 100644
--- a/src/qt/addressbookpage.cpp
+++ b/src/qt/addressbookpage.cpp
@@ -1,329 +1,327 @@
#include "addressbookpage.h"
#include "ui_addressbookpage.h"
#include "addresstablemodel.h"
#include "bitcoingui.h"
#include "editaddressdialog.h"
#include "csvmodelwriter.h"
#include "guiutil.h"
#include <QSortFilterProxyModel>
#include <QClipboard>
-#include <QFileDialog>
#include <QMessageBox>
#include <QMenu>
#ifdef USE_QRCODE
#include "qrcodedialog.h"
#endif
AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
QDialog(parent),
ui(new Ui::AddressBookPage),
model(0),
mode(mode),
tab(tab)
{
ui->setupUi(this);
#ifdef Q_WS_MAC // Icons on push buttons are very uncommon on Mac
ui->newAddressButton->setIcon(QIcon());
ui->copyToClipboard->setIcon(QIcon());
ui->deleteButton->setIcon(QIcon());
#endif
#ifndef USE_QRCODE
ui->showQRCode->setVisible(false);
#endif
switch(mode)
{
case ForSending:
connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableView->setFocus();
break;
case ForEditing:
ui->buttonBox->hide();
break;
}
switch(tab)
{
case SendingTab:
ui->labelExplanation->hide();
break;
case ReceivingTab:
break;
}
ui->tableView->setTabKeyNavigation(false);
ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
// Context menu actions
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *editAction = new QAction(tr("Edit"), this);
deleteAction = new QAction(tr("Delete"), this);
contextMenu = new QMenu();
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(editAction);
contextMenu->addAction(deleteAction);
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(on_copyToClipboard_clicked()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(onCopyLabelAction()));
connect(editAction, SIGNAL(triggered()), this, SLOT(onEditAction()));
connect(deleteAction, SIGNAL(triggered()), this, SLOT(on_deleteButton_clicked()));
connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
// Pass through accept action from button box
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
}
AddressBookPage::~AddressBookPage()
{
delete ui;
}
void AddressBookPage::setModel(AddressTableModel *model)
{
this->model = model;
if(!model)
return;
// Refresh list from core
model->updateList();
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(model);
proxyModel->setDynamicSortFilter(true);
switch(tab)
{
case ReceivingTab:
// Receive filter
proxyModel->setFilterRole(AddressTableModel::TypeRole);
proxyModel->setFilterFixedString(AddressTableModel::Receive);
break;
case SendingTab:
// Send filter
proxyModel->setFilterRole(AddressTableModel::TypeRole);
proxyModel->setFilterFixedString(AddressTableModel::Send);
break;
}
ui->tableView->setModel(proxyModel);
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
// Set column widths
ui->tableView->horizontalHeader()->resizeSection(
AddressTableModel::Address, 320);
ui->tableView->horizontalHeader()->setResizeMode(
AddressTableModel::Label, QHeaderView::Stretch);
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(selectionChanged()));
if(mode == ForSending)
{
// Auto-select first row when in sending mode
ui->tableView->selectRow(0);
}
selectionChanged();
}
void AddressBookPage::on_copyToClipboard_clicked()
{
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
}
void AddressBookPage::onCopyLabelAction()
{
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
}
void AddressBookPage::onEditAction()
{
if(!ui->tableView->selectionModel())
return;
QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
if(indexes.isEmpty())
return;
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::EditSendingAddress :
EditAddressDialog::EditReceivingAddress);
dlg.setModel(model);
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
dlg.loadRow(origIndex.row());
dlg.exec();
}
void AddressBookPage::on_signMessage_clicked()
{
QTableView *table = ui->tableView;
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
QString addr;
foreach (QModelIndex index, indexes)
{
QVariant address = index.data();
addr = address.toString();
}
QObject *qoGUI = parent()->parent();
BitcoinGUI *gui = qobject_cast<BitcoinGUI *>(qoGUI);
if (gui)
gui->gotoMessagePage(addr);
}
void AddressBookPage::on_newAddressButton_clicked()
{
if(!model)
return;
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::NewSendingAddress :
EditAddressDialog::NewReceivingAddress);
dlg.setModel(model);
if(dlg.exec())
{
// Select row for newly created address
QString address = dlg.getAddress();
QModelIndexList lst = proxyModel->match(proxyModel->index(0,
AddressTableModel::Address, QModelIndex()),
Qt::EditRole, address, 1, Qt::MatchExactly);
if(!lst.isEmpty())
{
ui->tableView->setFocus();
ui->tableView->selectRow(lst.at(0).row());
}
}
}
void AddressBookPage::on_deleteButton_clicked()
{
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
QModelIndexList indexes = table->selectionModel()->selectedRows();
if(!indexes.isEmpty())
{
table->model()->removeRow(indexes.at(0).row());
}
}
void AddressBookPage::selectionChanged()
{
// Set button states based on selected tab and selection
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
if(table->selectionModel()->hasSelection())
{
switch(tab)
{
case SendingTab:
// In sending tab, allow deletion of selection
ui->deleteButton->setEnabled(true);
deleteAction->setEnabled(true);
ui->signMessage->setEnabled(false);
break;
case ReceivingTab:
// Deleting receiving addresses, however, is not allowed
ui->deleteButton->setEnabled(false);
deleteAction->setEnabled(false);
ui->signMessage->setEnabled(true);
break;
}
ui->copyToClipboard->setEnabled(true);
ui->showQRCode->setEnabled(true);
}
else
{
ui->deleteButton->setEnabled(false);
ui->showQRCode->setEnabled(false);
ui->copyToClipboard->setEnabled(false);
ui->signMessage->setEnabled(false);
}
}
void AddressBookPage::done(int retval)
{
QTableView *table = ui->tableView;
if(!table->selectionModel() || !table->model())
return;
// When this is a tab/widget and not a model dialog, ignore "done"
if(mode == ForEditing)
return;
// Figure out which address was selected, and return it
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
foreach (QModelIndex index, indexes)
{
QVariant address = table->model()->data(index);
returnValue = address.toString();
}
if(returnValue.isEmpty())
{
// If no address entry selected, return rejected
retval = Rejected;
}
QDialog::done(retval);
}
void AddressBookPage::exportClicked()
{
// CSV is currently the only supported format
- QString filename = QFileDialog::getSaveFileName(
+ QString filename = GUIUtil::getSaveFileName(
this,
- tr("Export Address Book Data"),
- QDir::currentPath(),
+ tr("Export Address Book Data"), QString(),
tr("Comma separated file (*.csv)"));
if (filename.isNull()) return;
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(proxyModel);
writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
if(!writer.write())
{
QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
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
}
void AddressBookPage::contextualMenu(const QPoint &point)
{
QModelIndex index = ui->tableView->indexAt(point);
if(index.isValid())
{
contextMenu->exec(QCursor::pos());
}
}
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 29ef554ac3..b8e74203b9 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -1,123 +1,172 @@
#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "headers.h"
#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
#include <QTextDocument> // For Qt::escape
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
+#include <QFileDialog>
+#include <QDesktopServices>
QString GUIUtil::dateTimeStr(qint64 nTime)
{
return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}
QString GUIUtil::dateTimeStr(const QDateTime &date)
{
return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}
QFont GUIUtil::bitcoinAddressFont()
{
QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter);
return font;
}
void GUIUtil::setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
widget->setValidator(new BitcoinAddressValidator(parent));
widget->setFont(bitcoinAddressFont());
}
void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent)
{
QDoubleValidator *amountValidator = new QDoubleValidator(parent);
amountValidator->setDecimals(8);
amountValidator->setBottom(0.0);
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.amount = 0;
QList<QPair<QString, QString> > items = url->queryItems();
for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
{
bool fShouldReturnFalse = false;
if (i->first.startsWith("req-"))
{
i->first.remove(0, 4);
fShouldReturnFalse = true;
}
if (i->first == "label")
{
rv.label = i->second;
fShouldReturnFalse = false;
}
else if (i->first == "amount")
{
if(!i->second.isEmpty())
{
if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
{
return false;
}
}
fShouldReturnFalse = false;
}
if (fShouldReturnFalse)
return false;
}
if(out)
{
*out = rv;
}
return true;
}
QString GUIUtil::HtmlEscape(const QString& str, bool fMultiLine)
{
QString escaped = Qt::escape(str);
if(fMultiLine)
{
escaped = escaped.replace("\n", "<br>\n");
}
return escaped;
}
QString GUIUtil::HtmlEscape(const std::string& str, bool fMultiLine)
{
return HtmlEscape(QString::fromStdString(str), fMultiLine);
}
void GUIUtil::copyEntryData(QAbstractItemView *view, int column, int role)
{
if(!view || !view->selectionModel())
return;
QModelIndexList selection = view->selectionModel()->selectedRows(column);
if(!selection.isEmpty())
{
// Copy first item
QApplication::clipboard()->setText(selection.at(0).data(role).toString());
}
}
+
+QString GUIUtil::getSaveFileName(QWidget *parent, const QString &caption,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedSuffixOut)
+{
+ QString selectedFilter;
+ QString myDir;
+ if(dir.isEmpty()) // Default to user documents location
+ {
+ myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
+ }
+ else
+ {
+ myDir = dir;
+ }
+ QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);
+
+ /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
+ QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
+ QString selectedSuffix;
+ if(filter_re.exactMatch(selectedFilter))
+ {
+ selectedSuffix = filter_re.cap(1);
+ }
+
+ /* Add suffix if needed */
+ QFileInfo info(result);
+ if(!result.isEmpty())
+ {
+ if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
+ {
+ /* No suffix specified, add selected suffix */
+ if(!result.endsWith("."))
+ result.append(".");
+ result.append(selectedSuffix);
+ }
+ }
+
+ /* Return selected suffix if asked to */
+ if(selectedSuffixOut)
+ {
+ *selectedSuffixOut = selectedSuffix;
+ }
+ return result;
+}
+
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 3a81bd2f00..8c85668465 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -1,50 +1,64 @@
#ifndef GUIUTIL_H
#define GUIUTIL_H
#include <QString>
QT_BEGIN_NAMESPACE
class QFont;
class QLineEdit;
class QWidget;
class QDateTime;
class QUrl;
class QAbstractItemView;
QT_END_NAMESPACE
class SendCoinsRecipient;
/** Static utility functions used by the Bitcoin Qt UI.
*/
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
// See Bitcoin URL definition discussion here: https://bitcointalk.org/index.php?topic=33490.0
static bool parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out);
// HTML escaping for rich text controls
static QString HtmlEscape(const QString& str, bool fMultiLine=false);
static QString HtmlEscape(const std::string& str, bool fMultiLine=false);
/** Copy a field of the currently selected entry of a view to the clipboard. Does nothing if nothing
is selected.
@param[in] column Data column to extract from the model
@param[in] role Data role to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
static void copyEntryData(QAbstractItemView *view, int column, int role=Qt::EditRole);
+ /** Get save file name, mimics QFileDialog::getSaveFileName, except that it appends a default suffix
+ when no suffix is provided by the user.
+
+ @param[in] parent Parent window (or 0)
+ @param[in] caption Window caption (or empty, for default)
+ @param[in] dir Starting directory (or empty, to default to documents directory)
+ @param[in] filter Filter specification such as "Comma Separated Files (*.csv)"
+ @param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0).
+ Can be useful when choosing the save file format based on suffix.
+ */
+ static QString getSaveFileName(QWidget *parent=0, const QString &caption=QString(),
+ const QString &dir=QString(), const QString &filter=QString(),
+ QString *selectedSuffixOut=0);
+
};
#endif // GUIUTIL_H
diff --git a/src/qt/qrcodedialog.cpp b/src/qt/qrcodedialog.cpp
index 754e0bdcb2..82959831de 100644
--- a/src/qt/qrcodedialog.cpp
+++ b/src/qt/qrcodedialog.cpp
@@ -1,110 +1,110 @@
#include "qrcodedialog.h"
#include "ui_qrcodedialog.h"
+#include "guiutil.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)");
+ QString fn = GUIUtil::getSaveFileName(this, tr("Save Image..."), QString(), tr("PNG 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/transactionview.cpp b/src/qt/transactionview.cpp
index 4c357d1379..eaed48bfdf 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -1,419 +1,417 @@
#include "transactionview.h"
#include "transactionfilterproxy.h"
#include "transactionrecord.h"
#include "walletmodel.h"
#include "addresstablemodel.h"
#include "transactiontablemodel.h"
#include "bitcoinunits.h"
#include "csvmodelwriter.h"
#include "transactiondescdialog.h"
#include "editaddressdialog.h"
#include "optionsmodel.h"
#include "guiutil.h"
#include <QScrollBar>
#include <QComboBox>
#include <QDoubleValidator>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QTableView>
#include <QHeaderView>
#include <QPushButton>
-#include <QFileDialog>
#include <QMessageBox>
#include <QPoint>
#include <QMenu>
#include <QApplication>
#include <QClipboard>
#include <QLabel>
#include <QDateTimeEdit>
TransactionView::TransactionView(QWidget *parent) :
QWidget(parent), model(0), transactionProxyModel(0),
transactionView(0)
{
// Build filter row
setContentsMargins(0,0,0,0);
QHBoxLayout *hlayout = new QHBoxLayout();
hlayout->setContentsMargins(0,0,0,0);
#ifdef Q_WS_MAC
hlayout->setSpacing(5);
hlayout->addSpacing(26);
#else
hlayout->setSpacing(0);
hlayout->addSpacing(23);
#endif
dateWidget = new QComboBox(this);
#ifdef Q_WS_MAC
dateWidget->setFixedWidth(121);
#else
dateWidget->setFixedWidth(120);
#endif
dateWidget->addItem(tr("All"), All);
dateWidget->addItem(tr("Today"), Today);
dateWidget->addItem(tr("This week"), ThisWeek);
dateWidget->addItem(tr("This month"), ThisMonth);
dateWidget->addItem(tr("Last month"), LastMonth);
dateWidget->addItem(tr("This year"), ThisYear);
dateWidget->addItem(tr("Range..."), Range);
hlayout->addWidget(dateWidget);
typeWidget = new QComboBox(this);
#ifdef Q_WS_MAC
typeWidget->setFixedWidth(121);
#else
typeWidget->setFixedWidth(120);
#endif
typeWidget->addItem(tr("All"), TransactionFilterProxy::ALL_TYPES);
typeWidget->addItem(tr("Received with"), TransactionFilterProxy::TYPE(TransactionRecord::RecvWithAddress) |
TransactionFilterProxy::TYPE(TransactionRecord::RecvFromOther));
typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) |
TransactionFilterProxy::TYPE(TransactionRecord::SendToOther));
typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf));
typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated));
typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other));
hlayout->addWidget(typeWidget);
addressWidget = new QLineEdit(this);
#if QT_VERSION >= 0x040700
addressWidget->setPlaceholderText(tr("Enter address or label to search"));
#endif
hlayout->addWidget(addressWidget);
amountWidget = new QLineEdit(this);
#if QT_VERSION >= 0x040700
amountWidget->setPlaceholderText(tr("Min amount"));
#endif
#ifdef Q_WS_MAC
amountWidget->setFixedWidth(97);
#else
amountWidget->setFixedWidth(100);
#endif
amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this));
hlayout->addWidget(amountWidget);
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setContentsMargins(0,0,0,0);
vlayout->setSpacing(0);
QTableView *view = new QTableView(this);
vlayout->addLayout(hlayout);
vlayout->addWidget(createDateRangeWidget());
vlayout->addWidget(view);
vlayout->setSpacing(0);
int width = view->verticalScrollBar()->sizeHint().width();
// Cover scroll bar width with spacing
#ifdef Q_WS_MAC
hlayout->addSpacing(width+2);
#else
hlayout->addSpacing(width);
#endif
// Always show scroll bar
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->setTabKeyNavigation(false);
view->setContextMenuPolicy(Qt::CustomContextMenu);
transactionView = view;
// Actions
QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
QAction *editLabelAction = new QAction(tr("Edit label"), this);
QAction *showDetailsAction = new QAction(tr("Show details..."), this);
contextMenu = new QMenu();
contextMenu->addAction(copyAddressAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyAmountAction);
contextMenu->addAction(editLabelAction);
contextMenu->addAction(showDetailsAction);
// Connect actions
connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int)));
connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int)));
connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString)));
connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString)));
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
connect(editLabelAction, SIGNAL(triggered()), this, SLOT(editLabel()));
connect(showDetailsAction, SIGNAL(triggered()), this, SLOT(showDetails()));
}
void TransactionView::setModel(WalletModel *model)
{
this->model = model;
if(model)
{
transactionProxyModel = new TransactionFilterProxy(this);
transactionProxyModel->setSourceModel(model->getTransactionTableModel());
transactionProxyModel->setDynamicSortFilter(true);
transactionProxyModel->setSortRole(Qt::EditRole);
transactionView->setModel(transactionProxyModel);
transactionView->setAlternatingRowColors(true);
transactionView->setSelectionBehavior(QAbstractItemView::SelectRows);
transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
transactionView->setSortingEnabled(true);
transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder);
transactionView->verticalHeader()->hide();
transactionView->horizontalHeader()->resizeSection(
TransactionTableModel::Status, 23);
transactionView->horizontalHeader()->resizeSection(
TransactionTableModel::Date, 120);
transactionView->horizontalHeader()->resizeSection(
TransactionTableModel::Type, 120);
transactionView->horizontalHeader()->setResizeMode(
TransactionTableModel::ToAddress, QHeaderView::Stretch);
transactionView->horizontalHeader()->resizeSection(
TransactionTableModel::Amount, 100);
}
}
void TransactionView::chooseDate(int idx)
{
if(!transactionProxyModel)
return;
QDate current = QDate::currentDate();
dateRangeWidget->setVisible(false);
switch(dateWidget->itemData(idx).toInt())
{
case All:
transactionProxyModel->setDateRange(
TransactionFilterProxy::MIN_DATE,
TransactionFilterProxy::MAX_DATE);
break;
case Today:
transactionProxyModel->setDateRange(
QDateTime(current),
TransactionFilterProxy::MAX_DATE);
break;
case ThisWeek: {
// Find last monday
QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1));
transactionProxyModel->setDateRange(
QDateTime(startOfWeek),
TransactionFilterProxy::MAX_DATE);
} break;
case ThisMonth:
transactionProxyModel->setDateRange(
QDateTime(QDate(current.year(), current.month(), 1)),
TransactionFilterProxy::MAX_DATE);
break;
case LastMonth:
transactionProxyModel->setDateRange(
QDateTime(QDate(current.year(), current.month()-1, 1)),
QDateTime(QDate(current.year(), current.month(), 1)));
break;
case ThisYear:
transactionProxyModel->setDateRange(
QDateTime(QDate(current.year(), 1, 1)),
TransactionFilterProxy::MAX_DATE);
break;
case Range:
dateRangeWidget->setVisible(true);
dateRangeChanged();
break;
}
}
void TransactionView::chooseType(int idx)
{
if(!transactionProxyModel)
return;
transactionProxyModel->setTypeFilter(
typeWidget->itemData(idx).toInt());
}
void TransactionView::changedPrefix(const QString &prefix)
{
if(!transactionProxyModel)
return;
transactionProxyModel->setAddressPrefix(prefix);
}
void TransactionView::changedAmount(const QString &amount)
{
if(!transactionProxyModel)
return;
qint64 amount_parsed = 0;
if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed))
{
transactionProxyModel->setMinAmount(amount_parsed);
}
else
{
transactionProxyModel->setMinAmount(0);
}
}
void TransactionView::exportClicked()
{
// CSV is currently the only supported format
- QString filename = QFileDialog::getSaveFileName(
+ QString filename = GUIUtil::getSaveFileName(
this,
- tr("Export Transaction Data"),
- QDir::currentPath(),
+ tr("Export Transaction Data"), QString(),
tr("Comma separated file (*.csv)"));
if (filename.isNull()) return;
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(transactionProxyModel);
writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole);
writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole);
writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole);
writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole);
writer.addColumn(tr("Address"), 0, TransactionTableModel::AddressRole);
writer.addColumn(tr("Amount"), 0, TransactionTableModel::FormattedAmountRole);
writer.addColumn(tr("ID"), 0, TransactionTableModel::TxIDRole);
if(!writer.write())
{
QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
QMessageBox::Abort, QMessageBox::Abort);
}
}
void TransactionView::contextualMenu(const QPoint &point)
{
QModelIndex index = transactionView->indexAt(point);
if(index.isValid())
{
contextMenu->exec(QCursor::pos());
}
}
void TransactionView::copyAddress()
{
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);
}
void TransactionView::copyLabel()
{
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::LabelRole);
}
void TransactionView::copyAmount()
{
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::FormattedAmountRole);
}
void TransactionView::editLabel()
{
if(!transactionView->selectionModel() ||!model)
return;
QModelIndexList selection = transactionView->selectionModel()->selectedRows();
if(!selection.isEmpty())
{
AddressTableModel *addressBook = model->getAddressTableModel();
if(!addressBook)
return;
QString address = selection.at(0).data(TransactionTableModel::AddressRole).toString();
if(address.isEmpty())
{
// If this transaction has no associated address, exit
return;
}
// Is address in address book? Address book can miss address when a transaction is
// sent from outside the UI.
int idx = addressBook->lookupAddress(address);
if(idx != -1)
{
// Edit sending / receiving address
QModelIndex modelIdx = addressBook->index(idx, 0, QModelIndex());
// Determine type of address, launch appropriate editor dialog type
QString type = modelIdx.data(AddressTableModel::TypeRole).toString();
EditAddressDialog dlg(type==AddressTableModel::Receive
? EditAddressDialog::EditReceivingAddress
: EditAddressDialog::EditSendingAddress,
this);
dlg.setModel(addressBook);
dlg.loadRow(idx);
dlg.exec();
}
else
{
// Add sending address
EditAddressDialog dlg(EditAddressDialog::NewSendingAddress,
this);
dlg.setModel(addressBook);
dlg.setAddress(address);
dlg.exec();
}
}
}
void TransactionView::showDetails()
{
if(!transactionView->selectionModel())
return;
QModelIndexList selection = transactionView->selectionModel()->selectedRows();
if(!selection.isEmpty())
{
TransactionDescDialog dlg(selection.at(0));
dlg.exec();
}
}
QWidget *TransactionView::createDateRangeWidget()
{
dateRangeWidget = new QFrame();
dateRangeWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
dateRangeWidget->setContentsMargins(1,1,1,1);
QHBoxLayout *layout = new QHBoxLayout(dateRangeWidget);
layout->setContentsMargins(0,0,0,0);
layout->addSpacing(23);
layout->addWidget(new QLabel(tr("Range:")));
dateFrom = new QDateTimeEdit(this);
dateFrom->setDisplayFormat("dd/MM/yy");
dateFrom->setCalendarPopup(true);
dateFrom->setMinimumWidth(100);
dateFrom->setDate(QDate::currentDate().addDays(-7));
layout->addWidget(dateFrom);
layout->addWidget(new QLabel(tr("to")));
dateTo = new QDateTimeEdit(this);
dateTo->setDisplayFormat("dd/MM/yy");
dateTo->setCalendarPopup(true);
dateTo->setMinimumWidth(100);
dateTo->setDate(QDate::currentDate());
layout->addWidget(dateTo);
layout->addStretch();
// Hide by default
dateRangeWidget->setVisible(false);
// Notify on change
connect(dateFrom, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
connect(dateTo, SIGNAL(dateChanged(QDate)), this, SLOT(dateRangeChanged()));
return dateRangeWidget;
}
void TransactionView::dateRangeChanged()
{
if(!transactionProxyModel)
return;
transactionProxyModel->setDateRange(
QDateTime(dateFrom->date()),
QDateTime(dateTo->date()).addDays(1));
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 21:41 (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865992
Default Alt Text
(35 KB)

Event Timeline