diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 1408a2beac3e..ecd77a3cd304 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -28,6 +28,7 @@ QT_FORMS_UI = \ qt/forms/editaddressdialog.ui \ qt/forms/helpmessagedialog.ui \ qt/forms/intro.ui \ + qt/forms/modaloverlay.ui \ qt/forms/masternodelist.ui \ qt/forms/openuridialog.ui \ qt/forms/optionsdialog.ui \ @@ -59,6 +60,7 @@ QT_MOC_CPP = \ qt/moc_intro.cpp \ qt/moc_macdockiconhandler.cpp \ qt/moc_macnotificationhandler.cpp \ + qt/moc_modaloverlay.cpp \ qt/moc_masternodelist.cpp \ qt/moc_notificator.cpp \ qt/moc_openuridialog.cpp \ @@ -128,6 +130,7 @@ BITCOIN_QT_H = \ qt/intro.h \ qt/macdockiconhandler.h \ qt/macnotificationhandler.h \ + qt/modaloverlay.h \ qt/masternodelist.h \ qt/networkstyle.h \ qt/notificator.h \ @@ -168,6 +171,7 @@ RES_ICONS = \ qt/res/icons/bitcoin.ico \ qt/res/icons/bitcoin.png \ qt/res/icons/chevron.png \ + qt/res/icons/warning.png \ qt/res/icons/drkblue/add.png \ qt/res/icons/drkblue/address-book.png \ qt/res/icons/drkblue/browse.png \ @@ -220,6 +224,7 @@ RES_ICONS = \ qt/res/icons/drkblue/verify.png \ qt/res/icons/drkblue/fontbigger.png \ qt/res/icons/drkblue/fontsmaller.png \ + qt/res/icons/drkblue/transaction_abandoned.png \ qt/res/icons/crownium/add.png \ qt/res/icons/crownium/address-book.png \ qt/res/icons/crownium/browse.png \ @@ -272,6 +277,7 @@ RES_ICONS = \ qt/res/icons/crownium/verify.png \ qt/res/icons/crownium/fontbigger.png \ qt/res/icons/crownium/fontsmaller.png \ + qt/res/icons/crownium/transaction_abandoned.png \ qt/res/icons/light/add.png \ qt/res/icons/light/address-book.png \ qt/res/icons/light/browse.png \ @@ -324,6 +330,7 @@ RES_ICONS = \ qt/res/icons/light/verify.png \ qt/res/icons/light/fontbigger.png \ qt/res/icons/light/fontsmaller.png \ + qt/res/icons/light/transaction_abandoned.png \ qt/res/icons/trad/add.png \ qt/res/icons/trad/address-book.png \ qt/res/icons/trad/browse.png \ @@ -375,7 +382,8 @@ RES_ICONS = \ qt/res/icons/trad/about_qt.png \ qt/res/icons/trad/verify.png \ qt/res/icons/trad/fontbigger.png \ - qt/res/icons/trad/fontsmaller.png + qt/res/icons/trad/fontsmaller.png \ + qt/res/icons/trad/transaction_abandoned.png BITCOIN_QT_CPP = \ qt/bantablemodel.cpp \ @@ -387,6 +395,7 @@ BITCOIN_QT_CPP = \ qt/csvmodelwriter.cpp \ qt/guiutil.cpp \ qt/intro.cpp \ + qt/modaloverlay.cpp \ qt/networkstyle.cpp \ qt/notificator.cpp \ qt/optionsdialog.cpp \ diff --git a/src/coincontrol.h b/src/coincontrol.h index 82346aefd3a3..ff8f33a8475c 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -42,10 +42,9 @@ class CCoinControl return (setSelected.size() > 0); } - bool IsSelected(const uint256& hash, unsigned int n) const + bool IsSelected(const COutPoint& output) const { - COutPoint outpt(hash, n); - return (setSelected.count(outpt) > 0); + return (setSelected.count(output) > 0); } void Select(const COutPoint& output) diff --git a/src/init.cpp b/src/init.cpp index 6cbdaec33a7b..fa26b0a89386 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1613,8 +1613,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!fLoaded) { // first suggest a reindex if (!fReset) { - bool fRet = uiInterface.ThreadSafeMessageBox( + bool fRet = uiInterface.ThreadSafeQuestion( strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), + strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; diff --git a/src/net.cpp b/src/net.cpp index 90e794b69f21..4b132c607445 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -630,6 +630,7 @@ void CNode::copyStats(CNodeStats &stats) { stats.nodeid = this->GetId(); X(nServices); + X(addr); X(fRelayTxes); X(nLastSend); X(nLastRecv); @@ -659,7 +660,7 @@ void CNode::copyStats(CNodeStats &stats) // Raw ping time is in microseconds, but show it to user as whole seconds (Dash users should be well used to small numbers with many decimal places by now :) stats.dPingTime = (((double)nPingUsecTime) / 1e6); - stats.dPingMin = (((double)nMinPingUsecTime) / 1e6); + stats.dMinPing = (((double)nMinPingUsecTime) / 1e6); stats.dPingWait = (((double)nPingUsecWait) / 1e6); // Leave string empty if addrLocal invalid (not filled in yet) diff --git a/src/net.h b/src/net.h index 2d238ddd3be2..aaa8cc0dcb7b 100644 --- a/src/net.h +++ b/src/net.h @@ -607,8 +607,9 @@ class CNodeStats bool fWhitelisted; double dPingTime; double dPingWait; - double dPingMin; + double dMinPing; std::string addrLocal; + CAddress addr; }; diff --git a/src/noui.cpp b/src/noui.cpp index 3b6cc9a11645..886228e4c06e 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -40,6 +40,11 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str return false; } +static bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) +{ + return noui_ThreadSafeMessageBox(message, caption, style); +} + static void noui_InitMessage(const std::string& message) { LogPrintf("init message: %s\n", message); @@ -49,5 +54,6 @@ void noui_connect() { // Connect dashd signal handlers uiInterface.ThreadSafeMessageBox.connect(noui_ThreadSafeMessageBox); + uiInterface.ThreadSafeQuestion.connect(noui_ThreadSafeQuestion); uiInterface.InitMessage.connect(noui_InitMessage); } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 8d152ab4d954..16fc37208776 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -147,6 +147,11 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const return nTxSize; } +unsigned int CTransaction::GetTotalSize() const +{ + return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); +} + std::string CTransaction::ToString() const { std::string str; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 7081ae557e17..f87b45e728ac 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -273,6 +273,13 @@ class CTransaction // Compute modified tx size for priority calculation (optionally given tx size) unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; + + /** + * Get the total transaction size in bytes, including witness data. + * "Total Size" defined in BIP141 and BIP144. + * @return Total transaction size in bytes + */ + unsigned int GetTotalSize() const; bool IsCoinBase() const { diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index f853807b3166..df1c0148d22c 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -77,10 +77,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : AskPassphraseDialog::~AskPassphraseDialog() { - // Attempt to overwrite text so that they do not linger around in memory - ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size())); - ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size())); - ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size())); + secureClearPassFields(); delete ui; } @@ -103,6 +100,8 @@ void AskPassphraseDialog::accept() newpass1.assign(ui->passEdit2->text().toStdString().c_str()); newpass2.assign(ui->passEdit3->text().toStdString().c_str()); + secureClearPassFields(); + switch(mode) { case Encrypt: { @@ -262,3 +261,17 @@ bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) } return QDialog::eventFilter(object, event); } + +static void SecureClearQLineEdit(QLineEdit* edit) +{ + // Attempt to overwrite text so that they do not linger around in memory + edit->setText(QString(" ").repeated(edit->text().size())); + edit->clear(); +} + +void AskPassphraseDialog::secureClearPassFields() +{ + SecureClearQLineEdit(ui->passEdit1); + SecureClearQLineEdit(ui->passEdit2); + SecureClearQLineEdit(ui->passEdit3); +} diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 55612238e34b..fa02cad60bd7 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -43,6 +43,7 @@ class AskPassphraseDialog : public QDialog private Q_SLOTS: void textChanged(); + void secureClearPassFields(); protected: bool event(QEvent *event); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 507ee58bc474..ed1d257dc56f 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -9,6 +9,7 @@ #include "clientmodel.h" #include "guiconstants.h" #include "guiutil.h" +#include "modaloverlay.h" #include "networkstyle.h" #include "notificator.h" #include "openuridialog.h" @@ -116,6 +117,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n notificator(0), rpcConsole(0), helpMessageDialog(0), + modalOverlay(0), prevBlocks(0), spinnerFrame(0), platformStyle(platformStyle) @@ -253,6 +255,12 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n // Subscribe to notifications from core subscribeToCoreSignals(); + + modalOverlay = new ModalOverlay(this->centralWidget()); +#ifdef ENABLE_WALLET + if(enableWallet) + connect(walletFrame, SIGNAL(requestedSyncWarningInfo()), this, SLOT(showModalOverlay())); +#endif } BitcoinGUI::~BitcoinGUI() @@ -624,6 +632,18 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(clientModel->getOptionsModel()); + + OptionsModel* optionsModel = clientModel->getOptionsModel(); + if(optionsModel) + { + // be aware of the tray icon disable state change reported by the OptionsModel object. + connect(optionsModel,SIGNAL(hideTrayIconChanged(bool)),this,SLOT(setTrayIconVisible(bool))); + + // initialize the disable state of the tray icon with the current value in the model. + setTrayIconVisible(optionsModel->getHideTrayIcon()); + } + + modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime())); } else { // Disable possibility to show main window via action toggleHideAction->setEnabled(false); @@ -695,7 +715,7 @@ void BitcoinGUI::createTrayIcon(const NetworkStyle *networkStyle) QString toolTip = tr("Dash Core client") + " " + networkStyle->getTitleAddText(); trayIcon->setToolTip(toolTip); trayIcon->setIcon(networkStyle->getTrayAndWindowIcon()); - trayIcon->show(); + trayIcon->hide(); notificator = new Notificator(QApplication::applicationName(), trayIcon, this); } @@ -896,7 +916,17 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header) { - if(!clientModel) + if (modalOverlay) + { + if (header) { + /* use clientmodels getHeaderTipHeight and getHeaderTipTime because the NotifyHeaderTip signal does not fire when updating the best header */ + modalOverlay->setKnownBestHeight(clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(clientModel->getHeaderTipTime())); + } + else { + modalOverlay->tipUpdate(count, blockDate, nVerificationProgress); + } + } + if (!clientModel) return; // Prevent orphan statusbar messages (e.g. hover Quit in main menu, wait until chain-sync starts -> garbelled text) @@ -940,32 +970,23 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer // Set icon state: spinning if catching up, tick otherwise QString theme = GUIUtil::getThemeName(); - if(!masternodeSync.IsBlockchainSynced()) +#ifdef ENABLE_WALLET + if (walletFrame) { - // Represent time from last generated block in human readable text - QString timeBehindText; - const int HOUR_IN_SECONDS = 60*60; - const int DAY_IN_SECONDS = 24*60*60; - const int WEEK_IN_SECONDS = 7*24*60*60; - const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar - if(secs < 2*DAY_IN_SECONDS) - { - timeBehindText = tr("%n hour(s)","",secs/HOUR_IN_SECONDS); - } - else if(secs < 2*WEEK_IN_SECONDS) + if(secs < 25*60) // 90*60 in bitcoin { - timeBehindText = tr("%n day(s)","",secs/DAY_IN_SECONDS); - } - else if(secs < YEAR_IN_SECONDS) - { - timeBehindText = tr("%n week(s)","",secs/WEEK_IN_SECONDS); + modalOverlay->showHide(true, true); } else { - qint64 years = secs / YEAR_IN_SECONDS; - qint64 remainder = secs % YEAR_IN_SECONDS; - timeBehindText = tr("%1 and %2").arg(tr("%n year(s)", "", years)).arg(tr("%n week(s)","", remainder/WEEK_IN_SECONDS)); + modalOverlay->showHide(); } + } +#endif // ENABLE_WALLET + + if(!masternodeSync.IsBlockchainSynced()) + { + QString timeBehindText = GUIUtil::formateNiceTimeOffset(secs); progressBarLabel->setVisible(true); progressBar->setFormat(tr("%1 behind").arg(timeBehindText)); @@ -985,7 +1006,9 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer #ifdef ENABLE_WALLET if(walletFrame) + { walletFrame->showOutOfSyncWarning(true); + } #endif // ENABLE_WALLET tooltip += QString("
"); @@ -1342,6 +1365,20 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress) progressDialog->setValue(nProgress); } +void BitcoinGUI::setTrayIconVisible(bool fHideTrayIcon) +{ + if (trayIcon) + { + trayIcon->setVisible(!fHideTrayIcon); + } +} + +void BitcoinGUI::showModalOverlay() +{ + if (modalOverlay) + modalOverlay->showHide(false, true); +} + static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style) { bool modal = (style & CClientUIInterface::MODAL); @@ -1363,12 +1400,14 @@ void BitcoinGUI::subscribeToCoreSignals() { // Connect signals to client uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); + uiInterface.ThreadSafeQuestion.connect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); } void BitcoinGUI::unsubscribeFromCoreSignals() { // Disconnect signals from client uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); + uiInterface.ThreadSafeQuestion.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); } /** Get restart command-line parameters and request restart */ diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 1c1b944d99f2..bdeb1994902c 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -30,6 +30,7 @@ class UnitDisplayStatusBarControl; class WalletFrame; class WalletModel; class HelpMessageDialog; +class ModalOverlay; class MasternodeList; class CWallet; @@ -132,6 +133,7 @@ class BitcoinGUI : public QMainWindow Notificator *notificator; RPCConsole *rpcConsole; HelpMessageDialog *helpMessageDialog; + ModalOverlay *modalOverlay; /** Keep track of previous number of blocks, to detect progress */ int prevBlocks; @@ -263,6 +265,11 @@ private Q_SLOTS: /** Show progress dialog e.g. for verifychain */ void showProgress(const QString &title, int nProgress); + + /** When hideTrayIcon setting is changed in OptionsModel hide or show the icon accordingly. */ + void setTrayIconVisible(bool); + + void showModalOverlay(); }; class UnitDisplayStatusBarControl : public QLabel diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 22e19bf49938..8e0588f89e03 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -95,6 +95,22 @@ int ClientModel::getNumBlocks() const return chainActive.Height(); } +int ClientModel::getHeaderTipHeight() const +{ + LOCK(cs_main); + if (!pindexBestHeader) + return 0; + return pindexBestHeader->nHeight; +} + +int64_t ClientModel::getHeaderTipTime() const +{ + LOCK(cs_main); + if (!pindexBestHeader) + return 0; + return pindexBestHeader->GetBlockTime(); +} + quint64 ClientModel::getTotalBytesRecv() const { if(!g_connman) @@ -229,11 +245,6 @@ QString ClientModel::formatSubVersion() const return QString::fromStdString(strSubVersion); } -QString ClientModel::formatBuildDate() const -{ - return QString::fromStdString(CLIENT_DATE); -} - bool ClientModel::isReleaseVersion() const { return CLIENT_VERSION_IS_RELEASE; @@ -249,6 +260,11 @@ QString ClientModel::formatClientStartupTime() const return QDateTime::fromTime_t(nClientStartupTime).toString(); } +QString ClientModel::dataDir() const +{ + return QString::fromStdString(GetDataDir().string()); +} + void ClientModel::updateBanlist() { banTableModel->refresh(); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index ac4102b36db5..fa2575ce46cb 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -53,7 +53,8 @@ class ClientModel : public QObject int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; QString getMasternodeCountString() const; int getNumBlocks() const; - + int getHeaderTipHeight() const; + int64_t getHeaderTipTime() const; //! Return number of transactions in the mempool long getMempoolSize() const; //! Return the dynamic memory usage of the mempool @@ -74,10 +75,10 @@ class ClientModel : public QObject QString formatFullVersion() const; QString formatSubVersion() const; - QString formatBuildDate() const; bool isReleaseVersion() const; QString clientName() const; QString formatClientStartupTime() const; + QString dataDir() const; private: OptionsModel *optionsModel; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index adb4522d032f..0da95887b470 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -83,7 +83,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); - QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this); QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); @@ -92,7 +91,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee())); connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee())); connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes())); - connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(clipboardPriority())); connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput())); connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange())); @@ -101,7 +99,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget ui->labelCoinControlFee->addAction(clipboardFeeAction); ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); ui->labelCoinControlBytes->addAction(clipboardBytesAction); - ui->labelCoinControlPriority->addAction(clipboardPriorityAction); ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); @@ -140,11 +137,9 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *platformStyle, QWidget ui->treeWidget->setColumnWidth(COLUMN_PRIVATESEND_ROUNDS, 88); ui->treeWidget->setColumnWidth(COLUMN_DATE, 80); ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 100); - ui->treeWidget->setColumnWidth(COLUMN_PRIORITY, 100); ui->treeWidget->setColumnHidden(COLUMN_TXHASH, true); // store transaction hash in this column, but don't show it ui->treeWidget->setColumnHidden(COLUMN_VOUT_INDEX, true); // store vout index in this column, but don't show it ui->treeWidget->setColumnHidden(COLUMN_AMOUNT_INT64, true); // store amount int64 in this column, but don't show it - ui->treeWidget->setColumnHidden(COLUMN_PRIORITY_INT64, true); // store priority int64 in this column, but don't show it ui->treeWidget->setColumnHidden(COLUMN_DATE_INT64, true); // store date int64 in this column, but don't show it // default view is sorted by amount desc @@ -372,12 +367,6 @@ void CoinControlDialog::clipboardBytes() GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } -// copy label "Priority" to clipboard -void CoinControlDialog::clipboardPriority() -{ - GUIUtil::setClipboard(ui->labelCoinControlPriority->text()); -} - // copy label "Dust" to clipboard void CoinControlDialog::clipboardLowOutput() { @@ -475,25 +464,6 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) #endif } -// return human readable label for priority number -QString CoinControlDialog::getPriorityLabel(double dPriority, double mempoolEstimatePriority) -{ - double dPriorityMedium = mempoolEstimatePriority; - - if (dPriorityMedium <= 0) - dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded - - if (dPriority / 1000000 > dPriorityMedium) return tr("highest"); - else if (dPriority / 100000 > dPriorityMedium) return tr("higher"); - else if (dPriority / 10000 > dPriorityMedium) return tr("high"); - else if (dPriority / 1000 > dPriorityMedium) return tr("medium-high"); - else if (dPriority > dPriorityMedium) return tr("medium"); - else if (dPriority * 10 > dPriorityMedium) return tr("low-medium"); - else if (dPriority * 100 > dPriorityMedium) return tr("low"); - else if (dPriority * 1000 > dPriorityMedium) return tr("lower"); - else return tr("lowest"); -} - // shows count of locked unspent outputs void CoinControlDialog::updateLabelLocked() { @@ -529,7 +499,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) } } - QString sPriorityLabel = tr("none"); CAmount nAmount = 0; CAmount nPayFee = 0; CAmount nAfterFee = 0; @@ -595,11 +564,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) // Bytes nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here - // Priority - double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); - dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority) - sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority); - // in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate if (CoinControlDialog::fSubtractFeeFromAmount) if (nAmount - nPayAmount == 0) @@ -614,6 +578,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (coinControl->fUseInstantSend) nPayFee = std::max(nPayFee, CTxLockRequest(txDummy).GetMinFee()); // Allow free? (require at least hard-coded threshold and default to that if no estimate) + double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); + dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority) double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold()); fAllowFree = (dPriority >= dPriorityNeeded); @@ -669,7 +635,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) QLabel *l3 = dialog->findChild("labelCoinControlFee"); QLabel *l4 = dialog->findChild("labelCoinControlAfterFee"); QLabel *l5 = dialog->findChild("labelCoinControlBytes"); - QLabel *l6 = dialog->findChild("labelCoinControlPriority"); QLabel *l7 = dialog->findChild("labelCoinControlLowOutput"); QLabel *l8 = dialog->findChild("labelCoinControlChange"); @@ -685,7 +650,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes - l6->setText(sPriorityLabel); // Priority l7->setText(fDust ? tr("yes") : tr("no")); // Dust l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change if (nPayFee > 0 && (coinControl->nMinimumTotalFee < nPayFee)) @@ -696,21 +660,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l8->setText(ASYMP_UTF8 + l8->text()); } - // turn labels "red" - l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000 - l6->setStyleSheet((dPriority > 0 && !fAllowFree) ? "color:red;" : ""); // Priority < "medium" - l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes" + // turn label red when dust + l7->setStyleSheet((fDust) ? "color:red;" : ""); // tool tips - QString toolTip1 = tr("This label turns red if the transaction size is greater than 1000 bytes.") + "

"; - toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000))) + "

"; - toolTip1 += tr("Can vary +/- 1 byte per input."); - - QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "

"; - toolTip2 += tr("This label turns red if the priority is smaller than \"medium\".") + "

"; - toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatHtmlWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000))); - - QString toolTip3 = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold."); + 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 double dFeeVary; @@ -723,14 +677,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) l3->setToolTip(toolTip4); l4->setToolTip(toolTip4); - l5->setToolTip(toolTip1); - l6->setToolTip(toolTip2); - l7->setToolTip(toolTip3); + l7->setToolTip(toolTipDust); l8->setToolTip(toolTip4); dialog->findChild("labelCoinControlFeeText") ->setToolTip(l3->toolTip()); dialog->findChild("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip()); dialog->findChild("labelCoinControlBytesText") ->setToolTip(l5->toolTip()); - dialog->findChild("labelCoinControlPriorityText") ->setToolTip(l6->toolTip()); dialog->findChild("labelCoinControlLowOutputText")->setToolTip(l7->toolTip()); dialog->findChild("labelCoinControlChangeText") ->setToolTip(l8->toolTip()); @@ -755,7 +706,6 @@ void CoinControlDialog::updateView() QFlags flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); - double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); std::map > mapCoins; model->listCoins(mapCoins); @@ -786,11 +736,8 @@ void CoinControlDialog::updateView() } CAmount nSum = 0; - double dPrioritySum = 0; int nChildren = 0; - int nInputSum = 0; BOOST_FOREACH(const COutput& out, coins.second) { - int nInputSize = 0; nSum += out.tx->vout[out.i].nValue; nChildren++; @@ -812,11 +759,6 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_ADDRESS, sAddress); itemOutput->setToolTip(COLUMN_ADDRESS, sAddress); - - CPubKey pubkey; - CKeyID *keyid = boost::get(&outputAddress); - if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) - nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes) } // label @@ -856,13 +798,6 @@ void CoinControlDialog::updateView() // confirmations itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); - // priority - double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 - itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority)); - itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " ")); - dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); - nInputSum += nInputSize; - // transaction hash uint256 txhash = out.tx->GetHash(); itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); @@ -880,20 +815,17 @@ void CoinControlDialog::updateView() } // set checkbox - if (coinControl->IsSelected(txhash, out.i)) + if (coinControl->IsSelected(COutPoint(txhash, out.i))) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } // amount if (treeMode) { - dPrioritySum = dPrioritySum / (nInputSum + 78); itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); - itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum, mempoolEstimatePriority)); - itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " ")); } } diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 792afbdb918f..1de4d5bbc3fa 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -40,7 +40,6 @@ class CoinControlDialog : public QDialog // static because also called from sendcoinsdialog static void updateLabels(WalletModel*, QDialog*); - static QString getPriorityLabel(double dPriority, double mempoolEstimatePriority); static QList payAmounts; static CCoinControl *coinControl; @@ -73,11 +72,9 @@ class CoinControlDialog : public QDialog COLUMN_PRIVATESEND_ROUNDS, COLUMN_DATE, COLUMN_CONFIRMATIONS, - COLUMN_PRIORITY, COLUMN_TXHASH, COLUMN_VOUT_INDEX, COLUMN_AMOUNT_INT64, - COLUMN_PRIORITY_INT64, COLUMN_DATE_INT64 }; @@ -88,8 +85,6 @@ class CoinControlDialog : public QDialog { if (column == COLUMN_AMOUNT_INT64) return COLUMN_AMOUNT; - else if (column == COLUMN_PRIORITY_INT64) - return COLUMN_PRIORITY; else if (column == COLUMN_DATE_INT64) return COLUMN_DATE; } @@ -97,8 +92,6 @@ class CoinControlDialog : public QDialog { if (column == COLUMN_AMOUNT) return COLUMN_AMOUNT_INT64; - else if (column == COLUMN_PRIORITY) - return COLUMN_PRIORITY_INT64; else if (column == COLUMN_DATE) return COLUMN_DATE_INT64; } @@ -119,7 +112,6 @@ private Q_SLOTS: void clipboardFee(); void clipboardAfterFee(); void clipboardBytes(); - void clipboardPriority(); void clipboardLowOutput(); void clipboardChange(); void radioTreeMode(bool); diff --git a/src/qt/dash.qrc b/src/qt/dash.qrc index 2ba93024c44e..10e2437b9a7d 100644 --- a/src/qt/dash.qrc +++ b/src/qt/dash.qrc @@ -2,6 +2,7 @@ res/icons/bitcoin.png res/icons/chevron.png + res/icons/warning.png res/icons/drkblue/address-book.png @@ -56,6 +57,7 @@ res/icons/drkblue/hd_disabled.png res/icons/drkblue/fontbigger.png res/icons/drkblue/fontsmaller.png + res/icons/drkblue/transaction_abandoned.png res/icons/crownium/address-book.png @@ -110,6 +112,7 @@ res/icons/crownium/hd_disabled.png res/icons/crownium/fontbigger.png res/icons/crownium/fontsmaller.png + res/icons/crownium/transaction_abandoned.png res/icons/light/address-book.png @@ -164,6 +167,7 @@ res/icons/light/hd_disabled.png res/icons/light/fontbigger.png res/icons/light/fontsmaller.png + res/icons/light/transaction_abandoned.png res/icons/trad/address-book.png @@ -218,6 +222,7 @@ res/icons/trad/hd_disabled.png res/icons/trad/fontbigger.png res/icons/trad/fontsmaller.png + res/icons/trad/transaction_abandoned.png res/css/drkblue.css diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index 5cc46ea96b79..065c7a03e97e 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -140,7 +140,10 @@ - + + + false + 75 @@ -148,12 +151,15 @@ - Priority: + Dust: - + + + false + IBeamCursor @@ -161,7 +167,7 @@ Qt::ActionsContextMenu - medium + no Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -213,41 +219,6 @@ - - - - false - - - - 75 - true - - - - Dust: - - - - - - - false - - - IBeamCursor - - - Qt::ActionsContextMenu - - - no - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - @@ -447,7 +418,7 @@ false - 13 + 11 true @@ -493,16 +464,6 @@ Confirmed - - - Priority - - - - - - - diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index f2275ac7ff5e..1df09f0f7416 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -141,12 +141,12 @@ - Build date + Datadir - + IBeamCursor @@ -156,6 +156,9 @@ Qt::PlainText + + true + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -1383,13 +1386,36 @@ + + + Min Ping + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + Time Offset - + IBeamCursor @@ -1405,7 +1431,7 @@ - + Qt::Vertical diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui new file mode 100644 index 000000000000..02d8840bd1fb --- /dev/null +++ b/src/qt/forms/modaloverlay.ui @@ -0,0 +1,373 @@ + + + ModalOverlay + + + + 0 + 0 + 640 + 385 + + + + Form + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + #bgWidget { background: rgba(0,0,0,220); } + + + + 60 + + + 60 + + + 60 + + + 60 + + + + + #contentWidget { background: rgba(255,255,255,240); border-radius: 6px; } + +QLabel { color: rgb(40,40,40); } + + + + 0 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 20 + + + + + 0 + + + + + false + + + + + + + :/icons/warning + :/icons/warning:/icons/warning + + + + 48 + 48 + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + 0 + + + + + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. + + + Qt::RichText + + + true + + + + + + + + 75 + true + + + + Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. + + + Qt::RichText + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QFormLayout::FieldsStayAtSizeHint + + + 6 + + + 6 + + + 10 + + + + + + 75 + true + + + + Number of blocks left + + + + + + + unknown... + + + + + + + + 75 + true + + + + Last block time + + + + + + + + 0 + 0 + + + + unknown... + + + + + + + + 75 + true + + + + Progress + + + + + + + + + ~ + + + + + + + 24 + + + + + + + + + + 75 + true + + + + Progress increase per hour + + + + + + + calculating... + + + + + + + + 75 + true + + + + Estimated time left until synced + + + + + + + calculating... + + + + + + + + + 10 + + + 10 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Hide + + + + + + + + + + + + + + + + ModalOverlay + QWidget +
modaloverlay.h
+ 1 +
+
+ + +
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index a41f13a72cec..4681792d393b 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -613,6 +613,16 @@ &Window
+ + + + &Hide the icon from the system tray. + + + Hide tray icon + + + diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 07a7651908e8..a544f414bf5e 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -26,7 +26,7 @@ false - background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; + QLabel { background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; } true @@ -34,6 +34,9 @@ 3 + + Qt::TextSelectableByMouse + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index fdce0f823791..6330eba191de 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -332,7 +332,7 @@ - + 75 @@ -340,12 +340,12 @@ - Priority: + Dust: - + IBeamCursor @@ -353,7 +353,7 @@ Qt::ActionsContextMenu - medium + no Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -411,36 +411,7 @@ - - - - - 75 - true - - - - Dust: - - - - - - - IBeamCursor - - - Qt::ActionsContextMenu - - - no - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - +
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index b7fe177ddc2f..161cd95ef1e9 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -30,6 +30,8 @@ static const bool DEFAULT_SPLASHSCREEN = true; #define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255) /* Transaction list -- TX status decoration - offline */ #define COLOR_TX_STATUS_OFFLINE QColor(192, 192, 192) +/* Transaction list -- TX status decoration - danger, tx needs attention */ +#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) /* Transaction list -- TX status decoration - default color */ #define COLOR_BLACK QColor(0, 0, 0) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f8d67fe6a2c6..670edecfc6da 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1021,7 +1021,7 @@ QString formatServicesStr(quint64 mask) QString formatPingTime(double dPingTime) { - return dPingTime == 0 ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10)); + return (dPingTime == std::numeric_limits::max()/1e6 || dPingTime == 0) ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10)); } QString formatTimeOffset(int64_t nTimeOffset) @@ -1029,4 +1029,40 @@ QString formatTimeOffset(int64_t nTimeOffset) return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); } +QString formateNiceTimeOffset(qint64 secs) +{ + // Represent time from last generated block in human readable text + QString timeBehindText; + const int HOUR_IN_SECONDS = 60*60; + const int DAY_IN_SECONDS = 24*60*60; + const int WEEK_IN_SECONDS = 7*24*60*60; + const int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar + if(secs < 60) + { + timeBehindText = QObject::tr("%n seconds(s)","",secs); + } + else if(secs < 2*HOUR_IN_SECONDS) + { + timeBehindText = QObject::tr("%n minutes(s)","",secs/60); + } + else if(secs < 2*DAY_IN_SECONDS) + { + timeBehindText = QObject::tr("%n hour(s)","",secs/HOUR_IN_SECONDS); + } + else if(secs < 2*WEEK_IN_SECONDS) + { + timeBehindText = QObject::tr("%n day(s)","",secs/DAY_IN_SECONDS); + } + else if(secs < YEAR_IN_SECONDS) + { + timeBehindText = QObject::tr("%n week(s)","",secs/WEEK_IN_SECONDS); + } + else + { + qint64 years = secs / YEAR_IN_SECONDS; + qint64 remainder = secs % YEAR_IN_SECONDS; + timeBehindText = QObject::tr("%1 and %2").arg(QObject::tr("%n year(s)", "", years)).arg(QObject::tr("%n week(s)","", remainder/WEEK_IN_SECONDS)); + } + return timeBehindText; +} } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 29add470fce3..8d2ac64197aa 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -218,6 +218,8 @@ namespace GUIUtil /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ QString formatTimeOffset(int64_t nTimeOffset); + QString formateNiceTimeOffset(qint64 secs); + #if defined(Q_OS_MAC) && QT_VERSION >= 0x050000 // workaround for Qt OSX Bug: // https://bugreports.qt-project.org/browse/QTBUG-15631 diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp new file mode 100644 index 000000000000..00bb4da5e9fe --- /dev/null +++ b/src/qt/modaloverlay.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "modaloverlay.h" +#include "ui_modaloverlay.h" + +#include "guiutil.h" + +#include +#include + +ModalOverlay::ModalOverlay(QWidget *parent) : +QWidget(parent), +ui(new Ui::ModalOverlay), +bestHeaderHeight(0), +bestHeaderDate(QDateTime()), +layerIsVisible(false), +userClosed(false) +{ + ui->setupUi(this); + connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(closeClicked())); + if (parent) { + parent->installEventFilter(this); + raise(); + } + + blockProcessTime.clear(); + setVisible(false); +} + +ModalOverlay::~ModalOverlay() +{ + delete ui; +} + +bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) { + if (obj == parent()) { + if (ev->type() == QEvent::Resize) { + QResizeEvent * rev = static_cast(ev); + resize(rev->size()); + if (!layerIsVisible) + setGeometry(0, height(), width(), height()); + + } + else if (ev->type() == QEvent::ChildAdded) { + raise(); + } + } + return QWidget::eventFilter(obj, ev); +} + +//! Tracks parent widget changes +bool ModalOverlay::event(QEvent* ev) { + if (ev->type() == QEvent::ParentAboutToChange) { + if (parent()) parent()->removeEventFilter(this); + } + else if (ev->type() == QEvent::ParentChange) { + if (parent()) { + parent()->installEventFilter(this); + raise(); + } + } + return QWidget::event(ev); +} + +void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate) +{ + if (count > bestHeaderHeight) { + bestHeaderHeight = count; + bestHeaderDate = blockDate; + } +} + +void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress) +{ + QDateTime currentDate = QDateTime::currentDateTime(); + + // keep a vector of samples of verification progress at height + blockProcessTime.push_front(qMakePair(currentDate.currentMSecsSinceEpoch(), nVerificationProgress)); + + // show progress speed if we have more then one sample + if (blockProcessTime.size() >= 2) + { + double progressStart = blockProcessTime[0].second; + 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++) + { + QPair sample = blockProcessTime[i]; + + // take first sample after 500 seconds or last available one + if (sample.first < (currentDate.currentMSecsSinceEpoch() - 500*1000) || i == blockProcessTime.size()-1) + { + progressDelta = progressStart-sample.second; + timeDelta = blockProcessTime[0].first - sample.first; + progressPerHour = progressDelta/(double)timeDelta*1000*3600; + remainingMSecs = remainingProgress / progressDelta * timeDelta; + break; + } + } + // show progress increase per hour + ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); + + // show expected remaining time + ui->expectedTimeLeft->setText(GUIUtil::formateNiceTimeOffset(remainingMSecs/1000.0)); + + // keep maximal 5000 samples + static const int MAX_SAMPLES = 5000; + if (blockProcessTime.count() > MAX_SAMPLES) + blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES); + } + + // show the last block date + ui->newestBlockDate->setText(blockDate.toString()); + + // show the percentage done according to nVerificationProgress + ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%"); + ui->progressBar->setValue(nVerificationProgress*100); + + if (!bestHeaderDate.isValid()) + // not syncing + return; + + // estimate the number of headers left based on nPowTargetSpacing + // and check if the gui is not aware of the the best header (happens rarely) + int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / 600; + bool hasBestHeader = bestHeaderHeight >= count; + + // show remaining number of blocks + if (estimateNumHeadersLeft < 24 && hasBestHeader) { + ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count)); + } else { + ui->expectedTimeLeft->setText(tr("Unknown. Syncing Headers...")); + } +} + +void ModalOverlay::showHide(bool hide, bool userRequested) +{ + if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested)) + return; + + if (!isVisible() && !hide) + setVisible(true); + + setGeometry(0, hide ? 0 : height(), width(), height()); + + QPropertyAnimation* animation = new QPropertyAnimation(this, "pos"); + animation->setDuration(300); + animation->setStartValue(QPoint(0, hide ? 0 : this->height())); + animation->setEndValue(QPoint(0, hide ? this->height() : 0)); + animation->setEasingCurve(QEasingCurve::OutQuad); + animation->start(QAbstractAnimation::DeleteWhenStopped); + layerIsVisible = !hide; +} + +void ModalOverlay::closeClicked() +{ + showHide(true); + userClosed = true; +} diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h new file mode 100644 index 000000000000..5a2b9d5a5df9 --- /dev/null +++ b/src/qt/modaloverlay.h @@ -0,0 +1,45 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_MODALOVERLAY_H +#define BITCOIN_QT_MODALOVERLAY_H + +#include +#include + +namespace Ui { + class ModalOverlay; +} + +/** Modal overlay to display information about the chain-sync state */ +class ModalOverlay : public QWidget +{ + Q_OBJECT + +public: + explicit ModalOverlay(QWidget *parent); + ~ModalOverlay(); + +public Q_SLOTS: + void tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress); + void setKnownBestHeight(int count, const QDateTime& blockDate); + + // will show or hide the modal layer + void showHide(bool hide = false, bool userRequested = false); + void closeClicked(); + +protected: + bool eventFilter(QObject * obj, QEvent * ev); + bool event(QEvent* ev); + +private: + Ui::ModalOverlay *ui; + int bestHeaderHeight; //best known height (based on the headers) + QDateTime bestHeaderDate; + QVector > blockProcessTime; + bool layerIsVisible; + bool userClosed; +}; + +#endif // BITCOIN_QT_MODALOVERLAY_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index ef3e3df3068e..c5f30c985e6a 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -221,6 +221,7 @@ void OptionsDialog::setMapper() /* Window */ #ifndef Q_OS_MAC + mapper->addMapping(ui->hideTrayIcon, OptionsModel::HideTrayIcon); mapper->addMapping(ui->minimizeToTray, OptionsModel::MinimizeToTray); mapper->addMapping(ui->minimizeOnClose, OptionsModel::MinimizeOnClose); #endif @@ -271,6 +272,19 @@ void OptionsDialog::on_cancelButton_clicked() reject(); } +void OptionsDialog::on_hideTrayIcon_stateChanged(int fState) +{ + if(fState) + { + ui->minimizeToTray->setChecked(false); + ui->minimizeToTray->setEnabled(false); + } + else + { + ui->minimizeToTray->setEnabled(true); + } +} + void OptionsDialog::showRestartWarning(bool fPersistent) { ui->statusLabel->setStyleSheet("QLabel { color: red; }"); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index e944fb9ee9b2..41b56d138625 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -49,6 +49,8 @@ private Q_SLOTS: void on_resetButton_clicked(); void on_okButton_clicked(); void on_cancelButton_clicked(); + + void on_hideTrayIcon_stateChanged(int fState); void showRestartWarning(bool fPersistent = false); void clearStatusLabel(); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index d1d610dfbfc6..77f79eb29871 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -60,9 +60,14 @@ void OptionsModel::Init(bool resetSettings) // These are Qt-only settings: // Window + if (!settings.contains("fHideTrayIcon")) + settings.setValue("fHideTrayIcon", false); + fHideTrayIcon = settings.value("fHideTrayIcon").toBool(); + Q_EMIT hideTrayIconChanged(fHideTrayIcon); + if (!settings.contains("fMinimizeToTray")) settings.setValue("fMinimizeToTray", false); - fMinimizeToTray = settings.value("fMinimizeToTray").toBool(); + fMinimizeToTray = settings.value("fMinimizeToTray").toBool() && !fHideTrayIcon; if (!settings.contains("fMinimizeOnClose")) settings.setValue("fMinimizeOnClose", false); @@ -215,6 +220,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const { case StartAtStartup: return GUIUtil::GetStartOnSystemStartup(); + case HideTrayIcon: + return fHideTrayIcon; case MinimizeToTray: return fMinimizeToTray; case MapPortUPnP: @@ -307,6 +314,11 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in case StartAtStartup: successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); break; + case HideTrayIcon: + fHideTrayIcon = value.toBool(); + settings.setValue("fHideTrayIcon", fHideTrayIcon); + Q_EMIT hideTrayIconChanged(fHideTrayIcon); + break; case MinimizeToTray: fMinimizeToTray = value.toBool(); settings.setValue("fMinimizeToTray", fMinimizeToTray); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 24244e7678cf..88839639dd9d 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -28,6 +28,7 @@ class OptionsModel : public QAbstractListModel enum OptionID { StartAtStartup, // bool + HideTrayIcon, // bool MinimizeToTray, // bool MapPortUPnP, // bool MinimizeOnClose, // bool @@ -66,6 +67,7 @@ class OptionsModel : public QAbstractListModel void setDisplayUnit(const QVariant &value); /* Explicit getters */ + bool getHideTrayIcon() { return fHideTrayIcon; } bool getMinimizeToTray() { return fMinimizeToTray; } bool getMinimizeOnClose() { return fMinimizeOnClose; } int getDisplayUnit() { return nDisplayUnit; } @@ -82,6 +84,7 @@ class OptionsModel : public QAbstractListModel private: /* Qt-only settings */ + bool fHideTrayIcon; bool fMinimizeToTray; bool fMinimizeOnClose; QString language; @@ -101,6 +104,7 @@ class OptionsModel : public QAbstractListModel void privateSentAmountChanged(); void advancedPSUIChanged(bool); void coinControlFeaturesChanged(bool); + void hideTrayIconChanged(bool); }; #endif // BITCOIN_QT_OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 8aaa4fdf0a0d..08472a17cefa 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -189,6 +189,11 @@ void OverviewPage::handleTransactionClicked(const QModelIndex &index) Q_EMIT transactionClicked(filter->mapToSource(index)); } +void OverviewPage::handleOutOfSyncWarningClicks() +{ + Q_EMIT outOfSyncWarningClicked(); +} + OverviewPage::~OverviewPage() { if(!fLiteMode && !fMasterNode) disconnect(timer, SIGNAL(timeout()), this, SLOT(privateSendStatus())); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 19a45b68b6b3..db93662c3786 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -43,6 +43,7 @@ public Q_SLOTS: Q_SIGNALS: void transactionClicked(const QModelIndex &index); + void outOfSyncWarningClicked(); private: QTimer *timer; @@ -76,6 +77,7 @@ private Q_SLOTS: void handleTransactionClicked(const QModelIndex &index); void updateAlerts(const QString &warnings); void updateWatchOnlyLabels(bool showWatchOnly); + void handleOutOfSyncWarningClicks(); }; #endif // BITCOIN_QT_OVERVIEWPAGE_H diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index d1a8e384d989..b5c2047db5ff 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -32,7 +32,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: - return pLeft->dPingTime < pRight->dPingTime; + return pLeft->dMinPing < pRight->dMinPing; } return false; @@ -114,7 +114,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : clientModel(parent), timer(0) { - columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping Time"); + columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping"); priv = new PeerTablePriv(); // default to unsorted priv->sortColumn = -1; @@ -167,7 +167,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: - return GUIUtil::formatPingTime(rec->nodeStats.dPingTime); + return GUIUtil::formatPingTime(rec->nodeStats.dMinPing); } } else if (role == Qt::TextAlignmentRole) { if (index.column() == Ping) diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 65c42e3d7b9f..b2d86f1037fd 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -44,18 +44,21 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidg } // context menu actions + QAction *copyURIAction = new QAction(tr("Copy URI"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this); QAction *copyMessageAction = new QAction(tr("Copy message"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this); // context menu contextMenu = new QMenu(); + contextMenu->addAction(copyURIAction); contextMenu->addAction(copyLabelAction); contextMenu->addAction(copyMessageAction); contextMenu->addAction(copyAmountAction); // context menu signals connect(ui->recentRequestsView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint))); + connect(copyURIAction, SIGNAL(triggered()), this, SLOT(copyURI())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage())); connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); @@ -229,30 +232,50 @@ void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event) this->QDialog::keyPressEvent(event); } -// copy column of selected row to clipboard -void ReceiveCoinsDialog::copyColumnToClipboard(int column) +QModelIndex ReceiveCoinsDialog::selectedRow() { if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) - return; + return QModelIndex(); QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); if(selection.empty()) - return; + return QModelIndex(); // correct for selection mode ContiguousSelection QModelIndex firstIndex = selection.at(0); + return firstIndex; +} + +// copy column of selected row to clipboard +void ReceiveCoinsDialog::copyColumnToClipboard(int column) +{ + QModelIndex firstIndex = selectedRow(); + if (!firstIndex.isValid()) { + return; + } GUIUtil::setClipboard(model->getRecentRequestsTableModel()->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole).toString()); } // context menu void ReceiveCoinsDialog::showMenu(const QPoint &point) { - if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel()) - return; - QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows(); - if(selection.empty()) + if (!selectedRow().isValid()) { return; + } contextMenu->exec(QCursor::pos()); } +// context menu action: copy URI +void ReceiveCoinsDialog::copyURI() +{ + QModelIndex sel = selectedRow(); + if (!sel.isValid()) { + return; + } + + const RecentRequestsTableModel * const submodel = model->getRecentRequestsTableModel(); + const QString uri = GUIUtil::formatBitcoinURI(submodel->entry(sel.row()).recipient); + GUIUtil::setClipboard(uri); +} + // context menu action: copy label void ReceiveCoinsDialog::copyLabel() { diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 543854a2f45e..0fef8c47b933 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -60,6 +60,7 @@ public Q_SLOTS: QMenu *contextMenu; const PlatformStyle *platformStyle; + QModelIndex selectedRow(); void copyColumnToClipboard(int column); virtual void resizeEvent(QResizeEvent *event); @@ -71,6 +72,7 @@ private Q_SLOTS: void recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void updateDisplayUnit(); void showMenu(const QPoint &point); + void copyURI(); void copyLabel(); void copyMessage(); void copyAmount(); diff --git a/src/qt/res/css/crownium.css b/src/qt/res/css/crownium.css index b8f580ddd040..bf9a482ff0f4 100644 --- a/src/qt/res/css/crownium.css +++ b/src/qt/res/css/crownium.css @@ -1160,6 +1160,15 @@ margin-top:12px; margin-left:0px; /* CSS Voodoo - set to -66px to hide default transaction icons */ } +/* MODAL OVERLAY */ + +QWidget#bgWidget .QPushButton#warningIcon { +width:64px; +height:64px; +padding:5px; +background-color:transparent; +} + /* SEND DIALOG */ QDialog#SendCoinsDialog .QFrame#frameCoinControl { /* Coin Control Section */ diff --git a/src/qt/res/css/drkblue.css b/src/qt/res/css/drkblue.css index 5d61092e0879..8a232bb9ceb5 100644 --- a/src/qt/res/css/drkblue.css +++ b/src/qt/res/css/drkblue.css @@ -1136,6 +1136,15 @@ margin-top:12px; margin-left:0px; /* CSS Voodoo - set to -66px to hide default transaction icons */ } +/* MODAL OVERLAY */ + +QWidget#bgWidget .QPushButton#warningIcon { +width:64px; +height:64px; +padding:5px; +background-color:transparent; +} + /* SEND DIALOG */ QDialog#SendCoinsDialog .QFrame#frameCoinControl { /* Coin Control Section */ diff --git a/src/qt/res/css/light.css b/src/qt/res/css/light.css index ff9d3f862e7a..560f5448e0d8 100644 --- a/src/qt/res/css/light.css +++ b/src/qt/res/css/light.css @@ -1142,6 +1142,15 @@ margin-top:12px; margin-left:0px; /* CSS Voodoo - set to -66px to hide default transaction icons */ } +/* MODAL OVERLAY */ + +QWidget#bgWidget .QPushButton#warningIcon { +width:64px; +height:64px; +padding:5px; +background-color:transparent; +} + /* SEND DIALOG */ QDialog#SendCoinsDialog .QFrame#frameCoinControl { /* Coin Control Section */ diff --git a/src/qt/res/icons/crownium/transaction_abandoned.png b/src/qt/res/icons/crownium/transaction_abandoned.png new file mode 100644 index 000000000000..8ca6445c20f2 Binary files /dev/null and b/src/qt/res/icons/crownium/transaction_abandoned.png differ diff --git a/src/qt/res/icons/drkblue/transaction_abandoned.png b/src/qt/res/icons/drkblue/transaction_abandoned.png new file mode 100644 index 000000000000..8ca6445c20f2 Binary files /dev/null and b/src/qt/res/icons/drkblue/transaction_abandoned.png differ diff --git a/src/qt/res/icons/light/transaction_abandoned.png b/src/qt/res/icons/light/transaction_abandoned.png new file mode 100644 index 000000000000..8ca6445c20f2 Binary files /dev/null and b/src/qt/res/icons/light/transaction_abandoned.png differ diff --git a/src/qt/res/icons/trad/transaction_abandoned.png b/src/qt/res/icons/trad/transaction_abandoned.png new file mode 100644 index 000000000000..8ca6445c20f2 Binary files /dev/null and b/src/qt/res/icons/trad/transaction_abandoned.png differ diff --git a/src/qt/res/icons/warning.png b/src/qt/res/icons/warning.png new file mode 100644 index 000000000000..6bc5ac78952b Binary files /dev/null and b/src/qt/res/icons/warning.png differ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index e80e9b3e4a14..2e14e99ccadf 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -471,7 +471,7 @@ void RPCConsole::setClientModel(ClientModel *model) ui->clientVersion->setText(model->formatFullVersion()); ui->clientUserAgent->setText(model->formatSubVersion()); ui->clientName->setText(model->clientName()); - ui->buildDate->setText(model->formatBuildDate()); + ui->dataDir->setText(model->dataDir()); ui->startupTime->setText(model->formatClientStartupTime()); ui->networkName->setText(QString::fromStdString(Params().NetworkIDString())); @@ -888,6 +888,7 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected)); ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingWait)); + ui->peerMinPing->setText(GUIUtil::formatPingTime(stats->nodeStats.dMinPing)); ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString("%1").arg(QString::number(stats->nodeStats.nVersion))); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); @@ -974,21 +975,21 @@ void RPCConsole::banSelectedNode(int bantime) if (!clientModel || !g_connman) return; - // Get currently selected peer address - QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address).toString(); - // Find possible nodes, ban it and clear the selected node - std::string nStr = strNode.toStdString(); - std::string addr; - int port = 0; - SplitHostPort(nStr, port, addr); + if(cachedNodeid == -1) + return; - CNetAddr resolved; - if(!LookupHost(addr.c_str(), resolved, false)) + // Get currently selected peer address + int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid); + if(detailNodeRow < 0) return; - g_connman->Ban(resolved, BanReasonManuallyAdded, bantime); - clearSelectedNode(); - clientModel->getBanTableModel()->refresh(); + // Find possible nodes, ban it and clear the selected node + const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); + if(stats) { + g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime); + clearSelectedNode(); + clientModel->getBanTableModel()->refresh(); + } } void RPCConsole::unbanSelectedNode() diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 847fdb1cdef5..74ee76f33fb2 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -96,7 +96,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this); QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this); QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this); - QAction *clipboardPriorityAction = new QAction(tr("Copy priority"), this); QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this); QAction *clipboardChangeAction = new QAction(tr("Copy change"), this); connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity())); @@ -104,7 +103,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee())); connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee())); connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes())); - connect(clipboardPriorityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardPriority())); connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput())); connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange())); ui->labelCoinControlQuantity->addAction(clipboardQuantityAction); @@ -112,7 +110,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa ui->labelCoinControlFee->addAction(clipboardFeeAction); ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction); ui->labelCoinControlBytes->addAction(clipboardBytesAction); - ui->labelCoinControlPriority->addAction(clipboardPriorityAction); ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlChange->addAction(clipboardChangeAction); @@ -708,6 +705,9 @@ void SendCoinsDialog::updateGlobalFeeVariables() { nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value(); payTxFee = CFeeRate(0); + + // set nMinimumTotalFee to 0 to not accidentally pay a custom fee + CoinControlDialog::coinControl->nMinimumTotalFee = 0; } else { @@ -799,12 +799,6 @@ void SendCoinsDialog::coinControlClipboardBytes() GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, "")); } -// Coin Control: copy label "Priority" to clipboard -void SendCoinsDialog::coinControlClipboardPriority() -{ - GUIUtil::setClipboard(ui->labelCoinControlPriority->text()); -} - // Coin Control: copy label "Dust" to clipboard void SendCoinsDialog::coinControlClipboardLowOutput() { @@ -908,7 +902,7 @@ void SendCoinsDialog::coinControlUpdateLabels() ui->radioCustomAtLeast->setVisible(true); // only enable the feature if inputs are selected - ui->radioCustomAtLeast->setEnabled(CoinControlDialog::coinControl->HasSelected()); + ui->radioCustomAtLeast->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() &&CoinControlDialog::coinControl->HasSelected()); } else { diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index c561121d0a86..1d690aef1108 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -90,7 +90,6 @@ private Q_SLOTS: void coinControlClipboardFee(); void coinControlClipboardAfterFee(); void coinControlClipboardBytes(); - void coinControlClipboardPriority(); void coinControlClipboardLowOutput(); void coinControlClipboardChange(); void setMinimumFee(); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 85de3fa5bb2e..de445cc6e5a8 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -113,6 +113,11 @@ SplashScreen::~SplashScreen() void SplashScreen::slotFinish(QWidget *mainWin) { Q_UNUSED(mainWin); + + /* If the window is minimized, hide() will be ignored. */ + /* Make sure we de-minimize the splashscreen window before hiding */ + if (isMinimized()) + showNormal(); hide(); } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index b8d21851e57a..b1ba96a94428 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -45,7 +45,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) if (fOffline) { strTxStatus = tr("%1/offline").arg(nDepth); } else if (nDepth == 0) { - strTxStatus = tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))); + strTxStatus = tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : ""); } else if (nDepth < 6) { strTxStatus = tr("%1/unconfirmed").arg(nDepth); } else { @@ -265,6 +265,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
"; strHTML += "" + tr("Transaction ID") + ": " + TransactionRecord::formatSubTxId(wtx.GetHash(), rec->idx) + "
"; + strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.GetTotalSize()) + " bytes
"; // Message from normal dash:URI (dash:XyZ...?message=example) Q_FOREACH (const PAIRTYPE(std::string, std::string)& r, wtx.vOrderForm) diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 2a946e6ad848..d711111a0bb7 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -301,6 +301,8 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; + if (wtx.isAbandoned()) + status.status = TransactionStatus::Abandoned; } else if (status.depth < RecommendedNumConfirmations) { diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 06656f6d718f..90d426e97f26 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -33,6 +33,7 @@ class TransactionStatus Unconfirmed, /**< Not yet mined into a block **/ Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/ Conflicted, /**< Conflicts with other transaction or mempool **/ + Abandoned, /**< Abandoned from the wallet **/ /// Generated (mined) transactions Immature, /**< Mined but waiting for maturity */ MaturesWarning, /**< Transaction will likely not mature because no nodes have confirmed */ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 497e374f69c7..5ba8e02229e7 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -312,6 +312,9 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons case TransactionStatus::Unconfirmed: status = tr("Unconfirmed"); break; + case TransactionStatus::Abandoned: + status = tr("Abandoned"); + break; case TransactionStatus::Confirming: status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations); break; @@ -493,6 +496,8 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) return COLOR_TX_STATUS_OFFLINE; case TransactionStatus::Unconfirmed: return QIcon(":/icons/" + theme + "/transaction_0"); + case TransactionStatus::Abandoned: + return QIcon(":/icons/" + theme + "/transaction_abandoned"); case TransactionStatus::Confirming: switch(wtx->status.depth) { @@ -598,6 +603,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case Qt::TextAlignmentRole: return column_alignments[index.column()]; case Qt::ForegroundRole: + // Use the "danger" color for abandoned transactions + if(rec->status.status == TransactionStatus::Abandoned) + { + return COLOR_TX_STATUS_DANGER; + } // Non-confirmed (but not immature) as transactions are grey if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 27e6be30a2ca..f0926ddb6ea8 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -41,7 +41,7 @@ static const char* PERSISTENCE_DATE_FORMAT = "yyyy-MM-dd"; TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) : QWidget(parent), model(0), transactionProxyModel(0), - transactionView(0) + transactionView(0), abandonAction(0) { QSettings settings; // Build filter row @@ -151,6 +151,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa transactionView = view; // Actions + abandonAction = new QAction(tr("Abandon transaction"), this); QAction *copyAddressAction = new QAction(tr("Copy address"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this); @@ -167,8 +168,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa contextMenu->addAction(copyTxIDAction); contextMenu->addAction(copyTxHexAction); contextMenu->addAction(copyTxPlainText); - contextMenu->addAction(editLabelAction); contextMenu->addAction(showDetailsAction); + contextMenu->addSeparator(); + contextMenu->addAction(abandonAction); + contextMenu->addAction(editLabelAction); mapperThirdPartyTxUrls = new QSignalMapper(this); @@ -185,6 +188,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(view, SIGNAL(clicked(QModelIndex)), this, SLOT(computeSum())); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); + connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx())); connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount())); @@ -399,12 +403,37 @@ void TransactionView::exportClicked() void TransactionView::contextualMenu(const QPoint &point) { QModelIndex index = transactionView->indexAt(point); + QModelIndexList selection = transactionView->selectionModel()->selectedRows(0); + + // check if transaction can be abandoned, disable context menu action in case it doesn't + uint256 hash; + hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString()); + abandonAction->setEnabled(model->transactionCanBeAbandoned(hash)); + if(index.isValid()) { contextMenu->exec(QCursor::pos()); } } +void TransactionView::abandonTx() +{ + if(!transactionView || !transactionView->selectionModel()) + return; + QModelIndexList selection = transactionView->selectionModel()->selectedRows(0); + + // get the hash from the TxHashRole (QVariant / QString) + uint256 hash; + QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString(); + hash.SetHex(hashQStr.toStdString()); + + // Abandon the wallet transaction over the walletModel + model->abandonTransaction(hash); + + // Update the table + model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); +} + void TransactionView::copyAddress() { GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole); diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 5dd896314294..2a2ad4912fbf 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -75,6 +75,7 @@ class TransactionView : public QWidget QFrame *dateRangeWidget; QDateTimeEdit *dateFrom; QDateTimeEdit *dateTo; + QAction *abandonAction; QWidget *createDateRangeWidget(); @@ -97,6 +98,7 @@ private Q_SLOTS: void copyTxPlainText(); void openThirdPartyTxUrl(QString url); void updateWatchOnlyColumn(bool fHaveWatchOnly); + void abandonTx(); Q_SIGNALS: void doubleClicked(const QModelIndex&); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 0ada6bb8a1d2..fcc34683f824 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -57,6 +57,8 @@ bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel) // Ensure a walletView is able to show the main window connect(walletView, SIGNAL(showNormalIfMinimized()), gui, SLOT(showNormalIfMinimized())); + connect(walletView, SIGNAL(outOfSyncWarningClicked()), this, SLOT(outOfSyncWarningClicked())); + return true; } @@ -209,3 +211,7 @@ WalletView *WalletFrame::currentWalletView() return qobject_cast(walletStack->currentWidget()); } +void WalletFrame::outOfSyncWarningClicked() +{ + Q_EMIT requestedSyncWarningInfo(); +} diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h index 49a7a61671ea..2325c034721f 100644 --- a/src/qt/walletframe.h +++ b/src/qt/walletframe.h @@ -38,6 +38,10 @@ class WalletFrame : public QFrame void showOutOfSyncWarning(bool fShow); +Q_SIGNALS: + /** Notify that the user has requested more information about the out-of-sync warning */ + void requestedSyncWarningInfo(); + private: QStackedWidget *walletStack; BitcoinGUI *gui; @@ -82,6 +86,8 @@ public Q_SLOTS: void usedSendingAddresses(); /** Show used receiving addresses */ void usedReceivingAddresses(); + /** Pass on signal over requested out-of-sync-warning information */ + void outOfSyncWarningClicked(); }; #endif // BITCOIN_QT_WALLETFRAME_H diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 16275110b382..167f07b913fa 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -648,7 +648,7 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); vOutputs.push_back(out); } } @@ -675,7 +675,7 @@ void WalletModel::listCoins(std::map >& mapCoins) if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); if (outpoint.n < out.tx->vout.size() && wallet->IsMine(out.tx->vout[outpoint.n]) == ISMINE_SPENDABLE) vCoins.push_back(out); } @@ -687,7 +687,7 @@ void WalletModel::listCoins(std::map >& mapCoins) while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) { if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true); + cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true); } CTxDestination address; @@ -745,6 +745,21 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t return wallet->AddDestData(dest, key, sRequest); } +bool WalletModel::transactionCanBeAbandoned(uint256 hash) const +{ + LOCK2(cs_main, wallet->cs_wallet); + const CWalletTx *wtx = wallet->GetWalletTx(hash); + if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool()) + return false; + return true; +} + +bool WalletModel::abandonTransaction(uint256 hash) const +{ + LOCK2(cs_main, wallet->cs_wallet); + return wallet->AbandonTransaction(hash); +} + bool WalletModel::hdEnabled() const { return wallet->IsHDEnabled(); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 055a94fbbc2c..c4cd012534cd 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -207,6 +207,9 @@ class WalletModel : public QObject void loadReceiveRequests(std::vector& vReceiveRequests); bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest); + bool transactionCanBeAbandoned(uint256 hash) const; + bool abandonTransaction(uint256 hash) const; + bool hdEnabled() const; private: diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index e36faf9e8c02..b42573cc734f 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -89,6 +89,7 @@ WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent): // Clicking on a transaction on the overview pre-selects the transaction on the transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); + connect(overviewPage, SIGNAL(outOfSyncWarningClicked()), this, SLOT(requestedSyncWarningInfo())); // Double-clicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); @@ -375,6 +376,11 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog->setValue(nProgress); } +void WalletView::requestedSyncWarningInfo() +{ + Q_EMIT outOfSyncWarningClicked(); +} + /** Update wallet with the sum of the selected transactions */ void WalletView::trxAmount(QString amount) { diff --git a/src/qt/walletview.h b/src/qt/walletview.h index b28b30502974..98c4195c1eaa 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -118,9 +118,12 @@ public Q_SLOTS: /** Show progress dialog e.g. for rescan */ void showProgress(const QString &title, int nProgress); + /** User has requested more information about the out of sync state */ + void requestedSyncWarningInfo(); + + /** Update selected DASH amount from transactionview */ void trxAmount(QString amount); - Q_SIGNALS: /** Signal that we want to show the main window */ void showNormalIfMinimized(); @@ -132,6 +135,8 @@ public Q_SLOTS: void hdEnabledStatusChanged(int hdEnabled); /** Notify that a new transaction appeared */ void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label); + /** Notify that the out of sync warning icon has been pressed */ + void outOfSyncWarningClicked(); }; #endif // BITCOIN_QT_WALLETVIEW_H diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 87066ebcf072..f2dff3e2bb9c 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -87,9 +87,9 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) " \"bytesrecv\": n, (numeric) The total bytes received\n" " \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n" " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" - " \"pingtime\": n, (numeric) ping time\n" - " \"minping\": n, (numeric) minimum observed ping time\n" - " \"pingwait\": n, (numeric) ping wait\n" + " \"pingtime\": n, (numeric) ping time (if available)\n" + " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n" + " \"pingwait\": n, (numeric) ping wait (if non-zero)\n" " \"version\": v, (numeric) The peer version, such as 7001\n" " \"subver\": \"/Dash Core:x.x.x/\", (string) The string version\n" " \"inbound\": true|false, (boolean) Inbound (true) or Outbound (false)\n" @@ -141,8 +141,10 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("bytesrecv", stats.nRecvBytes)); obj.push_back(Pair("conntime", stats.nTimeConnected)); obj.push_back(Pair("timeoffset", stats.nTimeOffset)); - obj.push_back(Pair("pingtime", stats.dPingTime)); - obj.push_back(Pair("minping", stats.dPingMin)); + if (stats.dPingTime > 0.0) + obj.push_back(Pair("pingtime", stats.dPingTime)); + if (stats.dMinPing < std::numeric_limits::max()/1e6) + obj.push_back(Pair("minping", stats.dMinPing)); if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); obj.push_back(Pair("version", stats.nVersion)); diff --git a/src/ui_interface.h b/src/ui_interface.h index 6d5b29cfa051..8d6491e72982 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -76,6 +76,9 @@ class CClientUIInterface /** Show message box. */ boost::signals2::signal > ThreadSafeMessageBox; + /** If possible, ask the user a question. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */ + boost::signals2::signal > ThreadSafeQuestion; + /** Progress message during initialization. */ boost::signals2::signal InitMessage; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1e079be53c79..53fd544913b4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2555,7 +2555,8 @@ UniValue listunspent(const UniValue& params, bool fHelp) " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n (numeric) The number of confirmations\n" " \"ps_rounds\" : n (numeric) The number of PS round\n" - " \"spendable\" : true|false (boolean) True if spendable\n" + " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" + " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" " }\n" " ,...\n" "]\n" @@ -2633,6 +2634,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) entry.push_back(Pair("confirmations",out.nDepth)); entry.push_back(Pair("ps_rounds", pwalletMain->GetInputPrivateSendRounds(CTxIn(out.tx->GetHash(), out.i)))); entry.push_back(Pair("spendable", out.fSpendable)); + entry.push_back(Pair("solvable", out.fSolvable)); results.push_back(entry); } @@ -2654,6 +2656,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) "Note that all existing inputs must have their previous output transaction be in the wallet.\n" "Note that all inputs selected must be of standard form and P2SH scripts must be" "in the wallet using importaddress or addmultisigaddress (to calculate fees).\n" + "You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n" "Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 719fd3ed122d..00c14965ca27 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -48,7 +48,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa wtx->fDebitCached = true; wtx->nDebitCached = 1; } - COutput output(wtx, nInput, nAge, true); + COutput output(wtx, nInput, nAge, true, true); vCoins.push_back(output); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c35631536004..8db058f8e119 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2422,10 +2422,11 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && (!IsLockedCoin((*it).first, i) || nCoinType == ONLY_1000) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && - (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected((*it).first, i))) + (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || - (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO))); + (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); } } } @@ -2758,16 +2759,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC // Add new txins (keeping original txin scriptSig/order) BOOST_FOREACH(const CTxIn& txin, wtx.vin) { - bool found = false; - BOOST_FOREACH(const CTxIn& origTxIn, tx.vin) - { - if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n) - { - found = true; - break; - } - } - if (!found) + if (!coinControl.IsSelected(txin.prevout)) tx.vin.push_back(txin); } @@ -3058,7 +3050,7 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) int nDepth = pcoin->GetDepthInMainChain(false); for (unsigned int i = 0; i < pcoin->vout.size(); i++) { - COutput out = COutput(pcoin, i, nDepth, true); + COutput out = COutput(pcoin, i, nDepth, true, true); CTxIn txin = CTxIn(out.tx->GetHash(), out.i); if(out.tx->vout[out.i].nValue != nInputAmount) continue; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 36252f109e4f..6c9f77393f1f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -483,10 +483,11 @@ class COutput int i; int nDepth; bool fSpendable; + bool fSolvable; - COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn) + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn) { - tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; + tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; } //Used with Darksend. Will return largest nondenom, then denominations, then very small inputs