9 #include "ui_coincontroldialog.h"
22 #include <boost/assign/list_of.hpp>
24 #include <QApplication>
27 #include <QDialogButtonBox>
32 #include <QTreeWidget>
39 int column = treeWidget()->sortColumn();
41 return data(column, Qt::UserRole).toLongLong() < other.data(column, Qt::UserRole).toLongLong();
55 QAction* copyAddressAction =
new QAction(tr(
"Copy address"),
this);
56 QAction* copyLabelAction =
new QAction(tr(
"Copy label"),
this);
57 QAction* copyAmountAction =
new QAction(tr(
"Copy amount"),
this);
59 lockAction =
new QAction(tr(
"Lock unspent"),
this);
73 connect(
ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(
showMenu(QPoint)));
74 connect(copyAddressAction, SIGNAL(triggered()),
this, SLOT(
copyAddress()));
75 connect(copyLabelAction, SIGNAL(triggered()),
this, SLOT(
copyLabel()));
76 connect(copyAmountAction, SIGNAL(triggered()),
this, SLOT(
copyAmount()));
82 QAction* clipboardQuantityAction =
new QAction(tr(
"Copy quantity"),
this);
83 QAction* clipboardAmountAction =
new QAction(tr(
"Copy amount"),
this);
84 QAction* clipboardFeeAction =
new QAction(tr(
"Copy fee"),
this);
85 QAction* clipboardAfterFeeAction =
new QAction(tr(
"Copy after fee"),
this);
86 QAction* clipboardBytesAction =
new QAction(tr(
"Copy bytes"),
this);
87 QAction* clipboardPriorityAction =
new QAction(tr(
"Copy priority"),
this);
88 QAction* clipboardLowOutputAction =
new QAction(tr(
"Copy dust"),
this);
89 QAction* clipboardChangeAction =
new QAction(tr(
"Copy change"),
this);
91 connect(clipboardQuantityAction, SIGNAL(triggered()),
this, SLOT(
clipboardQuantity()));
92 connect(clipboardAmountAction, SIGNAL(triggered()),
this, SLOT(
clipboardAmount()));
93 connect(clipboardFeeAction, SIGNAL(triggered()),
this, SLOT(
clipboardFee()));
94 connect(clipboardAfterFeeAction, SIGNAL(triggered()),
this, SLOT(
clipboardAfterFee()));
95 connect(clipboardBytesAction, SIGNAL(triggered()),
this, SLOT(
clipboardBytes()));
96 connect(clipboardPriorityAction, SIGNAL(triggered()),
this, SLOT(
clipboardPriority()));
97 connect(clipboardLowOutputAction, SIGNAL(triggered()),
this, SLOT(
clipboardLowOutput()));
98 connect(clipboardChangeAction, SIGNAL(triggered()),
this, SLOT(
clipboardChange()));
100 ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
101 ui->labelCoinControlAmount->addAction(clipboardAmountAction);
102 ui->labelCoinControlFee->addAction(clipboardFeeAction);
103 ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
104 ui->labelCoinControlBytes->addAction(clipboardBytesAction);
105 ui->labelCoinControlPriority->addAction(clipboardPriorityAction);
106 ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
107 ui->labelCoinControlChange->addAction(clipboardChangeAction);
110 connect(
ui->radioTreeMode, SIGNAL(toggled(
bool)),
this, SLOT(
radioTreeMode(
bool)));
111 connect(
ui->radioListMode, SIGNAL(toggled(
bool)),
this, SLOT(
radioListMode(
bool)));
114 connect(
ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,
int)),
this, SLOT(
viewItemChanged(QTreeWidgetItem*,
int)));
117 ui->treeWidget->header()->setSectionsClickable(
true);
121 connect(
ui->buttonBox, SIGNAL(clicked(QAbstractButton*)),
this, SLOT(
buttonBoxClicked(QAbstractButton*)));
148 if (settings.contains(
"nCoinControlMode") && !settings.value(
"nCoinControlMode").toBool())
149 ui->radioTreeMode->click();
150 if (settings.contains(
"nCoinControlSortColumn") && settings.contains(
"nCoinControlSortOrder"))
151 sortView(settings.value(
"nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value(
"nCoinControlSortOrder").toInt()));
157 settings.setValue(
"nCoinControlMode",
ui->radioListMode->isChecked());
158 settings.setValue(
"nCoinControlSortColumn",
sortColumn);
159 settings.setValue(
"nCoinControlSortOrder", (
int)
sortOrder);
179 if (
ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
180 done(QDialog::Accepted);
189 ui->treeWidget->setEnabled(
false);
190 for (
int i = 0; i <
ui->treeWidget->topLevelItemCount(); i++)
193 ui->treeWidget->setEnabled(
true);
196 ui->pushButtonSelectAll->setText(tr(
"Select all"));
198 ui->pushButtonSelectAll->setText(tr(
"Unselect all"));
208 QTreeWidgetItem* item;
210 if (
ui->radioListMode->isChecked()) {
211 ui->treeWidget->setEnabled(
false);
212 for (
int i = 0; i <
ui->treeWidget->topLevelItemCount(); i++) {
213 item =
ui->treeWidget->topLevelItem(i);
221 item->setDisabled(
false);
225 item->setDisabled(
true);
230 ui->treeWidget->setEnabled(
true);
235 msgBox.setObjectName(
"lockMessageBox");
237 msgBox.setText(tr(
"Please switch to \"List mode\" to use this function."));
245 QTreeWidgetItem* item =
ui->treeWidget->itemAt(point);
340 GUIUtil::setClipboard(
ui->labelCoinControlFee->text().left(
ui->labelCoinControlFee->text().indexOf(
" ")).replace(
"~",
""));
346 GUIUtil::setClipboard(
ui->labelCoinControlAfterFee->text().left(
ui->labelCoinControlAfterFee->text().indexOf(
" ")).replace(
"~",
""));
370 GUIUtil::setClipboard(
ui->labelCoinControlChange->text().left(
ui->labelCoinControlChange->text().indexOf(
" ")).replace(
"~",
""));
378 ui->treeWidget->sortItems(column, order);
390 sortOrder = ((
sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
403 if (checked &&
model)
410 if (checked &&
model)
423 else if (item->isDisabled())
429 if (
ui->treeWidget->isEnabled()){
447 double dPriorityMedium = mempoolEstimatePriority;
449 if (dPriorityMedium <= 0)
452 if (dPriority / 1000000 > dPriorityMedium)
453 return tr(
"highest");
454 else if (dPriority / 100000 > dPriorityMedium)
456 else if (dPriority / 10000 > dPriorityMedium)
458 else if (dPriority / 1000 > dPriorityMedium)
459 return tr(
"medium-high");
460 else if (dPriority > dPriorityMedium)
462 else if (dPriority * 10 > dPriorityMedium)
463 return tr(
"low-medium");
464 else if (dPriority * 100 > dPriorityMedium)
466 else if (dPriority * 1000 > dPriorityMedium)
475 std::vector<COutPoint> vOutpts;
477 if (vOutpts.size() > 0) {
478 ui->labelLocked->setText(tr(
"(%1 locked)").arg(vOutpts.size()));
479 ui->labelLocked->setVisible(
true);
481 ui->labelLocked->setVisible(
false);
487 if (this->parentWidget() ==
nullptr) {
492 std::vector<COutPoint> vCoinControl;
493 std::vector<COutput> vOutputs;
498 unsigned int nQuantity = 0;
499 for (
const COutput& out : vOutputs) {
527 nPayAmount += amount;
530 CTxOut txout(amount, (
CScript)std::vector<unsigned char>(24, 0));
531 txDummy.
vout.push_back(txout);
537 QString sPriorityLabel = tr(
"none");
542 unsigned int nBytes = 0;
543 unsigned int nBytesInputs = 0;
544 double dPriority = 0;
545 double dPriorityInputs = 0;
546 unsigned int nQuantity = 0;
547 int nQuantityUncompressed = 0;
548 bool fAllowFree =
false;
550 std::vector<COutPoint> vCoinControl;
551 std::vector<COutput> vOutputs;
555 for (
const COutput& out : vOutputs) {
578 CKeyID* keyid = boost::get<CKeyID>(&address);
582 nQuantityUncompressed++;
596 dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29));
605 double dPriorityNeeded = mempoolEstimatePriority;
606 if (dPriorityNeeded <= 0)
608 fAllowFree = (dPriority >= dPriorityNeeded);
611 if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
614 if (nPayAmount > 0) {
615 nChange = nAmount - nPayFee - nPayAmount;
618 if (nChange > 0 && nChange < CENT) {
619 CTxOut txout(nChange, (
CScript)std::vector<unsigned char>(24, 0));
631 nAfterFee = nAmount - nPayFee;
641 QLabel* l1 = dialog->findChild<QLabel*>(
"labelCoinControlQuantity");
642 QLabel* l2 = dialog->findChild<QLabel*>(
"labelCoinControlAmount");
643 QLabel* l3 = dialog->findChild<QLabel*>(
"labelCoinControlFee");
644 QLabel* l4 = dialog->findChild<QLabel*>(
"labelCoinControlAfterFee");
645 QLabel* l5 = dialog->findChild<QLabel*>(
"labelCoinControlBytes");
646 QLabel* l6 = dialog->findChild<QLabel*>(
"labelCoinControlPriority");
647 QLabel* l7 = dialog->findChild<QLabel*>(
"labelCoinControlLowOutput");
648 QLabel* l8 = dialog->findChild<QLabel*>(
"labelCoinControlChange");
651 dialog->findChild<QLabel*>(
"labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
652 dialog->findChild<QLabel*>(
"labelCoinControlLowOutput")->setEnabled(nPayAmount > 0);
653 dialog->findChild<QLabel*>(
"labelCoinControlChangeText")->setEnabled(nPayAmount > 0);
654 dialog->findChild<QLabel*>(
"labelCoinControlChange")->setEnabled(nPayAmount > 0);
657 l1->setText(QString::number(nQuantity));
661 l5->setText(((nBytes > 0) ?
"~" :
"") + QString::number(nBytes));
662 l6->setText(sPriorityLabel);
663 l7->setText(fDust ? tr(
"yes") : tr(
"no"));
666 l3->setText(
"~" + l3->text());
667 l4->setText(
"~" + l4->text());
669 l8->setText(
"~" + l8->text());
673 l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ?
"color:red;" :
"");
674 l6->setStyleSheet((dPriority > 0 && !fAllowFree) ?
"color:red;" :
"");
675 l7->setStyleSheet((fDust) ?
"color:red;" :
"");
678 QString toolTip1 = tr(
"This label turns red, if the transaction size is greater than 1000 bytes.") +
"<br /><br />";
680 toolTip1 += tr(
"Can vary +/- 1 byte per input.");
682 QString toolTip2 = tr(
"Transactions with higher priority are more likely to get included into a block.") +
"<br /><br />";
683 toolTip2 += tr(
"This label turns red, if the priority is smaller than \"medium\".") +
"<br /><br />";
694 QString toolTip4 = tr(
"Can vary +/- %1 duff(s) per input.").arg(dFeeVary);
696 l3->setToolTip(toolTip4);
697 l4->setToolTip(toolTip4);
698 l5->setToolTip(toolTip1);
699 l6->setToolTip(toolTip2);
700 l7->setToolTip(toolTip3);
701 l8->setToolTip(toolTip4);
702 dialog->findChild<QLabel*>(
"labelCoinControlFeeText")->setToolTip(l3->toolTip());
703 dialog->findChild<QLabel*>(
"labelCoinControlAfterFeeText")->setToolTip(l4->toolTip());
704 dialog->findChild<QLabel*>(
"labelCoinControlBytesText")->setToolTip(l5->toolTip());
705 dialog->findChild<QLabel*>(
"labelCoinControlPriorityText")->setToolTip(l6->toolTip());
706 dialog->findChild<QLabel*>(
"labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
707 dialog->findChild<QLabel*>(
"labelCoinControlChangeText")->setToolTip(l8->toolTip());
710 QLabel* label = dialog->findChild<QLabel*>(
"labelCoinControlInsuffFunds");
712 label->setVisible(nChange < 0);
720 bool treeMode =
ui->radioTreeMode->isChecked();
722 ui->treeWidget->clear();
723 ui->treeWidget->setEnabled(
false);
724 ui->treeWidget->setAlternatingRowColors(!treeMode);
725 QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
726 QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
731 std::map<QString, std::vector<COutput>> mapCoins;
734 for (
PAIRTYPE(QString, std::vector<COutput>) coins : mapCoins) {
737 QString sWalletAddress = coins.first;
739 if (sWalletLabel.isEmpty())
740 sWalletLabel = tr(
"(no label)");
744 ui->treeWidget->addTopLevelItem(itemWalletAddress);
746 itemWalletAddress->setFlags(flgTristate);
751 itemWalletAddress->setToolTip(
COLUMN_LABEL, sWalletLabel);
759 double dPrioritySum = 0;
762 for(
const COutput& out: coins.second) {
773 itemOutput->setFlags(flgCheckbox);
778 QString sAddress =
"";
780 sAddress = QString::fromStdString(
CBitcoinAddress(outputAddress).ToString());
783 if (!treeMode || (!(sAddress == sWalletAddress)))
789 CKeyID* keyid = boost::get<CKeyID>(&outputAddress);
795 if (!(sAddress == sWalletAddress))
798 itemOutput->setToolTip(
COLUMN_LABEL, tr(
"change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
800 }
else if (!treeMode) {
802 if (sLabel.isEmpty())
803 sLabel = tr(
"(no label)");
824 itemOutput->setData(
COLUMN_PRIORITY, Qt::UserRole, QVariant((qlonglong) dPriority));
826 nInputSum += nInputSize;
839 itemOutput->setDisabled(
true);
850 dPrioritySum = dPrioritySum / (nInputSum + 78);
851 itemWalletAddress->setText(
COLUMN_CHECKBOX,
"(" + QString::number(nChildren) +
")");
854 itemWalletAddress->setData(
COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) nSum));
856 itemWalletAddress->setData(
COLUMN_PRIORITY, Qt::UserRole, QVariant((qlonglong) dPrioritySum));
862 for (
int i = 0; i <
ui->treeWidget->topLevelItemCount(); i++)
863 if (
ui->treeWidget->topLevelItem(i)->checkState(
COLUMN_CHECKBOX) == Qt::PartiallyChecked)
864 ui->treeWidget->topLevelItem(i)->setExpanded(
true);
869 ui->treeWidget->setEnabled(
true);