diff options
Diffstat (limited to 'src/qt')
32 files changed, 278 insertions, 301 deletions
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 2fa032abdc..0eb7ec4306 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -82,14 +82,14 @@ public: LOCK(wallet->cs_wallet); for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; - bool fMine = IsMine(*wallet, address.Get()); + const CTxDestination& address = item.first; + bool fMine = IsMine(*wallet, address); AddressTableEntry::Type addressType = translateTransactionType( QString::fromStdString(item.second.purpose), fMine); const std::string& strName = item.second.name; cachedAddressTable.append(AddressTableEntry(addressType, QString::fromStdString(strName), - QString::fromStdString(address.ToString()))); + QString::fromStdString(EncodeDestination(address)))); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order @@ -246,7 +246,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, if(role == Qt::EditRole) { LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */ - CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get(); + CTxDestination curAddress = DecodeDestination(rec->address.toStdString()); if(index.column() == Label) { // Do nothing, if old label == new label @@ -257,7 +257,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose); } else if(index.column() == Address) { - CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get(); + CTxDestination newAddress = DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false if(boost::get<CNoDestination>(&newAddress)) { @@ -358,7 +358,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Check for duplicate addresses { LOCK(wallet->cs_wallet); - if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) + if(wallet->mapAddressBook.count(DecodeDestination(strAddress))) { editStatus = DUPLICATE_ADDRESS; return QString(); @@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } } - strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + strAddress = EncodeDestination(newKey.GetID()); } else { @@ -394,7 +394,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Add entry { LOCK(wallet->cs_wallet); - wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, + wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, (type == Send ? "send" : "receive")); } return QString::fromStdString(strAddress); @@ -412,7 +412,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent } { LOCK(wallet->cs_wallet); - wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get()); + wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); } return true; } @@ -423,8 +423,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const { { LOCK(wallet->cs_wallet); - CBitcoinAddress address_parsed(address.toStdString()); - std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); + CTxDestination destination = DecodeDestination(address.toStdString()); + std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination); if (mi != wallet->mapAddressBook.end()) { return QString::fromStdString(mi->second.name); diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index d712705c43..362a71f04d 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po if (((ch >= '0' && ch<='9') || (ch >= 'a' && ch<='z') || (ch >= 'A' && ch<='Z')) && - ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32 { // Alphanumeric and not a 'forbidden' character } @@ -89,9 +89,9 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po { Q_UNUSED(pos); // Validate the passed Bitcoin address - CBitcoinAddress addr(input.toStdString()); - if (addr.IsValid()) + if (IsValidDestinationString(input.toStdString())) { return QValidator::Acceptable; + } return QValidator::Invalid; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index be2d21daee..dc55141900 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -123,7 +123,11 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); + QSettings settings; + if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } QString windowTitle = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET @@ -261,7 +265,8 @@ BitcoinGUI::~BitcoinGUI() // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); - GUIUtil::saveWindowGeometry("nWindow", this); + QSettings settings; + settings.setValue("MainWindowGeometry", saveGeometry()); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); #ifdef Q_OS_MAC diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 562c361799..6952eb5064 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -582,6 +582,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); // how many satoshis the estimated fee can vary per byte we guess wrong + assert(nBytes != 0); double dFeeVary = (double)nPayFee / nBytes; QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary); @@ -660,7 +661,7 @@ void CoinControlDialog::updateView() QString sAddress = ""; if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) { - sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString()); + sAddress = QString::fromStdString(EncodeDestination(outputAddress)); // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs if (!treeMode || (!(sAddress == sWalletAddress))) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 14078b9ee8..e31bfee05e 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -199,10 +199,10 @@ <item> <widget class="QCheckBox" name="allowIncoming"> <property name="toolTip"> - <string>Accept connections from outside</string> + <string>Accept connections from outside.</string> </property> <property name="text"> - <string>Allow incoming connections</string> + <string>Allow incomin&g connections</string> </property> </widget> </item> @@ -399,7 +399,7 @@ <string>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</string> </property> <property name="text"> - <string>Use separate SOCKS5 proxy to reach peers via Tor hidden services:</string> + <string>Use separate SOCKS&5 proxy to reach peers via Tor hidden services:</string> </property> </widget> </item> @@ -507,10 +507,10 @@ <item> <widget class="QCheckBox" name="hideTrayIcon"> <property name="toolTip"> - <string>&Hide the icon from the system tray.</string> + <string>Hide the icon from the system tray.</string> </property> <property name="text"> - <string>Hide tray icon</string> + <string>&Hide tray icon</string> </property> </widget> </item> @@ -610,7 +610,7 @@ <string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string> </property> <property name="text"> - <string>Third party transaction URLs</string> + <string>&Third party transaction URLs</string> </property> <property name="buddy"> <cstring>thirdPartyTxUrls</cstring> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 1e2f2302b9..a0e48334c1 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -846,19 +846,13 @@ <item> <layout class="QHBoxLayout" name="horizontalLayoutFee13"> <item> - <widget class="QRadioButton" name="radioCustomPerKilobyte"> + <widget class="QLabel" name="labelCustomPerKilobyte"> <property name="toolTip"> <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string> </property> <property name="text"> <string>per kilobyte</string> </property> - <property name="checked"> - <bool>true</bool> - </property> - <attribute name="buttonGroup"> - <string notr="true">groupCustomFee</string> - </attribute> </widget> </item> <item> @@ -1285,6 +1279,5 @@ <connections/> <buttongroups> <buttongroup name="groupFee"/> - <buttongroup name="groupCustomFee"/> </buttongroups> </ui> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebec..d520d7d4be 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -112,8 +112,9 @@ static std::string DummyAddress(const CChainParams ¶ms) sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata)); for(int i=0; i<256; ++i) { // Try every trailing byte std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()); - if (!CBitcoinAddress(s).IsValid()) + if (!IsValidDestinationString(s)) { return s; + } sourcedata[sourcedata.size()-1] += 1; } return ""; @@ -248,7 +249,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) bool isDust(const QString& address, const CAmount& amount) { - CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); + CTxDestination dest = DecodeDestination(address.toStdString()); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); return IsDust(txOut, ::dustRelayFee); @@ -743,9 +744,10 @@ bool SetStartOnSystemStartup(bool fAutoStart) else { char pszExePath[MAX_PATH+1]; - memset(pszExePath, 0, sizeof(pszExePath)); - if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1); + if (r == -1) return false; + pszExePath[r] = '\0'; fs::create_directories(GetAutostartDir()); @@ -780,47 +782,64 @@ bool SetStartOnSystemStartup(bool fAutoStart) LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl); LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) { - // loop through the list of startup items and try to find the bitcoin app CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, nullptr); + if (listSnapshot == nullptr) { + return nullptr; + } + + // loop through the list of startup items and try to find the bitcoin app for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; CFURLRef currentItemURL = nullptr; #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100 - if(&LSSharedFileListItemCopyResolvedURL) - currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); + if(&LSSharedFileListItemCopyResolvedURL) + currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100 - else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); + else + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif #else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif - if(currentItemURL && CFEqual(currentItemURL, findUrl)) { - // found - CFRelease(currentItemURL); - return item; - } if(currentItemURL) { + if (CFEqual(currentItemURL, findUrl)) { + // found + CFRelease(listSnapshot); + CFRelease(currentItemURL); + return item; + } CFRelease(currentItemURL); } } + + CFRelease(listSnapshot); return nullptr; } bool GetStartOnSystemStartup() { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + if (bitcoinAppUrl == nullptr) { + return false; + } + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + + CFRelease(bitcoinAppUrl); return !!foundItem; // return boolified object } bool SetStartOnSystemStartup(bool fAutoStart) { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + if (bitcoinAppUrl == nullptr) { + return false; + } + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); @@ -832,6 +851,8 @@ bool SetStartOnSystemStartup(bool fAutoStart) // remove item LSSharedFileListItemRemove(loginItems, foundItem); } + + CFRelease(bitcoinAppUrl); return true; } #pragma GCC diagnostic pop @@ -842,32 +863,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; } #endif -void saveWindowGeometry(const QString& strSetting, QWidget *parent) -{ - QSettings settings; - settings.setValue(strSetting + "Pos", parent->pos()); - settings.setValue(strSetting + "Size", parent->size()); -} - -void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent) -{ - QSettings settings; - QPoint pos = settings.value(strSetting + "Pos").toPoint(); - QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); - - parent->resize(size); - parent->move(pos); - - if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1)) - { - QRect screen = QApplication::desktop()->screenGeometry(); - QPoint defaultPos((screen.width() - defaultSize.width()) / 2, - (screen.height() - defaultSize.height()) / 2); - parent->resize(defaultSize); - parent->move(defaultPos); - } -} - void setClipboard(const QString& str) { QApplication::clipboard()->setText(str, QClipboard::Clipboard); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d6aa8c4ea6..d10818d0c8 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -179,11 +179,6 @@ namespace GUIUtil bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); - /** Save window size and position */ - void saveWindowGeometry(const QString& strSetting, QWidget *parent); - /** Restore window size and position */ - void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent); - /* Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h index d4749b3d5f..3a005c3c46 100644 --- a/src/qt/macnotificationhandler.h +++ b/src/qt/macnotificationhandler.h @@ -7,20 +7,17 @@ #include <QObject> -/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). +/** Macintosh-specific notification handler (supports UserNotificationCenter). */ class MacNotificationHandler : public QObject { Q_OBJECT public: - /** shows a 10.8+ UserNotification in the UserNotificationCenter + /** shows a macOS 10.8+ UserNotification in the UserNotificationCenter */ void showNotification(const QString &title, const QString &text); - /** executes AppleScript */ - void sendAppleScript(const QString &script); - /** check if OS can handle UserNotifications */ bool hasUserNotificationCenterSupport(void); static MacNotificationHandler *instance(); diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 4c96d08c8a..1b16c5f524 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -47,20 +47,6 @@ void MacNotificationHandler::showNotification(const QString &title, const QStrin } } -// sendAppleScript just take a QString and executes it as apple script -void MacNotificationHandler::sendAppleScript(const QString &script) -{ - QByteArray utf8 = script.toUtf8(); - char* cString = (char *)utf8.constData(); - NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; - - NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; - NSDictionary *err = nil; - [as executeAndReturnError:&err]; - [as release]; - [scriptApple release]; -} - bool MacNotificationHandler::hasUserNotificationCenterSupport(void) { Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index a83f285034..e32a0bdda8 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -82,36 +82,38 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress)); // show progress speed if we have more then one sample - if (blockProcessTime.size() >= 2) - { - double progressStart = blockProcessTime[0].second; + if (blockProcessTime.size() >= 2) { double progressDelta = 0; double progressPerHour = 0; qint64 timeDelta = 0; qint64 remainingMSecs = 0; double remainingProgress = 1.0 - nVerificationProgress; - for (int i = 1; i < blockProcessTime.size(); i++) - { + for (int i = 1; i < blockProcessTime.size(); i++) { QPair<qint64, double> sample = blockProcessTime[i]; // take first sample after 500 seconds or last available one if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) { - progressDelta = progressStart-sample.second; + progressDelta = blockProcessTime[0].second - sample.second; timeDelta = blockProcessTime[0].first - sample.first; - progressPerHour = progressDelta/(double)timeDelta*1000*3600; - remainingMSecs = remainingProgress / progressDelta * timeDelta; + progressPerHour = progressDelta / (double) timeDelta * 1000 * 3600; + remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; break; } } // show progress increase per hour - ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); + ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%"); // show expected remaining time - ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0)); + if(remainingMSecs >= 0) { + ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); + } else { + ui->expectedTimeLeft->setText(QObject::tr("unknown")); + } static const int MAX_SAMPLES = 5000; - if (blockProcessTime.count() > MAX_SAMPLES) - blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES); + if (blockProcessTime.count() > MAX_SAMPLES) { + blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES); + } } // show the last block date diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index a7a7a4ce11..937928315b 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -60,22 +60,6 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { mode = UserNotificationCenter; } - else { - // Check if Growl is installed (based on Qt's tray icon implementation) - CFURLRef cfurl; - OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); - if (status != kLSApplicationNotFoundErr) { - CFBundleRef bundle = CFBundleCreate(0, cfurl); - if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { - if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) - mode = Growl13; - else - mode = Growl12; - } - CFRelease(cfurl); - CFRelease(bundle); - } - } #endif } @@ -241,52 +225,6 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString & // Based on Qt's tray icon implementation #ifdef Q_OS_MAC -void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon) -{ - const QString script( - "tell application \"%5\"\n" - " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all) - " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled) - " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl - " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification - "end tell" - ); - - QString notificationApp(QApplication::applicationName()); - if (notificationApp.isEmpty()) - notificationApp = "Application"; - - QPixmap notificationIconPixmap; - if (icon.isNull()) { // If no icon specified, set icon based on class - QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; - switch (cls) - { - case Information: sicon = QStyle::SP_MessageBoxInformation; break; - case Warning: sicon = QStyle::SP_MessageBoxWarning; break; - case Critical: sicon = QStyle::SP_MessageBoxCritical; break; - } - notificationIconPixmap = QApplication::style()->standardPixmap(sicon); - } - else { - QSize size = icon.actualSize(QSize(48, 48)); - notificationIconPixmap = icon.pixmap(size); - } - - QString notificationIcon; - QTemporaryFile notificationIconFile; - if (!notificationIconPixmap.isNull() && notificationIconFile.open()) { - QImageWriter writer(¬ificationIconFile, "PNG"); - if (writer.write(notificationIconPixmap.toImage())) - notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName()); - } - - QString quotedTitle(title), quotedText(text); - quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); - quotedText.replace("\\", "\\\\").replace("\"", "\\"); - QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); - MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp)); -} - void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) { // icon is not supported by the user notification center yet. OSX will use the app icon. MacNotificationHandler::instance()->showNotification(title, text); @@ -310,10 +248,6 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c case UserNotificationCenter: notifyMacUserNotificationCenter(cls, title, text, icon); break; - case Growl12: - case Growl13: - notifyGrowl(cls, title, text, icon); - break; #endif default: if(cls == Critical) diff --git a/src/qt/notificator.h b/src/qt/notificator.h index f92b791d4a..67f2b1df69 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -58,8 +58,6 @@ private: None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ Freedesktop, /**< Use DBus org.freedesktop.Notifications */ QSystemTray, /**< Use QSystemTray::showMessage */ - Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ - Growl13, /**< Use the Growl 1.3 notification system (Mac only) */ UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */ }; QString programName; @@ -72,7 +70,6 @@ private: #endif void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); #ifdef Q_OS_MAC - void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon); void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon); #endif }; diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index e9960a01b1..fb8c60d100 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -124,7 +124,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseProxy")) settings.setValue("fUseProxy", false); - if (!settings.contains("addrProxy")) + if (!settings.contains("addrProxy") || !settings.value("addrProxy").toString().contains(':')) settings.setValue("addrProxy", "127.0.0.1:9050"); // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) @@ -134,7 +134,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseSeparateProxyTor")) settings.setValue("fUseSeparateProxyTor", false); - if (!settings.contains("addrSeparateProxyTor")) + if (!settings.contains("addrSeparateProxyTor") || !settings.value("addrSeparateProxyTor").toString().contains(':')) settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); // Only try to set -onion, if user has enabled fUseSeparateProxyTor if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) @@ -151,10 +151,32 @@ void OptionsModel::Init(bool resetSettings) language = settings.value("language").toString(); } +/** Helper function to copy contents from one QSettings to another. + * By using allKeys this also covers nested settings in a hierarchy. + */ +static void CopySettings(QSettings& dst, const QSettings& src) +{ + for (const QString& key : src.allKeys()) { + dst.setValue(key, src.value(key)); + } +} + +/** Back up a QSettings to an ini-formatted file. */ +static void BackupSettings(const fs::path& filename, const QSettings& src) +{ + qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename); + QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat); + dst.clear(); + CopySettings(dst, src); +} + void OptionsModel::Reset() { QSettings settings; + // Backup old settings to chain-specific datadir for troubleshooting + BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); + // Save the strDataDir setting QString dataDir = Intro::getDefaultDataDirectory(); dataDir = settings.value("strDataDir", dataDir).toString(); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index d4137d280f..169684cf6d 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -218,17 +218,15 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) SendCoinsRecipient r; if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) { - CBitcoinAddress address(r.address.toStdString()); auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN); - if (address.IsValid(*tempChainParams)) - { + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { SelectParams(CBaseChainParams::MAIN); - } - else { + } else { tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); - if (address.IsValid(*tempChainParams)) + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { SelectParams(CBaseChainParams::TESTNET); + } } } } @@ -441,8 +439,7 @@ void PaymentServer::handleURIOrFile(const QString& s) SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { - CBitcoinAddress address(recipient.address.toStdString()); - if (!address.IsValid()) { + if (!IsValidDestinationString(recipient.address.toStdString())) { Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), CClientUIInterface::MSG_ERROR); } @@ -560,7 +557,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { // Append destination address - addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); + addresses.append(QString::fromStdString(EncodeDestination(dest))); } else if (!recipient.authenticatedMerchant.isEmpty()) { // Unauthenticated payment requests to custom bitcoin addresses are not supported diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3590a98efa..d895fc1663 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -28,6 +28,7 @@ #include <wallet/wallet.h> #endif +#include <QDesktopWidget> #include <QKeyEvent> #include <QMenu> #include <QMessageBox> @@ -428,7 +429,11 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : consoleFontSize(0) { ui->setupUi(this); - GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); + QSettings settings; + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME))); @@ -466,14 +471,14 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); - QSettings settings; consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt(); clear(); } RPCConsole::~RPCConsole() { - GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); + QSettings settings; + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 625e435742..6309070fef 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -114,10 +114,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p settings.setValue("nFeeRadio", 1); // custom if (!settings.contains("nFeeRadio")) settings.setValue("nFeeRadio", 0); // recommended - if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility - settings.setValue("nCustomFeeRadio", 1); // total at least - if (!settings.contains("nCustomFeeRadio")) - settings.setValue("nCustomFeeRadio", 0); // per kilobyte if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) @@ -127,8 +123,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupFee->setId(ui->radioSmartFee, 0); ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true); - ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); - ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); @@ -178,7 +172,6 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); @@ -214,7 +207,6 @@ SendCoinsDialog::~SendCoinsDialog() QSettings settings; settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); - settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex())); settings.setValue("nTransactionFee", (qint64)ui->customFee->value()); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); @@ -609,7 +601,6 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::setMinimumFee() { - ui->radioCustomPerKilobyte->setChecked(true); ui->customFee->setValue(GetRequiredFee(1000)); } @@ -622,7 +613,7 @@ void SendCoinsDialog::updateFeeSectionControls() ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); - ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); + ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } @@ -634,8 +625,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel() if (ui->radioSmartFee->isChecked()) ui->labelFeeMinimized->setText(ui->labelSmartFee->text()); else { - ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + - ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : "")); + ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB"); } } @@ -777,22 +767,19 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) CoinControlDialog::coinControl->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); - CBitcoinAddress addr = CBitcoinAddress(text.toStdString()); + const CTxDestination dest = DecodeDestination(text.toStdString()); if (text.isEmpty()) // Nothing entered { ui->labelCoinControlChangeLabel->setText(""); } - else if (!addr.IsValid()) // Invalid address + else if (!IsValidDestination(dest)) // Invalid address { ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); } else // Valid address { - CKeyID keyid; - addr.GetKeyID(keyid); - if (!model->havePrivKey(keyid)) // Unknown change address - { + if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); // confirmation dialog @@ -800,7 +787,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(btnRetVal == QMessageBox::Yes) - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); @@ -819,7 +806,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; } } } diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 0950ed0234..cba9d4da38 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -117,16 +117,14 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() /* Clear old signature to ensure users don't get confused on error with an old signature displayed */ ui->signatureOut_SM->clear(); - CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); - if (!addr.IsValid()) - { + CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - { + const CKeyID* keyID = boost::get<CKeyID>(&destination); + if (!keyID) { ui->addressIn_SM->setValid(false); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); @@ -142,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() } CKey key; - if (!model->getPrivKey(keyID, key)) + if (!model->getPrivKey(*keyID, key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); @@ -164,7 +162,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }"); ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") + QString("</nobr>")); - ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); + ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(vchSig.data(), vchSig.size()))); } void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked() @@ -197,16 +195,13 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() { - CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); - if (!addr.IsValid()) - { + CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - { + if (!boost::get<CKeyID>(&destination)) { ui->addressIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); @@ -237,8 +232,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() return; } - if (!(CBitcoinAddress(pubkey.GetID()) == addr)) - { + if (!(CTxDestination(pubkey.GetID()) == destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")); return; diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 1b7cc69231..a1fbba963c 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -142,8 +142,8 @@ SplashScreen::~SplashScreen() bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) { if (ev->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev); - if(keyEvent->text()[0] == 'q' && breakAction != nullptr) { - breakAction(); + if(keyEvent->text()[0] == 'q') { + StartShutdown(); } } return QObject::eventFilter(obj, ev); @@ -170,27 +170,18 @@ static void InitMessage(SplashScreen *splash, const std::string &message) Q_ARG(QColor, QColor(55,55,55))); } -static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress) +static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible) { - InitMessage(splash, title + strprintf("%d", nProgress) + "%"); -} - -void SplashScreen::setBreakAction(const std::function<void(void)> &action) -{ - breakAction = action; -} - -static void SetProgressBreakAction(SplashScreen *splash, const std::function<void(void)> &action) -{ - QMetaObject::invokeMethod(splash, "setBreakAction", - Qt::QueuedConnection, - Q_ARG(std::function<void(void)>, action)); + InitMessage(splash, title + std::string("\n") + + (resume_possible ? _("(press q to shutdown and continue later)") + : _("press q to shutdown")) + + strprintf("\n%d", nProgress) + "%"); } #ifdef ENABLE_WALLET void SplashScreen::ConnectWallet(CWallet* wallet) { - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, false)); connectedWallets.push_back(wallet); } #endif @@ -199,8 +190,7 @@ void SplashScreen::subscribeToCoreSignals() { // Connect signals to client uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1)); + uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1)); #endif @@ -210,10 +200,10 @@ void SplashScreen::unsubscribeFromCoreSignals() { // Disconnect signals from client uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET for (CWallet* const & pwallet : connectedWallets) { - pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, false)); } #endif } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index a88ebb98a8..c6cfd503f7 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -36,8 +36,6 @@ public Q_SLOTS: /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); - /** Sets the break action */ - void setBreakAction(const std::function<void(void)> &action); protected: bool eventFilter(QObject * obj, QEvent * ev); @@ -55,8 +53,6 @@ private: int curAlignment; QList<CWallet*> connectedWallets; - - std::function<void(void)> breakAction; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index c7830071ed..273bd10487 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -24,7 +24,7 @@ X509 *parse_b64der_cert(const char* cert_data) { std::vector<unsigned char> data = DecodeBase64(cert_data); assert(data.size() > 0); - const unsigned char* dptr = &data[0]; + const unsigned char* dptr = data.data(); X509 *cert = d2i_X509(nullptr, &dptr, data.size()); assert(cert); return cert; @@ -43,7 +43,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig // Write data to a temp file: QTemporaryFile f; f.open(); - f.write((const char*)&data[0], data.size()); + f.write((const char*)data.data(), data.size()); f.close(); // Create a QObject, install event filter from PaymentServer @@ -139,7 +139,7 @@ void PaymentServerTests::paymentServerTests() // Contains a testnet paytoaddress, so payment request network doesn't match client network: data = DecodeBase64(paymentrequest1_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized, because network "main" is default, even for // uninitialized payment requests and that will fail our test here. @@ -148,7 +148,7 @@ void PaymentServerTests::paymentServerTests() // Expired payment request (expires is set to 1 = 1970-01-01 00:00:01): data = DecodeBase64(paymentrequest2_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); @@ -159,7 +159,7 @@ void PaymentServerTests::paymentServerTests() // 9223372036854775807 (uint64), 9223372036854775807 (int64_t) and -1 (int32_t) // -1 is 1969-12-31 23:59:59 (for a 32 bit time values) data = DecodeBase64(paymentrequest3_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); @@ -170,7 +170,7 @@ void PaymentServerTests::paymentServerTests() // 9223372036854775808 (uint64), -9223372036854775808 (int64_t) and 0 (int32_t) // 0 is 1970-01-01 00:00:00 (for a 32 bit time values) data = DecodeBase64(paymentrequest4_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); @@ -190,7 +190,7 @@ void PaymentServerTests::paymentServerTests() // Payment request with amount overflow (amount is set to 21000001 BTC): data = DecodeBase64(paymentrequest5_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 00edd64f72..70fdd4bf58 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -36,11 +36,6 @@ void RPCNestedTests::rpcNestedTests() // do some test setup // could be moved to a more generic place when we add more tests on QT level tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); - ClearDatadirCache(); - std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); - QDir dir(QString::fromStdString(path)); - dir.mkpath("."); - gArgs.ForceSetArg("-datadir", path); //mempool.setSanityCheck(1.0); TestingSetup test; @@ -135,6 +130,4 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , #endif - - fs::remove_all(fs::path(path)); } diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 80a00a634a..4c04e67ccc 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -53,6 +53,10 @@ int main(int argc, char *argv[]) SetupNetworking(); SelectParams(CBaseChainParams::MAIN); noui_connect(); + ClearDatadirCache(); + fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000)); + fs::create_directories(pathTemp); + gArgs.ForceSetArg("-datadir", pathTemp.string()); bool fInvalid = false; @@ -97,5 +101,7 @@ int main(int argc, char *argv[]) } #endif + fs::remove_all(pathTemp); + return fInvalid; } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ebd9102f84..12755d43e4 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -13,6 +13,10 @@ #include "test/test_bitcoin.h" #include "validation.h" #include "wallet/wallet.h" +#include "qt/overviewpage.h" +#include "qt/receivecoinsdialog.h" +#include "qt/recentrequeststablemodel.h" +#include "qt/receiverequestdialog.h" #include <QAbstractButton> #include <QAction> @@ -21,6 +25,9 @@ #include <QPushButton> #include <QTimer> #include <QVBoxLayout> +#include <QTextEdit> +#include <QListView> +#include <QDialogButtonBox> namespace { @@ -57,11 +64,11 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false) } //! Send coins to address and return txid. -uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf) +uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf) { QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries"); SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget()); - entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString())); + entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address))); entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount); sendCoinsDialog.findChild<QFrame*>("frameFee") ->findChild<QFrame*>("frameFeeSelection") @@ -140,7 +147,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st // src/qt/test/test_bitcoin-qt -platform xcb # Linux // src/qt/test/test_bitcoin-qt -platform windows # Windows // src/qt/test/test_bitcoin-qt -platform cocoa # macOS -void TestSendCoins() +void TestGUI() { // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; @@ -172,8 +179,8 @@ void TestSendCoins() // Send two transactions, and verify they are added to transaction list. TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); - uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */); - uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */); + uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */); + uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); @@ -184,6 +191,68 @@ void TestSendCoins() BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */); BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */); + // Check current balance on OverviewPage + OverviewPage overviewPage(platformStyle.get()); + overviewPage.setWalletModel(&walletModel); + QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance"); + QString balanceText = balanceLabel->text(); + int unit = walletModel.getOptionsModel()->getDisplayUnit(); + CAmount balance = walletModel.getBalance(); + QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); + QCOMPARE(balanceText, balanceComparison); + + // Check Request Payment button + ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get()); + receiveCoinsDialog.setModel(&walletModel); + RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel(); + + // Label input + QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel"); + labelInput->setText("TEST_LABEL_1"); + + // Amount input + BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount"); + amountInput->setValue(1); + + // Message input + QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage"); + messageInput->setText("TEST_MESSAGE_1"); + int initialRowCount = requestTableModel->rowCount({}); + QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton"); + requestPaymentButton->click(); + for (QWidget* widget : QApplication::topLevelWidgets()) { + if (widget->inherits("ReceiveRequestDialog")) { + ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget); + QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri"); + QString paymentText = rlist->toPlainText(); + QStringList paymentTextList = paymentText.split('\n'); + QCOMPARE(paymentTextList.at(0), QString("Payment information")); + QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1); + QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); + QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); + QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); + QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); + } + } + + // Clear button + QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton"); + clearButton->click(); + QCOMPARE(labelInput->text(), QString("")); + QCOMPARE(amountInput->value(), CAmount(0)); + QCOMPARE(messageInput->text(), QString("")); + + // Check addition to history + int currentRowCount = requestTableModel->rowCount({}); + QCOMPARE(currentRowCount, initialRowCount+1); + + // Check Remove button + QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView"); + table->selectRow(currentRowCount-1); + QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton"); + removeRequestButton->click(); + QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); + bitdb.Flush(true); bitdb.Reset(); } @@ -192,5 +261,5 @@ void TestSendCoins() void WalletTests::walletTests() { - TestSendCoins(); + TestGUI(); } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index bcacc47ef3..74f5c774a0 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -91,9 +91,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (nNet > 0) { // Credit - if (CBitcoinAddress(rec->address).IsValid()) - { - CTxDestination address = CBitcoinAddress(rec->address).Get(); + CTxDestination address = DecodeDestination(rec->address); + if (IsValidDestination(address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; @@ -118,7 +117,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "<b>" + tr("To") + ":</b> "; - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; @@ -189,7 +188,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<b>" + tr("To") + ":</b> "; if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; - strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + strHTML += GUIUtil::HtmlEscape(EncodeDestination(address)); if(toSelf == ISMINE_SPENDABLE) strHTML += " (own address)"; else if(toSelf & ISMINE_WATCH_ONLY) @@ -304,7 +303,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco { if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; - strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); + strHTML += QString::fromStdString(EncodeDestination(address)); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 36d98ce49d..d40ffd22cd 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -55,7 +55,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { @@ -127,7 +127,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c38da9db..39dfdb587c 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -33,6 +33,7 @@ #include <QScrollBar> #include <QSignalMapper> #include <QTableView> +#include <QTimer> #include <QUrl> #include <QVBoxLayout> @@ -112,6 +113,17 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); + // Delay before filtering transactions in ms + static const int input_filter_delay = 200; + + QTimer* amount_typing_delay = new QTimer(this); + amount_typing_delay->setSingleShot(true); + amount_typing_delay->setInterval(input_filter_delay); + + QTimer* prefix_typing_delay = new QTimer(this); + prefix_typing_delay->setSingleShot(true); + prefix_typing_delay->setInterval(input_filter_delay); + QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); vlayout->setSpacing(0); @@ -173,8 +185,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); - connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start())); + connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount())); + connect(addressWidget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start())); + connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedPrefix())); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); @@ -312,20 +326,19 @@ void TransactionView::chooseWatchonly(int idx) (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); } -void TransactionView::changedPrefix(const QString &prefix) +void TransactionView::changedPrefix() { if(!transactionProxyModel) return; - transactionProxyModel->setAddressPrefix(prefix); + transactionProxyModel->setAddressPrefix(addressWidget->text()); } -void TransactionView::changedAmount(const QString &amount) +void TransactionView::changedAmount() { if(!transactionProxyModel) return; CAmount amount_parsed = 0; - if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) - { + if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } else diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 52e57cae4c..5b4cfd4a88 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -112,8 +112,8 @@ public Q_SLOTS: void chooseDate(int idx); void chooseType(int idx); void chooseWatchonly(int idx); - void changedPrefix(const QString &prefix); - void changedAmount(const QString &amount); + void changedAmount(); + void changedPrefix(); void exportClicked(); void focusTransaction(const QModelIndex&); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index f3183320f0..714a594318 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -7,6 +7,7 @@ #include "bitcoingui.h" #include "walletview.h" +#include <cassert> #include <cstdio> #include <QHBoxLayout> @@ -69,6 +70,7 @@ bool WalletFrame::setCurrentWallet(const QString& name) WalletView *walletView = mapWalletViews.value(name); walletStack->setCurrentWidget(walletView); + assert(walletView); walletView->updateEncryptionStatus(); return true; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 445d00e9c8..53b1c2967c 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -188,8 +188,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) bool WalletModel::validateAddress(const QString &address) { - CBitcoinAddress addressParsed(address.toStdString()); - return addressParsed.IsValid(); + return IsValidDestinationString(address.toStdString()); } WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) @@ -247,7 +246,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact setAddress.insert(rcp.address); ++nAddresses; - CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -348,7 +347,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); @@ -464,7 +463,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, const std::string &purpose, ChangeType status) { - QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString()); + QString strAddress = QString::fromStdString(EncodeDestination(address)); QString strLabel = QString::fromStdString(label); QString strPurpose = QString::fromStdString(purpose); @@ -561,9 +560,9 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const return wallet->GetPubKey(address, vchPubKeyOut); } -bool WalletModel::havePrivKey(const CKeyID &address) const +bool WalletModel::IsSpendable(const CTxDestination& dest) const { - return wallet->HaveKey(address); + return IsMine(*wallet, dest) & ISMINE_SPENDABLE; } bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const @@ -596,7 +595,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const { for (auto& group : wallet->ListCoins()) { - auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())]; + auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))]; for (auto& coin : group.second) { resultGroup.emplace_back(std::move(coin)); } @@ -634,7 +633,7 @@ void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) { - CTxDestination dest = CBitcoinAddress(sAddress).Get(); + CTxDestination dest = DecodeDestination(sAddress); std::stringstream ss; ss << nId; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6be36a57e2..05733f8272 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -190,7 +190,7 @@ public: UnlockContext requestUnlock(); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool havePrivKey(const CKeyID &address) const; + bool IsSpendable(const CTxDestination& dest) const; bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const; void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs); bool isSpent(const COutPoint& outpoint) const; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 971f5e0e1a..a56a40037f 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -122,8 +122,8 @@ void WalletView::setWalletModel(WalletModel *_walletModel) overviewPage->setWalletModel(_walletModel); receiveCoinsPage->setModel(_walletModel); sendCoinsPage->setModel(_walletModel); - usedReceivingAddressesPage->setModel(_walletModel->getAddressTableModel()); - usedSendingAddressesPage->setModel(_walletModel->getAddressTableModel()); + usedReceivingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); + usedSendingAddressesPage->setModel(_walletModel ? _walletModel->getAddressTableModel() : nullptr); if (_walletModel) { |