diff options
Diffstat (limited to 'src/qt/guiutil.cpp')
| -rw-r--r-- | src/qt/guiutil.cpp | 292 |
1 files changed, 220 insertions, 72 deletions
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index d4e73adf9..7e1831f03 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,26 +1,17 @@ +// Copyright (c) 2011-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "guiutil.h" + #include "bitcoinaddressvalidator.h" -#include "walletmodel.h" #include "bitcoinunits.h" -#include "util.h" -#include "init.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> -#include <QThread> +#include "qvalidatedlineedit.h" +#include "walletmodel.h" -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> +#include "core.h" +#include "init.h" +#include "util.h" #ifdef WIN32 #ifdef _WIN32_WINNT @@ -35,9 +26,32 @@ #ifndef NOMINMAX #define NOMINMAX #endif -#include "shlwapi.h" -#include "shlobj.h" #include "shellapi.h" +#include "shlobj.h" +#include "shlwapi.h" +#endif + +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> + +#include <QAbstractItemView> +#include <QApplication> +#include <QClipboard> +#include <QDateTime> +#include <QDesktopServices> +#include <QDesktopWidget> +#include <QDoubleValidator> +#include <QFileDialog> +#include <QFont> +#include <QLineEdit> +#include <QSettings> +#include <QTextDocument> // for Qt::mightBeRichText +#include <QThread> + +#if QT_VERSION < 0x050000 +#include <QUrl> +#else +#include <QUrlQuery> #endif namespace GUIUtil { @@ -59,11 +73,16 @@ QFont bitcoinAddressFont() return font; } -void setupAddressWidget(QLineEdit *widget, QWidget *parent) +void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent) { - widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength); - widget->setValidator(new BitcoinAddressValidator(parent)); + parent->setFocusProxy(widget); + widget->setFont(bitcoinAddressFont()); +#if QT_VERSION >= 0x040700 + widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); +#endif + widget->setValidator(new BitcoinAddressEntryValidator(parent)); + widget->setCheckValidator(new BitcoinAddressCheckValidator(parent)); } void setupAmountWidget(QLineEdit *widget, QWidget *parent) @@ -77,14 +96,20 @@ void setupAmountWidget(QLineEdit *widget, QWidget *parent) bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { - // return if URI is not valid or is no bitcoin URI + // return if URI is not valid or is no bitcoin: URI if(!uri.isValid() || uri.scheme() != QString("bitcoin")) return false; SendCoinsRecipient rv; rv.address = uri.path(); rv.amount = 0; + +#if QT_VERSION < 0x050000 QList<QPair<QString, QString> > items = uri.queryItems(); +#else + QUrlQuery uriQuery(uri); + QList<QPair<QString, QString> > items = uriQuery.queryItems(); +#endif for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++) { bool fShouldReturnFalse = false; @@ -99,6 +124,11 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) rv.label = i->second; fShouldReturnFalse = false; } + if (i->first == "message") + { + rv.message = i->second; + fShouldReturnFalse = false; + } else if (i->first == "amount") { if(!i->second.isEmpty()) @@ -127,7 +157,7 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) // // Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host, // which will lower-case it (and thus invalidate the address). - if(uri.startsWith("bitcoin://")) + if(uri.startsWith("bitcoin://", Qt::CaseInsensitive)) { uri.replace(0, 10, "bitcoin:"); } @@ -135,9 +165,49 @@ bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) return parseBitcoinURI(uriInstance, out); } +QString formatBitcoinURI(const SendCoinsRecipient &info) +{ + QString ret = QString("bitcoin:%1").arg(info.address); + int paramCount = 0; + + if (info.amount) + { + ret += QString("?amount=%1").arg(BitcoinUnits::format(BitcoinUnits::BTC, info.amount)); + paramCount++; + } + + if (!info.label.isEmpty()) + { + QString lbl(QUrl::toPercentEncoding(info.label)); + ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl); + paramCount++; + } + + if (!info.message.isEmpty()) + { + QString msg(QUrl::toPercentEncoding(info.message));; + ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg); + paramCount++; + } + + return ret; +} + +bool isDust(const QString& address, qint64 amount) +{ + CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); + CScript script; script.SetDestination(dest); + CTxOut txOut(amount, script); + return txOut.IsDust(CTransaction::nMinRelayTxFee); +} + QString HtmlEscape(const QString& str, bool fMultiLine) { +#if QT_VERSION < 0x050000 QString escaped = Qt::escape(str); +#else + QString escaped = str.toHtmlEscaped(); +#endif if(fMultiLine) { escaped = escaped.replace("\n", "<br>\n"); @@ -159,26 +229,30 @@ void copyEntryData(QAbstractItemView *view, int column, int role) if(!selection.isEmpty()) { // Copy first item - QApplication::clipboard()->setText(selection.at(0).data(role).toString()); + setClipboard(selection.at(0).data(role).toString()); } } -QString getSaveFileName(QWidget *parent, const QString &caption, - const QString &dir, - const QString &filter, - QString *selectedSuffixOut) +QString 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 { +#if QT_VERSION < 0x050000 myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif } else { myDir = dir; } - QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter); + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter)); /* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */ QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]"); @@ -209,9 +283,44 @@ QString getSaveFileName(QWidget *parent, const QString &caption, return result; } +QString getOpenFileName(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 + { +#if QT_VERSION < 0x050000 + myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); +#else + myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); +#endif + } + else + { + myDir = dir; + } + /* Directly convert path to native OS path separators */ + QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter)); + + if(selectedSuffixOut) + { + /* 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); + } + *selectedSuffixOut = selectedSuffix; + } + return result; +} + Qt::ConnectionType blockingGUIThreadConnection() { - if(QThread::currentThread() != QCoreApplication::instance()->thread()) + if(QThread::currentThread() != qApp->thread()) { return Qt::BlockingQueuedConnection; } @@ -223,7 +332,7 @@ Qt::ConnectionType blockingGUIThreadConnection() bool checkPoint(const QPoint &p, const QWidget *w) { - QWidget *atW = qApp->widgetAt(w->mapToGlobal(p)); + QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p)); if (!atW) return false; return atW->topLevelWidget() == w; } @@ -258,11 +367,11 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) { QWidget *widget = static_cast<QWidget*>(obj); QString tooltip = widget->toolTip(); - if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt/>") && !Qt::mightBeRichText(tooltip)) + if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt") && !Qt::mightBeRichText(tooltip)) { - // Prefix <qt/> to make sure Qt detects this as rich text + // Envelop with <qt></qt> to make sure Qt detects this as rich text // Escape the current message as HTML and replace \n by <br> - tooltip = "<qt/>" + HtmlEscape(tooltip, true); + tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>"; widget->setToolTip(tooltip); return true; } @@ -404,55 +513,94 @@ bool SetStartOnSystemStartup(bool fAutoStart) } return true; } -#else -// TODO: OSX startup stuff; see: -// https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html -bool GetStartOnSystemStartup() { return false; } -bool SetStartOnSystemStartup(bool fAutoStart) { return false; } +#elif defined(Q_OS_MAC) +// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m -#endif +#include <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> -HelpMessageBox::HelpMessageBox(QWidget *parent) : - QMessageBox(parent) +LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl); +LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) { - header = tr("Bitcoin-Qt") + " " + tr("version") + " " + - QString::fromStdString(FormatFullVersion()) + "\n\n" + - tr("Usage:") + "\n" + - " bitcoin-qt [" + tr("command-line options") + "] " + "\n"; + // loop through the list of startup items and try to find the bitcoin app + CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, NULL); + for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { + LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); + UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; + CFURLRef currentItemURL = NULL; + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, NULL); + if(currentItemURL && CFEqual(currentItemURL, findUrl)) { + // found + CFRelease(currentItemURL); + return item; + } + if(currentItemURL) { + CFRelease(currentItemURL); + } + } + return NULL; +} - coreOptions = QString::fromStdString(HelpMessage()); +bool GetStartOnSystemStartup() +{ + CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + return !!foundItem; // return boolified object +} - uiOptions = tr("UI options") + ":\n" + - " -lang=<lang> " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + - " -min " + tr("Start minimized") + "\n" + - " -splash " + tr("Show splash screen on startup (default: 1)") + "\n"; +bool SetStartOnSystemStartup(bool fAutoStart) +{ + CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL); + LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); - setWindowTitle(tr("Bitcoin-Qt")); - setTextFormat(Qt::PlainText); - // setMinimumWidth is ignored for QMessageBox so put in non-breaking spaces to make it wider. - setText(header + QString(QChar(0x2003)).repeated(50)); - setDetailedText(coreOptions + "\n" + uiOptions); + if(fAutoStart && !foundItem) { + // add bitcoin app to startup item list + LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, bitcoinAppUrl, NULL, NULL); + } + else if(!fAutoStart && foundItem) { + // remove item + LSSharedFileListItemRemove(loginItems, foundItem); + } + return true; } +#else + +bool GetStartOnSystemStartup() { return false; } +bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + +#endif -void HelpMessageBox::printToConsole() +void saveWindowGeometry(const QString& strSetting, QWidget *parent) { - // On other operating systems, the expected action is to print the message to the console. - QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions; - fprintf(stdout, "%s", strUsage.toStdString().c_str()); + QSettings settings; + settings.setValue(strSetting + "Pos", parent->pos()); + settings.setValue(strSetting + "Size", parent->size()); } -void HelpMessageBox::showOrPrint() +void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent) { -#if defined(WIN32) - // On Windows, show a message box, as there is no stderr/stdout in windowed applications - exec(); -#else - // On other operating systems, print help text to console - printToConsole(); -#endif + QSettings settings; + QPoint pos = settings.value(strSetting + "Pos").toPoint(); + QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); + + if (!pos.x() && !pos.y()) { + QRect screen = QApplication::desktop()->screenGeometry(); + pos.setX((screen.width() - size.width()) / 2); + pos.setY((screen.height() - size.height()) / 2); + } + + parent->resize(size); + parent->move(pos); } -} // namespace GUIUtil +void setClipboard(const QString& str) +{ + QApplication::clipboard()->setText(str, QClipboard::Clipboard); + QApplication::clipboard()->setText(str, QClipboard::Selection); +} +} // namespace GUIUtil |