Initial Vexta Core v0.1.0 release

This commit is contained in:
Capri 2026-07-15 14:07:28 +00:00
commit 25c454329d
2481 changed files with 689289 additions and 0 deletions

124
src/qt/README.md Normal file
View file

@ -0,0 +1,124 @@
This directory contains the source code for the DigiByte Core graphical user interface (GUI). It uses the [Qt](https://www1.qt.io/developers/) cross-platform framework.
The current precise version for Qt 5 is specified in [qt.mk](/depends/packages/qt.mk).
## Compile and run
See build instructions: [Unix](/doc/build-unix.md), [macOS](/doc/build-osx.md), [Windows](/doc/build-windows.md), [FreeBSD](/doc/build-freebsd.md), [NetBSD](/doc/build-netbsd.md), [OpenBSD](/doc/build-openbsd.md)
When following your systems build instructions, make sure to install the `Qt` dependencies.
To run:
```sh
./src/qt/vexta-qt
```
## Files and Directories
#### forms/
- A directory that contains [Designer UI](https://doc.qt.io/qt-5.9/designer-using-a-ui-file.html) files. These files specify the characteristics of form elements in XML. Qt UI files can be edited with [Qt Creator](#using-qt-creator-as-ide) or using any text editor.
#### locale/
- Contains translations. They are periodically updated and an effort is made to support as many languages as possible. The process of contributing translations is described in [doc/translation_process.md](/doc/translation_process.md).
#### res/
- Contains graphical resources used to enhance the UI experience.
#### test/
- Functional tests used to ensure proper functionality of the GUI. Significant changes to the GUI code normally require new or updated tests.
#### digibytegui.(h/cpp)
- Represents the main window of the DigiByte UI.
#### \*model.(h/cpp)
- The model. When it has a corresponding controller, it generally inherits from [QAbstractTableModel](https://doc.qt.io/qt-5/qabstracttablemodel.html). Models that are used by controllers as helpers inherit from other Qt classes like [QValidator](https://doc.qt.io/qt-5/qvalidator.html).
- ClientModel is used by the main application `digibytegui` and several models like `peertablemodel`.
#### \*page.(h/cpp)
- A controller. `:NAMEpage.cpp` generally includes `:NAMEmodel.h` and `forms/:NAME.page.ui` with a similar `:NAME`.
#### \*dialog.(h/cpp)
- Various dialogs, e.g. to open a URL. Inherit from [QDialog](https://doc.qt.io/qt-5/qdialog.html).
#### paymentserver.(h/cpp)
- (Deprecated) Used to process BIP21 payment URI requests. Also handles URI-based application switching (e.g. when following a digibyte:... link from a browser).
#### walletview.(h/cpp)
- Represents the view to a single wallet.
#### Other .h/cpp files
* UI elements like DigiByteAmountField, which inherit from QWidget.
* `digibytestrings.cpp`: automatically generated
* `digibyteunits.(h/cpp)`: DGB / mDGB / etc. handling
* `callback.h`
* `guiconstants.h`: UI colors, app name, etc.
* `guiutil.h`: several helper functions
* `macdockiconhandler.(h/mm)`: macOS dock icon handler
* `macnotificationhandler.(h/mm)`: display notifications in macOS
## Contribute
See [CONTRIBUTING.md](/CONTRIBUTING.md) for general guidelines.
**Note:** Do not change `local/digibyte_en.ts`. It is updated [automatically](/doc/translation_process.md#writing-code-with-translations).
## Using Qt Creator as an IDE
[Qt Creator](https://www.qt.io/product/development-tools) is a powerful tool which packages a UI designer tool (Qt Designer) and a C++ IDE into one application. This is especially useful if you want to change the UI layout.
#### Download Qt Creator
On Unix and macOS, Qt Creator can be installed through your package manager. Alternatively, you can download a binary from the [Qt Website](https://www.qt.io/download/).
**Note:** If installing from a binary grabbed from the Qt Website: During the installation process, uncheck everything except for `Qt Creator`.
##### macOS
```sh
brew install qt-creator
```
##### Ubuntu & Debian
```sh
sudo apt-get install qtcreator
```
#### Setup Qt Creator
1. Make sure you've installed all dependencies specified in your systems build instructions
2. Follow the compile instructions for your system, run `./configure` with the `--enable-debug` flag
3. Start Qt Creator. At the start page, do: `New` -> `Import Project` -> `Import Existing Project`
4. Enter `vexta-qt` as the Project Name and enter the absolute path to `src/qt` as Location
5. Check over the file selection, you may need to select the `forms` directory (necessary if you intend to edit *.ui files)
6. Confirm the `Summary` page
7. In the `Projects` tab, select `Manage Kits...`
**macOS**
- Under `Kits`: select the default "Desktop" kit
- Under `Compilers`: select `"Clang (x86 64bit in /usr/bin)"`
- Under `Debuggers`: select `"LLDB"` as debugger (you might need to set the path to your LLDB installation)
**Ubuntu & Debian**
Note: Some of these options may already be set
- Under `Kits`: select the default "Desktop" kit
- Under `Compilers`: select `"GCC (x86 64bit in /usr/bin)"`
- Under `Debuggers`: select `"GDB"` as debugger
8. While in the `Projects` tab, ensure that you have the `vexta-qt` executable specified under `Run`
- If the executable is not specified: click `"Choose..."`, navigate to `src/qt`, and select `vexta-qt`
9. You're all set! Start developing, building, and debugging the DigiByte Core GUI

326
src/qt/addressbookpage.cpp Normal file
View file

@ -0,0 +1,326 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <qt/addressbookpage.h>
#include <qt/forms/ui_addressbookpage.h>
#include <qt/addresstablemodel.h>
#include <qt/csvmodelwriter.h>
#include <qt/editaddressdialog.h>
#include <qt/guiutil.h>
#include <qt/platformstyle.h>
#include <QIcon>
#include <QMenu>
#include <QMessageBox>
#include <QSortFilterProxyModel>
class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel
{
const QString m_type;
public:
AddressBookSortFilterProxyModel(const QString& type, QObject* parent)
: QSortFilterProxyModel(parent)
, m_type(type)
{
setDynamicSortFilter(true);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
protected:
bool filterAcceptsRow(int row, const QModelIndex& parent) const override
{
auto model = sourceModel();
auto label = model->index(row, AddressTableModel::Label, parent);
if (model->data(label, AddressTableModel::TypeRole).toString() != m_type) {
return false;
}
auto address = model->index(row, AddressTableModel::Address, parent);
if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
filterRegExp().indexIn(model->data(label).toString()) < 0) {
return false;
}
return true;
}
};
AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::AddressBookPage),
model(nullptr),
mode(_mode),
tab(_tab)
{
ui->setupUi(this);
if (!platformStyle->getImagesOnButtons()) {
ui->newAddress->setIcon(QIcon());
ui->copyAddress->setIcon(QIcon());
ui->deleteAddress->setIcon(QIcon());
ui->exportButton->setIcon(QIcon());
} else {
ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
ui->copyAddress->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy"));
ui->deleteAddress->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
ui->exportButton->setIcon(platformStyle->SingleColorIcon(":/icons/export"));
}
switch(mode)
{
case ForSelection:
switch(tab)
{
case SendingTab: setWindowTitle(tr("Choose the address to send coins to")); break;
case ReceivingTab: setWindowTitle(tr("Choose the address to receive coins with")); break;
}
connect(ui->tableView, &QTableView::doubleClicked, this, &QDialog::accept);
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableView->setFocus();
ui->closeButton->setText(tr("C&hoose"));
ui->exportButton->hide();
break;
case ForEditing:
switch(tab)
{
case SendingTab: setWindowTitle(tr("Sending addresses")); break;
case ReceivingTab: setWindowTitle(tr("Receiving addresses")); break;
}
break;
}
switch(tab)
{
case SendingTab:
ui->labelExplanation->setText(tr("These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins."));
ui->deleteAddress->setVisible(true);
ui->newAddress->setVisible(true);
break;
case ReceivingTab:
ui->labelExplanation->setText(tr("These are your Vexta addresses for receiving payments. Use the 'Create new receiving address' button in the receive tab to create new addresses.\nSigning is only possible with addresses of the type 'legacy'."));
ui->deleteAddress->setVisible(false);
ui->newAddress->setVisible(false);
break;
}
// Build context menu
contextMenu = new QMenu(this);
contextMenu->addAction(tr("&Copy Address"), this, &AddressBookPage::on_copyAddress_clicked);
contextMenu->addAction(tr("Copy &Label"), this, &AddressBookPage::onCopyLabelAction);
contextMenu->addAction(tr("&Edit"), this, &AddressBookPage::onEditAction);
if (tab == SendingTab) {
contextMenu->addAction(tr("&Delete"), this, &AddressBookPage::on_deleteAddress_clicked);
}
connect(ui->tableView, &QWidget::customContextMenuRequested, this, &AddressBookPage::contextualMenu);
connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);
GUIUtil::handleCloseWindowShortcut(this);
}
AddressBookPage::~AddressBookPage()
{
delete ui;
}
void AddressBookPage::setModel(AddressTableModel *_model)
{
this->model = _model;
if(!_model)
return;
auto type = tab == ReceivingTab ? AddressTableModel::Receive : AddressTableModel::Send;
proxyModel = new AddressBookSortFilterProxyModel(type, this);
proxyModel->setSourceModel(_model);
connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterWildcard);
ui->tableView->setModel(proxyModel);
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
// Set column widths
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Label, QHeaderView::Stretch);
ui->tableView->horizontalHeader()->setSectionResizeMode(AddressTableModel::Address, QHeaderView::ResizeToContents);
connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &AddressBookPage::selectionChanged);
// Select row for newly created address
connect(_model, &AddressTableModel::rowsInserted, this, &AddressBookPage::selectNewAddress);
selectionChanged();
}
void AddressBookPage::on_copyAddress_clicked()
{
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
}
void AddressBookPage::onCopyLabelAction()
{
GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
}
void AddressBookPage::onEditAction()
{
if(!model)
return;
if(!ui->tableView->selectionModel())
return;
QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
if(indexes.isEmpty())
return;
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::EditSendingAddress :
EditAddressDialog::EditReceivingAddress, this);
dlg.setModel(model);
QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
dlg.loadRow(origIndex.row());
dlg.exec();
}
void AddressBookPage::on_newAddress_clicked()
{
if(!model)
return;
if (tab == ReceivingTab) {
return;
}
EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, this);
dlg.setModel(model);
if(dlg.exec())
{
newAddressToSelect = dlg.getAddress();
}
}
void AddressBookPage::on_deleteAddress_clicked()
{
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
QModelIndexList indexes = table->selectionModel()->selectedRows();
if(!indexes.isEmpty())
{
table->model()->removeRow(indexes.at(0).row());
}
}
void AddressBookPage::selectionChanged()
{
// Set button states based on selected tab and selection
QTableView *table = ui->tableView;
if(!table->selectionModel())
return;
if(table->selectionModel()->hasSelection())
{
switch(tab)
{
case SendingTab:
// In sending tab, allow deletion of selection
ui->deleteAddress->setEnabled(true);
ui->deleteAddress->setVisible(true);
break;
case ReceivingTab:
// Deleting receiving addresses, however, is not allowed
ui->deleteAddress->setEnabled(false);
ui->deleteAddress->setVisible(false);
break;
}
ui->copyAddress->setEnabled(true);
}
else
{
ui->deleteAddress->setEnabled(false);
ui->copyAddress->setEnabled(false);
}
}
void AddressBookPage::done(int retval)
{
QTableView *table = ui->tableView;
if(!table->selectionModel() || !table->model())
return;
// Figure out which address was selected, and return it
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
for (const QModelIndex& index : indexes) {
QVariant address = table->model()->data(index);
returnValue = address.toString();
}
if(returnValue.isEmpty())
{
// If no address entry selected, return rejected
retval = Rejected;
}
QDialog::done(retval);
}
void AddressBookPage::on_exportButton_clicked()
{
// CSV is currently the only supported format
QString filename = GUIUtil::getSaveFileName(this,
tr("Export Address List"), QString(),
/*: Expanded name of the CSV file format.
See https://en.wikipedia.org/wiki/Comma-separated_values */
tr("Comma separated file") + QLatin1String(" (*.csv)"), nullptr);
if (filename.isNull())
return;
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(proxyModel);
writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
if(!writer.write()) {
QMessageBox::critical(this, tr("Exporting Failed"),
/*: An error message. %1 is a stand-in argument for the name
of the file we attempted to save to. */
tr("There was an error trying to save the address list to %1. Please try again.").arg(filename));
}
}
void AddressBookPage::contextualMenu(const QPoint &point)
{
QModelIndex index = ui->tableView->indexAt(point);
if(index.isValid())
{
contextMenu->exec(QCursor::pos());
}
}
void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin, int /*end*/)
{
QModelIndex idx = proxyModel->mapFromSource(model->index(begin, AddressTableModel::Address, parent));
if(idx.isValid() && (idx.data(Qt::EditRole).toString() == newAddressToSelect))
{
// Select row of newly created address, once
ui->tableView->setFocus();
ui->tableView->selectRow(idx.row());
newAddressToSelect.clear();
}
}

86
src/qt/addressbookpage.h Normal file
View file

@ -0,0 +1,86 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_ADDRESSBOOKPAGE_H
#define DIGIBYTE_QT_ADDRESSBOOKPAGE_H
#include <QDialog>
class AddressBookSortFilterProxyModel;
class AddressTableModel;
class PlatformStyle;
namespace Ui {
class AddressBookPage;
}
QT_BEGIN_NAMESPACE
class QItemSelection;
class QMenu;
class QModelIndex;
QT_END_NAMESPACE
/** Widget that shows a list of sending or receiving addresses.
*/
class AddressBookPage : public QDialog
{
Q_OBJECT
public:
enum Tabs {
SendingTab = 0,
ReceivingTab = 1
};
enum Mode {
ForSelection, /**< Open address book to pick address */
ForEditing /**< Open address book for editing */
};
explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent = nullptr);
~AddressBookPage();
void setModel(AddressTableModel *model);
const QString &getReturnValue() const { return returnValue; }
public Q_SLOTS:
void done(int retval) override;
private:
Ui::AddressBookPage *ui;
AddressTableModel *model;
Mode mode;
Tabs tab;
QString returnValue;
AddressBookSortFilterProxyModel *proxyModel;
QMenu *contextMenu;
QString newAddressToSelect;
private Q_SLOTS:
/** Delete currently selected address entry */
void on_deleteAddress_clicked();
/** Create a new address for receiving coins and / or add a new address book entry */
void on_newAddress_clicked();
/** Copy address of currently selected address entry to clipboard */
void on_copyAddress_clicked();
/** Copy label of currently selected address entry to clipboard (no button) */
void onCopyLabelAction();
/** Edit currently selected address entry (no button) */
void onEditAction();
/** Export button clicked */
void on_exportButton_clicked();
/** Set button states based on selected tab and selection */
void selectionChanged();
/** Spawn contextual menu (right mouse menu) for address book entry */
void contextualMenu(const QPoint &point);
/** New entry/entries were added to address table */
void selectNewAddress(const QModelIndex &parent, int begin, int /*end*/);
Q_SIGNALS:
void sendCoins(QString addr);
};
#endif // DIGIBYTE_QT_ADDRESSBOOKPAGE_H

View file

@ -0,0 +1,457 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/addresstablemodel.h>
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <key_io.h>
#include <wallet/wallet.h>
#include <algorithm>
#include <QFont>
#include <QDebug>
const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";
struct AddressTableEntry
{
enum Type {
Sending,
Receiving,
Hidden /* QSortFilterProxyModel will filter these out */
};
Type type;
QString label;
QString address;
AddressTableEntry() {}
AddressTableEntry(Type _type, const QString &_label, const QString &_address):
type(_type), label(_label), address(_address) {}
};
struct AddressTableEntryLessThan
{
bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
{
return a.address < b.address;
}
bool operator()(const AddressTableEntry &a, const QString &b) const
{
return a.address < b;
}
bool operator()(const QString &a, const AddressTableEntry &b) const
{
return a < b.address;
}
};
/* Determine address type from address purpose */
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
{
AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
// "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all.
if (strPurpose == "send")
addressType = AddressTableEntry::Sending;
else if (strPurpose == "receive")
addressType = AddressTableEntry::Receiving;
else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
return addressType;
}
// Private implementation
class AddressTablePriv
{
public:
QList<AddressTableEntry> cachedAddressTable;
AddressTableModel *parent;
explicit AddressTablePriv(AddressTableModel *_parent):
parent(_parent) {}
void refreshAddressTable(interfaces::Wallet& wallet, bool pk_hash_only = false)
{
cachedAddressTable.clear();
{
for (const auto& address : wallet.getAddresses())
{
if (pk_hash_only && !std::holds_alternative<PKHash>(address.dest)) {
continue;
}
AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(address.purpose), address.is_mine);
cachedAddressTable.append(AddressTableEntry(addressType,
QString::fromStdString(address.name),
QString::fromStdString(EncodeDestination(address.dest))));
}
}
// std::lower_bound() and std::upper_bound() require our cachedAddressTable list to be sorted in asc order
// Even though the map is already sorted this re-sorting step is needed because the originating map
// is sorted by binary address, not by base58() address.
std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
}
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
{
// Find address / label in model
QList<AddressTableEntry>::iterator lower = std::lower_bound(
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
QList<AddressTableEntry>::iterator upper = std::upper_bound(
cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
int lowerIndex = (lower - cachedAddressTable.begin());
int upperIndex = (upper - cachedAddressTable.begin());
bool inModel = (lower != upper);
AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
switch(status)
{
case CT_NEW:
if(inModel)
{
qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
break;
}
parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
parent->endInsertRows();
break;
case CT_UPDATED:
if(!inModel)
{
qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
break;
}
lower->type = newEntryType;
lower->label = label;
parent->emitDataChanged(lowerIndex);
break;
case CT_DELETED:
if(!inModel)
{
qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
break;
}
parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
cachedAddressTable.erase(lower, upper);
parent->endRemoveRows();
break;
}
}
int size()
{
return cachedAddressTable.size();
}
AddressTableEntry *index(int idx)
{
if(idx >= 0 && idx < cachedAddressTable.size())
{
return &cachedAddressTable[idx];
}
else
{
return nullptr;
}
}
};
AddressTableModel::AddressTableModel(WalletModel *parent, bool pk_hash_only) :
QAbstractTableModel(parent), walletModel(parent)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(this);
priv->refreshAddressTable(parent->wallet(), pk_hash_only);
}
AddressTableModel::~AddressTableModel()
{
delete priv;
}
int AddressTableModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return priv->size();
}
int AddressTableModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return columns.length();
}
QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
const auto column = static_cast<ColumnIndex>(index.column());
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (column) {
case Label:
if (rec->label.isEmpty() && role == Qt::DisplayRole) {
return tr("(no label)");
} else {
return rec->label;
}
case Address:
return rec->address;
} // no default case, so the compiler can warn about missing cases
assert(false);
} else if (role == Qt::FontRole) {
switch (column) {
case Label:
return QFont();
case Address:
return GUIUtil::fixedPitchFont();
} // no default case, so the compiler can warn about missing cases
assert(false);
} else if (role == TypeRole) {
switch(rec->type)
{
case AddressTableEntry::Sending:
return Send;
case AddressTableEntry::Receiving:
return Receive;
case AddressTableEntry::Hidden:
return {};
} // no default case, so the compiler can warn about missing cases
assert(false);
}
return QVariant();
}
bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(!index.isValid())
return false;
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
editStatus = OK;
if(role == Qt::EditRole)
{
CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
if(index.column() == Label)
{
// Do nothing, if old label == new label
if(rec->label == value.toString())
{
editStatus = NO_CHANGES;
return false;
}
walletModel->wallet().setAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} else if(index.column() == Address) {
CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
// Refuse to set invalid address, set error status and return false
if(std::get_if<CNoDestination>(&newAddress))
{
editStatus = INVALID_ADDRESS;
return false;
}
// Do nothing, if old address == new address
else if(newAddress == curAddress)
{
editStatus = NO_CHANGES;
return false;
}
// Check for duplicate addresses to prevent accidental deletion of addresses, if you try
// to paste an existing address over another address (with a different label)
if (walletModel->wallet().getAddress(
newAddress, /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
{
editStatus = DUPLICATE_ADDRESS;
return false;
}
// Double-check that we're not overwriting a receiving address
else if(rec->type == AddressTableEntry::Sending)
{
// Remove old entry
walletModel->wallet().delAddressBook(curAddress);
// Add new entry with new address
walletModel->wallet().setAddressBook(newAddress, value.toString().toStdString(), strPurpose);
}
}
return true;
}
return false;
}
QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal)
{
if(role == Qt::DisplayRole && section < columns.size())
{
return columns[section];
}
}
return QVariant();
}
Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
{
if (!index.isValid()) return Qt::NoItemFlags;
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
// Can edit address and label for sending addresses,
// and only label for receiving addresses.
if(rec->type == AddressTableEntry::Sending ||
(rec->type == AddressTableEntry::Receiving && index.column()==Label))
{
retval |= Qt::ItemIsEditable;
}
return retval;
}
QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
AddressTableEntry *data = priv->index(row);
if(data)
{
return createIndex(row, column, priv->index(row));
}
else
{
return QModelIndex();
}
}
void AddressTableModel::updateEntry(const QString &address,
const QString &label, bool isMine, const QString &purpose, int status)
{
// Update address book model from Vexta Core
priv->updateEntry(address, label, isMine, purpose, status);
}
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type)
{
std::string strLabel = label.toStdString();
std::string strAddress = address.toStdString();
editStatus = OK;
if(type == Send)
{
if(!walletModel->validateAddress(address))
{
editStatus = INVALID_ADDRESS;
return QString();
}
// Check for duplicate addresses
{
if (walletModel->wallet().getAddress(
DecodeDestination(strAddress), /* name= */ nullptr, /* is_mine= */ nullptr, /* purpose= */ nullptr))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
}
}
// Add entry
walletModel->wallet().setAddressBook(DecodeDestination(strAddress), strLabel, "send");
}
else if(type == Receive)
{
// Generate a new address to associate with given label
CTxDestination dest;
if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
{
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
{
// Unlock wallet failed or was cancelled
editStatus = WALLET_UNLOCK_FAILURE;
return QString();
}
if(!walletModel->wallet().getNewDestination(address_type, strLabel, dest))
{
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
}
strAddress = EncodeDestination(dest);
}
else
{
return QString();
}
return QString::fromStdString(strAddress);
}
bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
Q_UNUSED(parent);
AddressTableEntry *rec = priv->index(row);
if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
{
// Can only remove one row at a time, and cannot remove rows not in model.
// Also refuse to remove receiving addresses.
return false;
}
walletModel->wallet().delAddressBook(DecodeDestination(rec->address.toStdString()));
return true;
}
QString AddressTableModel::labelForAddress(const QString &address) const
{
std::string name;
if (getAddressData(address, &name, /* purpose= */ nullptr)) {
return QString::fromStdString(name);
}
return QString();
}
QString AddressTableModel::purposeForAddress(const QString &address) const
{
std::string purpose;
if (getAddressData(address, /* name= */ nullptr, &purpose)) {
return QString::fromStdString(purpose);
}
return QString();
}
bool AddressTableModel::getAddressData(const QString &address,
std::string* name,
std::string* purpose) const {
CTxDestination destination = DecodeDestination(address.toStdString());
return walletModel->wallet().getAddress(destination, name, /* is_mine= */ nullptr, purpose);
}
int AddressTableModel::lookupAddress(const QString &address) const
{
QModelIndexList lst = match(index(0, Address, QModelIndex()),
Qt::EditRole, address, 1, Qt::MatchExactly);
if(lst.isEmpty())
{
return -1;
}
else
{
return lst.at(0).row();
}
}
OutputType AddressTableModel::GetDefaultAddressType() const { return walletModel->wallet().getDefaultAddressType(); };
void AddressTableModel::emitDataChanged(int idx)
{
Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}

106
src/qt/addresstablemodel.h Normal file
View file

@ -0,0 +1,106 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_ADDRESSTABLEMODEL_H
#define DIGIBYTE_QT_ADDRESSTABLEMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
enum class OutputType;
class AddressTablePriv;
class WalletModel;
namespace interfaces {
class Wallet;
}
/**
Qt model of the address book in the core. This allows views to access and modify the address book.
*/
class AddressTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit AddressTableModel(WalletModel *parent = nullptr, bool pk_hash_only = false);
~AddressTableModel();
enum ColumnIndex {
Label = 0, /**< User specified label */
Address = 1 /**< Vexta address */
};
enum RoleIndex {
TypeRole = Qt::UserRole /**< Type of address (#Send or #Receive) */
};
/** Return status of edit/insert operation */
enum EditStatus {
OK, /**< Everything ok */
NO_CHANGES, /**< No changes were made during edit operation */
INVALID_ADDRESS, /**< Unparseable address */
DUPLICATE_ADDRESS, /**< Address already in address book */
WALLET_UNLOCK_FAILURE, /**< Wallet could not be unlocked to create new receiving address */
KEY_GENERATION_FAILURE /**< Generating a new public key for a receiving address failed */
};
static const QString Send; /**< Specifies send address */
static const QString Receive; /**< Specifies receive address */
/** @name Methods overridden from QAbstractTableModel
@{*/
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
/*@}*/
/* Add an address to the model.
Returns the added address on success, and an empty string otherwise.
*/
QString addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type);
/** Look up label for address in address book, if not found return empty string. */
QString labelForAddress(const QString &address) const;
/** Look up purpose for address in address book, if not found return empty string. */
QString purposeForAddress(const QString &address) const;
/* Look up row index of an address in the model.
Return -1 if not found.
*/
int lookupAddress(const QString &address) const;
EditStatus getEditStatus() const { return editStatus; }
OutputType GetDefaultAddressType() const;
private:
WalletModel* const walletModel;
AddressTablePriv *priv = nullptr;
QStringList columns;
EditStatus editStatus = OK;
/** Look up address book data given an address string. */
bool getAddressData(const QString &address, std::string* name, std::string* purpose) const;
/** Notify listeners that data changed. */
void emitDataChanged(int index);
public Q_SLOTS:
/* Update address list from core.
*/
void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
friend class AddressTablePriv;
};
#endif // DIGIBYTE_QT_ADDRESSTABLEMODEL_H

9
src/qt/android/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
/.gradle
/build
/gradle/wrapper
/gradlew*
/libs
/res/layout
/res/values*
/src/org/kde
/src/org/qtproject

View file

@ -0,0 +1,38 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest package="org.digibytecore.qt" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<uses-sdk android:targetSdkVersion="24"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="Vexta Core">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
android:name="org.digibytecore.qt.VextaQtActivity"
android:label="Vexta Core"
android:icon="@drawable/digibyte"
android:screenOrientation="unspecified"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.arguments" android:value="-testnet"/>
<meta-data android:name="android.app.lib_name" android:value="vexta-qt"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/>
<meta-data android:name="android.app.background_running" android:value="true"/>
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="true"/>
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,52 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
}
}
repositories {
google()
jcenter()
}
apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
android {
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qt5AndroidDir + '/res', 'res']
resources.srcDirs = ['src']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
lintOptions {
abortOnError false
}
dexOptions {
javaMaxHeapSize '4g'
}
defaultConfig {
minSdkVersion 24
}
}

View file

@ -0,0 +1,4 @@
androidBuildToolsVersion=28.0.3
androidCompileSdkVersion=28
qt5AndroidDir=new File(".").absolutePath
org.gradle.jvmargs=-Xmx4608M

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,29 @@
package org.digibytecore.qt;
import android.os.Bundle;
import android.system.ErrnoException;
import android.system.Os;
import org.qtproject.qt5.android.bindings.QtActivity;
import java.io.File;
public class DigiByteQtActivity extends QtActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
final File digibyteDir = new File(getFilesDir().getAbsolutePath() + "/.digibyte");
if (!digibyteDir.exists()) {
digibyteDir.mkdir();
}
try {
Os.setenv("QT_QPA_PLATFORM", "android", true);
} catch (ErrnoException e) {
e.printStackTrace();
}
super.onCreate(savedInstanceState);
}
}

View file

@ -0,0 +1,276 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <qt/askpassphrasedialog.h>
#include <qt/forms/ui_askpassphrasedialog.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/walletmodel.h>
#include <support/allocators/secure.h>
#include <QKeyEvent>
#include <QMessageBox>
#include <QPushButton>
AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureString* passphrase_out) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::AskPassphraseDialog),
mode(_mode),
model(nullptr),
fCapsLock(false),
m_passphrase_out(passphrase_out)
{
ui->setupUi(this);
ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint());
ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint());
ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint());
ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);
// Setup Caps Lock detection.
ui->passEdit1->installEventFilter(this);
ui->passEdit2->installEventFilter(this);
ui->passEdit3->installEventFilter(this);
switch(mode)
{
case Encrypt: // Ask passphrase x2
ui->warningLabel->setText(tr("Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
ui->passLabel1->hide();
ui->passEdit1->hide();
setWindowTitle(tr("Encrypt wallet"));
break;
case Unlock: // Ask passphrase
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
ui->passLabel2->hide();
ui->passEdit2->hide();
ui->passLabel3->hide();
ui->passEdit3->hide();
setWindowTitle(tr("Unlock wallet"));
break;
case ChangePass: // Ask old passphrase + new passphrase x2
setWindowTitle(tr("Change passphrase"));
ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet."));
break;
}
textChanged();
connect(ui->toggleShowPasswordButton, &QPushButton::toggled, this, &AskPassphraseDialog::toggleShowPassword);
connect(ui->passEdit1, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
connect(ui->passEdit2, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
connect(ui->passEdit3, &QLineEdit::textChanged, this, &AskPassphraseDialog::textChanged);
GUIUtil::handleCloseWindowShortcut(this);
}
AskPassphraseDialog::~AskPassphraseDialog()
{
secureClearPassFields();
delete ui;
}
void AskPassphraseDialog::setModel(WalletModel *_model)
{
this->model = _model;
}
void AskPassphraseDialog::accept()
{
SecureString oldpass, newpass1, newpass2;
if (!model && mode != Encrypt)
return;
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
newpass2.reserve(MAX_PASSPHRASE_SIZE);
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make this input mlock()'d to begin with.
oldpass.assign(ui->passEdit1->text().toStdString().c_str());
newpass1.assign(ui->passEdit2->text().toStdString().c_str());
newpass2.assign(ui->passEdit3->text().toStdString().c_str());
secureClearPassFields();
switch(mode)
{
case Encrypt: {
if(newpass1.empty() || newpass2.empty())
{
// Cannot encrypt with empty passphrase
break;
}
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
tr("Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR VTX</b>!") + "<br><br>" + tr("Are you sure you wish to encrypt your wallet?"),
QMessageBox::Yes|QMessageBox::Cancel,
QMessageBox::Cancel);
if(retval == QMessageBox::Yes)
{
if(newpass1 == newpass2)
{
QString encryption_reminder = tr("Remember that encrypting your wallet cannot fully protect "
"your VTX from being stolen by malware infecting your computer.");
if (m_passphrase_out) {
m_passphrase_out->assign(newpass1);
QMessageBox::warning(this, tr("Wallet to be encrypted"),
"<qt>" +
tr("Your wallet is about to be encrypted. ") + encryption_reminder +
"</b></qt>");
} else {
assert(model != nullptr);
if (model->setWalletEncrypted(newpass1)) {
QMessageBox::warning(this, tr("Wallet encrypted"),
"<qt>" +
tr("Your wallet is now encrypted. ") + encryption_reminder +
"<br><br><b>" +
tr("IMPORTANT: Any previous backups you have made of your wallet file "
"should be replaced with the newly generated, encrypted wallet file. "
"For security reasons, previous backups of the unencrypted wallet file "
"will become useless as soon as you start using the new, encrypted wallet.") +
"</b></qt>");
} else {
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
}
}
QDialog::accept(); // Success
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The supplied passphrases do not match."));
}
}
else
{
QDialog::reject(); // Cancelled
}
} break;
case Unlock:
try {
if (!model->setWalletLocked(false, oldpass)) {
QMessageBox::critical(this, tr("Wallet unlock failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
} else {
QDialog::accept(); // Success
}
} catch (const std::runtime_error& e) {
QMessageBox::critical(this, tr("Wallet unlock failed"), e.what());
}
break;
case ChangePass:
if(newpass1 == newpass2)
{
if(model->changePassphrase(oldpass, newpass1))
{
QMessageBox::information(this, tr("Wallet encrypted"),
tr("Wallet passphrase was successfully changed."));
QDialog::accept(); // Success
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The supplied passphrases do not match."));
}
break;
}
}
void AskPassphraseDialog::textChanged()
{
// Validate input, set Ok button to enabled when acceptable
bool acceptable = false;
switch(mode)
{
case Encrypt: // New passphrase x2
acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
case Unlock: // Old passphrase x1
acceptable = !ui->passEdit1->text().isEmpty();
break;
case ChangePass: // Old passphrase x1, new passphrase x2
acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
}
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
}
bool AskPassphraseDialog::event(QEvent *event)
{
// Detect Caps Lock key press.
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_CapsLock) {
fCapsLock = !fCapsLock;
}
if (fCapsLock) {
ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
} else {
ui->capsLabel->clear();
}
}
return QWidget::event(event);
}
void AskPassphraseDialog::toggleShowPassword(bool show)
{
ui->toggleShowPasswordButton->setDown(show);
const auto mode = show ? QLineEdit::Normal : QLineEdit::Password;
ui->passEdit1->setEchoMode(mode);
ui->passEdit2->setEchoMode(mode);
ui->passEdit3->setEchoMode(mode);
}
bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event)
{
/* Detect Caps Lock.
* There is no good OS-independent way to check a key state in Qt, but we
* can detect Caps Lock by checking for the following condition:
* Shift key is down and the result is a lower case character, or
* Shift key is not down and the result is an upper case character.
*/
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
QString str = ke->text();
if (str.length() != 0) {
const QChar *psz = str.unicode();
bool fShift = (ke->modifiers() & Qt::ShiftModifier) != 0;
if ((fShift && *psz >= 'a' && *psz <= 'z') || (!fShift && *psz >= 'A' && *psz <= 'Z')) {
fCapsLock = true;
ui->capsLabel->setText(tr("Warning: The Caps Lock key is on!"));
} else if (psz->isLetter()) {
fCapsLock = false;
ui->capsLabel->clear();
}
}
}
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);
}

View file

@ -0,0 +1,56 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_ASKPASSPHRASEDIALOG_H
#define DIGIBYTE_QT_ASKPASSPHRASEDIALOG_H
#include <QDialog>
#include <support/allocators/secure.h>
class WalletModel;
namespace Ui {
class AskPassphraseDialog;
}
/** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase.
*/
class AskPassphraseDialog : public QDialog
{
Q_OBJECT
public:
enum Mode {
Encrypt, /**< Ask passphrase twice and encrypt */
Unlock, /**< Ask passphrase and unlock */
ChangePass, /**< Ask old passphrase + new passphrase twice */
};
explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr);
~AskPassphraseDialog();
void accept() override;
void setModel(WalletModel *model);
private:
Ui::AskPassphraseDialog *ui;
Mode mode;
WalletModel *model;
bool fCapsLock;
SecureString* m_passphrase_out;
private Q_SLOTS:
void textChanged();
void secureClearPassFields();
void toggleShowPassword(bool);
protected:
bool event(QEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override;
};
#endif // DIGIBYTE_QT_ASKPASSPHRASEDIALOG_H

184
src/qt/bantablemodel.cpp Normal file
View file

@ -0,0 +1,184 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/bantablemodel.h>
#include <interfaces/node.h>
#include <net_types.h> // For banmap_t
#include <utility>
#include <QDateTime>
#include <QList>
#include <QLocale>
#include <QModelIndex>
#include <QVariant>
bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan& right) const
{
const CCombinedBan* pLeft = &left;
const CCombinedBan* pRight = &right;
if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);
switch (static_cast<BanTableModel::ColumnIndex>(column)) {
case BanTableModel::Address:
return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0;
case BanTableModel::Bantime:
return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil;
} // no default case, so the compiler can warn about missing cases
assert(false);
}
// private implementation
class BanTablePriv
{
public:
/** Local cache of peer information */
QList<CCombinedBan> cachedBanlist;
/** Column to sort nodes by (default to unsorted) */
int sortColumn{-1};
/** Order (ascending or descending) to sort nodes by */
Qt::SortOrder sortOrder;
/** Pull a full list of banned nodes from CNode into our cache */
void refreshBanlist(interfaces::Node& node)
{
banmap_t banMap;
node.getBanned(banMap);
cachedBanlist.clear();
cachedBanlist.reserve(banMap.size());
for (const auto& entry : banMap)
{
CCombinedBan banEntry;
banEntry.subnet = entry.first;
banEntry.banEntry = entry.second;
cachedBanlist.append(banEntry);
}
if (sortColumn >= 0)
// sort cachedBanlist (use stable sort to prevent rows jumping around unnecessarily)
std::stable_sort(cachedBanlist.begin(), cachedBanlist.end(), BannedNodeLessThan(sortColumn, sortOrder));
}
int size() const
{
return cachedBanlist.size();
}
CCombinedBan *index(int idx)
{
if (idx >= 0 && idx < cachedBanlist.size())
return &cachedBanlist[idx];
return nullptr;
}
};
BanTableModel::BanTableModel(interfaces::Node& node, QObject* parent) :
QAbstractTableModel(parent),
m_node(node)
{
columns << tr("IP/Netmask") << tr("Banned Until");
priv.reset(new BanTablePriv());
// load initial data
refresh();
}
BanTableModel::~BanTableModel()
{
// Intentionally left empty
}
int BanTableModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return priv->size();
}
int BanTableModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return columns.length();
}
QVariant BanTableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
CCombinedBan *rec = static_cast<CCombinedBan*>(index.internalPointer());
const auto column = static_cast<ColumnIndex>(index.column());
if (role == Qt::DisplayRole) {
switch (column) {
case Address:
return QString::fromStdString(rec->subnet.ToString());
case Bantime:
QDateTime date = QDateTime::fromMSecsSinceEpoch(0);
date = date.addSecs(rec->banEntry.nBanUntil);
return QLocale::system().toString(date, QLocale::LongFormat);
} // no default case, so the compiler can warn about missing cases
assert(false);
}
return QVariant();
}
QVariant BanTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal)
{
if(role == Qt::DisplayRole && section < columns.size())
{
return columns[section];
}
}
return QVariant();
}
Qt::ItemFlags BanTableModel::flags(const QModelIndex &index) const
{
if (!index.isValid()) return Qt::NoItemFlags;
Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return retval;
}
QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
CCombinedBan *data = priv->index(row);
if (data)
return createIndex(row, column, data);
return QModelIndex();
}
void BanTableModel::refresh()
{
Q_EMIT layoutAboutToBeChanged();
priv->refreshBanlist(m_node);
Q_EMIT layoutChanged();
}
void BanTableModel::sort(int column, Qt::SortOrder order)
{
priv->sortColumn = column;
priv->sortOrder = order;
refresh();
}
bool BanTableModel::shouldShow()
{
return priv->size() > 0;
}

80
src/qt/bantablemodel.h Normal file
View file

@ -0,0 +1,80 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_BANTABLEMODEL_H
#define DIGIBYTE_QT_BANTABLEMODEL_H
#include <net.h>
#include <memory>
#include <QAbstractTableModel>
#include <QStringList>
class BanTablePriv;
namespace interfaces {
class Node;
}
struct CCombinedBan {
CSubNet subnet;
CBanEntry banEntry;
};
class BannedNodeLessThan
{
public:
BannedNodeLessThan(int nColumn, Qt::SortOrder fOrder) :
column(nColumn), order(fOrder) {}
bool operator()(const CCombinedBan& left, const CCombinedBan& right) const;
private:
int column;
Qt::SortOrder order;
};
/**
Qt model providing information about connected peers, similar to the
"getpeerinfo" RPC call. Used by the rpc console UI.
*/
class BanTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit BanTableModel(interfaces::Node& node, QObject* parent);
~BanTableModel();
void startAutoRefresh();
void stopAutoRefresh();
enum ColumnIndex {
Address = 0,
Bantime = 1
};
/** @name Methods overridden from QAbstractTableModel
@{*/
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void sort(int column, Qt::SortOrder order) override;
/*@}*/
bool shouldShow();
public Q_SLOTS:
void refresh();
private:
interfaces::Node& m_node;
QStringList columns;
std::unique_ptr<BanTablePriv> priv;
};
#endif // DIGIBYTE_QT_BANTABLEMODEL_H

30
src/qt/callback.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef DIGIBYTE_QT_CALLBACK_H
#define DIGIBYTE_QT_CALLBACK_H
#include <QObject>
class Callback : public QObject
{
Q_OBJECT
public Q_SLOTS:
virtual void call() = 0;
};
template <typename F>
class FunctionCallback : public Callback
{
F f;
public:
explicit FunctionCallback(F f_) : f(std::move(f_)) {}
~FunctionCallback() override {}
void call() override { f(this); }
};
template <typename F>
FunctionCallback<F>* makeCallback(F f)
{
return new FunctionCallback<F>(std::move(f));
}
#endif // DIGIBYTE_QT_CALLBACK_H

338
src/qt/clientmodel.cpp Normal file
View file

@ -0,0 +1,338 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/clientmodel.h>
#include <qt/bantablemodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/peertablemodel.h>
#include <qt/peertablesortproxy.h>
#include <clientversion.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <net.h>
#include <netbase.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <validation.h>
#include <stdint.h>
#include <functional>
#include <QDebug>
#include <QThread>
#include <QTimer>
static int64_t nLastHeaderTipUpdateNotification = 0;
static int64_t nLastBlockTipUpdateNotification = 0;
ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
QObject(parent),
m_node(node),
optionsModel(_optionsModel),
peerTableModel(nullptr),
banTableModel(nullptr),
m_thread(new QThread(this))
{
cachedBestHeaderHeight = -1;
cachedBestHeaderTime = -1;
peerTableModel = new PeerTableModel(m_node, this);
m_peer_table_sort_proxy = new PeerTableSortProxy(this);
m_peer_table_sort_proxy->setSourceModel(peerTableModel);
banTableModel = new BanTableModel(m_node, this);
QTimer* timer = new QTimer;
timer->setInterval(MODEL_UPDATE_DELAY);
connect(timer, &QTimer::timeout, [this] {
// no locking required at this point
// the following calls will acquire the required lock
Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
});
connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
connect(m_thread, &QThread::started, [timer] { timer->start(); });
// move timer to thread so that polling doesn't disturb main event loop
timer->moveToThread(m_thread);
m_thread->start();
QTimer::singleShot(0, timer, []() {
util::ThreadRename("qt-clientmodl");
});
subscribeToCoreSignals();
}
ClientModel::~ClientModel()
{
unsubscribeFromCoreSignals();
m_thread->quit();
m_thread->wait();
}
int ClientModel::getNumConnections(unsigned int flags) const
{
ConnectionDirection connections = ConnectionDirection::None;
if(flags == CONNECTIONS_IN)
connections = ConnectionDirection::In;
else if (flags == CONNECTIONS_OUT)
connections = ConnectionDirection::Out;
else if (flags == CONNECTIONS_ALL)
connections = ConnectionDirection::Both;
return m_node.getNodeCount(connections);
}
int ClientModel::getHeaderTipHeight() const
{
if (cachedBestHeaderHeight == -1) {
// make sure we initially populate the cache via a cs_main lock
// otherwise we need to wait for a tip update
int height;
int64_t blockTime;
if (m_node.getHeaderTip(height, blockTime)) {
cachedBestHeaderHeight = height;
cachedBestHeaderTime = blockTime;
}
}
return cachedBestHeaderHeight;
}
int64_t ClientModel::getHeaderTipTime() const
{
if (cachedBestHeaderTime == -1) {
int height;
int64_t blockTime;
if (m_node.getHeaderTip(height, blockTime)) {
cachedBestHeaderHeight = height;
cachedBestHeaderTime = blockTime;
}
}
return cachedBestHeaderTime;
}
int ClientModel::getNumBlocks() const
{
if (m_cached_num_blocks == -1) {
m_cached_num_blocks = m_node.getNumBlocks();
}
return m_cached_num_blocks;
}
uint256 ClientModel::getBestBlockHash()
{
uint256 tip{WITH_LOCK(m_cached_tip_mutex, return m_cached_tip_blocks)};
if (!tip.IsNull()) {
return tip;
}
// Lock order must be: first `cs_main`, then `m_cached_tip_mutex`.
// The following will lock `cs_main` (and release it), so we must not
// own `m_cached_tip_mutex` here.
tip = m_node.getBestBlockHash();
LOCK(m_cached_tip_mutex);
// We checked that `m_cached_tip_blocks` is not null above, but then we
// released the mutex `m_cached_tip_mutex`, so it could have changed in the
// meantime. Thus, check again.
if (m_cached_tip_blocks.IsNull()) {
m_cached_tip_blocks = tip;
}
return m_cached_tip_blocks;
}
void ClientModel::updateNumConnections(int numConnections)
{
Q_EMIT numConnectionsChanged(numConnections);
}
void ClientModel::updateNetworkActive(bool networkActive)
{
Q_EMIT networkActiveChanged(networkActive);
}
void ClientModel::updateAlert()
{
Q_EMIT alertsChanged(getStatusBarWarnings());
}
enum BlockSource ClientModel::getBlockSource() const
{
if (m_node.getReindex())
return BlockSource::REINDEX;
else if (m_node.getImporting())
return BlockSource::DISK;
else if (getNumConnections() > 0)
return BlockSource::NETWORK;
return BlockSource::NONE;
}
QString ClientModel::getStatusBarWarnings() const
{
return QString::fromStdString(m_node.getWarnings().translated);
}
OptionsModel *ClientModel::getOptionsModel()
{
return optionsModel;
}
PeerTableModel *ClientModel::getPeerTableModel()
{
return peerTableModel;
}
PeerTableSortProxy* ClientModel::peerTableSortProxy()
{
return m_peer_table_sort_proxy;
}
BanTableModel *ClientModel::getBanTableModel()
{
return banTableModel;
}
QString ClientModel::formatFullVersion() const
{
return QString::fromStdString(FormatFullVersion());
}
QString ClientModel::formatSubVersion() const
{
return QString::fromStdString(strSubVersion);
}
bool ClientModel::isReleaseVersion() const
{
return CLIENT_VERSION_IS_RELEASE;
}
QString ClientModel::formatClientStartupTime() const
{
return QDateTime::fromTime_t(GetStartupTime()).toString();
}
QString ClientModel::dataDir() const
{
return GUIUtil::boostPathToQString(gArgs.GetDataDirNet());
}
QString ClientModel::blocksDir() const
{
return GUIUtil::boostPathToQString(gArgs.GetBlocksDirPath());
}
void ClientModel::updateBanlist()
{
banTableModel->refresh();
}
// Handlers for core signals
static void ShowProgress(ClientModel *clientmodel, const std::string &title, int nProgress)
{
// emits signal "showProgress"
bool invoked = QMetaObject::invokeMethod(clientmodel, "showProgress", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(title)),
Q_ARG(int, nProgress));
assert(invoked);
}
static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections)
{
// Too noisy: qDebug() << "NotifyNumConnectionsChanged: " + QString::number(newNumConnections);
bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNumConnections", Qt::QueuedConnection,
Q_ARG(int, newNumConnections));
assert(invoked);
}
static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
{
bool invoked = QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
Q_ARG(bool, networkActive));
assert(invoked);
}
static void NotifyAlertChanged(ClientModel *clientmodel)
{
qDebug() << "NotifyAlertChanged";
bool invoked = QMetaObject::invokeMethod(clientmodel, "updateAlert", Qt::QueuedConnection);
assert(invoked);
}
static void BannedListChanged(ClientModel *clientmodel)
{
qDebug() << QString("%1: Requesting update for peer banlist").arg(__func__);
bool invoked = QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection);
assert(invoked);
}
static void BlockTipChanged(ClientModel* clientmodel, SynchronizationState sync_state, interfaces::BlockTip tip, double verificationProgress, bool fHeader)
{
if (fHeader) {
// cache best headers time and height to reduce future cs_main locks
clientmodel->cachedBestHeaderHeight = tip.block_height;
clientmodel->cachedBestHeaderTime = tip.block_time;
} else {
clientmodel->m_cached_num_blocks = tip.block_height;
WITH_LOCK(clientmodel->m_cached_tip_mutex, clientmodel->m_cached_tip_blocks = tip.block_hash;);
}
// Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex.
const bool throttle = (sync_state != SynchronizationState::POST_INIT && !fHeader) || sync_state == SynchronizationState::INIT_REINDEX;
const int64_t now = throttle ? GetTimeMillis() : 0;
int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification;
if (throttle && now < nLastUpdateNotification + MODEL_UPDATE_DELAY) {
return;
}
bool invoked = QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection,
Q_ARG(int, tip.block_height),
Q_ARG(QDateTime, QDateTime::fromTime_t(tip.block_time)),
Q_ARG(double, verificationProgress),
Q_ARG(bool, fHeader),
Q_ARG(SynchronizationState, sync_state));
assert(invoked);
nLastUpdateNotification = now;
}
void ClientModel::subscribeToCoreSignals()
{
// Connect signals to client
m_handler_show_progress = m_node.handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2));
m_handler_notify_num_connections_changed = m_node.handleNotifyNumConnectionsChanged(std::bind(NotifyNumConnectionsChanged, this, std::placeholders::_1));
m_handler_notify_network_active_changed = m_node.handleNotifyNetworkActiveChanged(std::bind(NotifyNetworkActiveChanged, this, std::placeholders::_1));
m_handler_notify_alert_changed = m_node.handleNotifyAlertChanged(std::bind(NotifyAlertChanged, this));
m_handler_banned_list_changed = m_node.handleBannedListChanged(std::bind(BannedListChanged, this));
m_handler_notify_block_tip = m_node.handleNotifyBlockTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, false));
m_handler_notify_header_tip = m_node.handleNotifyHeaderTip(std::bind(BlockTipChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, true));
}
void ClientModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
m_handler_show_progress->disconnect();
m_handler_notify_num_connections_changed->disconnect();
m_handler_notify_network_active_changed->disconnect();
m_handler_notify_alert_changed->disconnect();
m_handler_banned_list_changed->disconnect();
m_handler_notify_block_tip->disconnect();
m_handler_notify_header_tip->disconnect();
}
bool ClientModel::getProxyInfo(std::string& ip_port) const
{
proxyType ipv4, ipv6;
if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
ip_port = ipv4.proxy.ToStringIPPort();
return true;
}
return false;
}

132
src/qt/clientmodel.h Normal file
View file

@ -0,0 +1,132 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_CLIENTMODEL_H
#define DIGIBYTE_QT_CLIENTMODEL_H
#include <QObject>
#include <QDateTime>
#include <atomic>
#include <memory>
#include <sync.h>
#include <uint256.h>
class BanTableModel;
class CBlockIndex;
class OptionsModel;
class PeerTableModel;
class PeerTableSortProxy;
enum class SynchronizationState;
namespace interfaces {
class Handler;
class Node;
}
QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
enum class BlockSource {
NONE,
REINDEX,
DISK,
NETWORK
};
enum NumConnections {
CONNECTIONS_NONE = 0,
CONNECTIONS_IN = (1U << 0),
CONNECTIONS_OUT = (1U << 1),
CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT),
};
/** Model for Vexta network client. */
class ClientModel : public QObject
{
Q_OBJECT
public:
explicit ClientModel(interfaces::Node& node, OptionsModel *optionsModel, QObject *parent = nullptr);
~ClientModel();
interfaces::Node& node() const { return m_node; }
OptionsModel *getOptionsModel();
PeerTableModel *getPeerTableModel();
PeerTableSortProxy* peerTableSortProxy();
BanTableModel *getBanTableModel();
//! Return number of connections, default is in- and outbound (total)
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
int getNumBlocks() const;
uint256 getBestBlockHash();
int getHeaderTipHeight() const;
int64_t getHeaderTipTime() const;
//! Returns enum BlockSource of the current importing/syncing state
enum BlockSource getBlockSource() const;
//! Return warnings to be displayed in status bar
QString getStatusBarWarnings() const;
QString formatFullVersion() const;
QString formatSubVersion() const;
bool isReleaseVersion() const;
QString formatClientStartupTime() const;
QString dataDir() const;
QString blocksDir() const;
bool getProxyInfo(std::string& ip_port) const;
// caches for the best header: hash, number of blocks and block time
mutable std::atomic<int> cachedBestHeaderHeight;
mutable std::atomic<int64_t> cachedBestHeaderTime;
mutable std::atomic<int> m_cached_num_blocks{-1};
Mutex m_cached_tip_mutex;
uint256 m_cached_tip_blocks GUARDED_BY(m_cached_tip_mutex){};
private:
interfaces::Node& m_node;
std::unique_ptr<interfaces::Handler> m_handler_show_progress;
std::unique_ptr<interfaces::Handler> m_handler_notify_num_connections_changed;
std::unique_ptr<interfaces::Handler> m_handler_notify_network_active_changed;
std::unique_ptr<interfaces::Handler> m_handler_notify_alert_changed;
std::unique_ptr<interfaces::Handler> m_handler_banned_list_changed;
std::unique_ptr<interfaces::Handler> m_handler_notify_block_tip;
std::unique_ptr<interfaces::Handler> m_handler_notify_header_tip;
OptionsModel *optionsModel;
PeerTableModel *peerTableModel;
PeerTableSortProxy* m_peer_table_sort_proxy{nullptr};
BanTableModel *banTableModel;
//! A thread to interact with m_node asynchronously
QThread* const m_thread;
void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
Q_SIGNALS:
void numConnectionsChanged(int count);
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header, SynchronizationState sync_state);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
void networkActiveChanged(bool networkActive);
void alertsChanged(const QString &warnings);
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
//! Fired when a message should be reported to the user
void message(const QString &title, const QString &message, unsigned int style);
// Show progress dialog e.g. for verifychain
void showProgress(const QString &title, int nProgress);
public Q_SLOTS:
void updateNumConnections(int numConnections);
void updateNetworkActive(bool networkActive);
void updateAlert();
void updateBanlist();
};
#endif // DIGIBYTE_QT_CLIENTMODEL_H

View file

@ -0,0 +1,704 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <qt/coincontroldialog.h>
#include <qt/forms/ui_coincontroldialog.h>
#include <qt/addresstablemodel.h>
#include <qt/digibyteunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/walletmodel.h>
#include <wallet/coincontrol.h>
#include <interfaces/node.h>
#include <key_io.h>
#include <policy/policy.h>
#include <wallet/wallet.h>
#include <QApplication>
#include <QCheckBox>
#include <QCursor>
#include <QDialogButtonBox>
#include <QFlags>
#include <QIcon>
#include <QSettings>
#include <QTreeWidget>
QList<CAmount> CoinControlDialog::payAmounts;
bool CoinControlDialog::fSubtractFeeFromAmount = false;
bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
int column = treeWidget()->sortColumn();
if (column == CoinControlDialog::COLUMN_AMOUNT || column == CoinControlDialog::COLUMN_DATE || column == CoinControlDialog::COLUMN_CONFIRMATIONS)
return data(column, Qt::UserRole).toLongLong() < other.data(column, Qt::UserRole).toLongLong();
return QTreeWidgetItem::operator<(other);
}
CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _model, const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::CoinControlDialog),
m_coin_control(coin_control),
model(_model),
platformStyle(_platformStyle)
{
ui->setupUi(this);
// context menu
contextMenu = new QMenu(this);
contextMenu->addAction(tr("&Copy address"), this, &CoinControlDialog::copyAddress);
contextMenu->addAction(tr("Copy &label"), this, &CoinControlDialog::copyLabel);
contextMenu->addAction(tr("Copy &amount"), this, &CoinControlDialog::copyAmount);
copyTransactionHashAction = contextMenu->addAction(tr("Copy transaction &ID"), this, &CoinControlDialog::copyTransactionHash);
contextMenu->addSeparator();
lockAction = contextMenu->addAction(tr("L&ock unspent"), this, &CoinControlDialog::lockCoin);
unlockAction = contextMenu->addAction(tr("&Unlock unspent"), this, &CoinControlDialog::unlockCoin);
connect(ui->treeWidget, &QWidget::customContextMenuRequested, this, &CoinControlDialog::showMenu);
// clipboard actions
QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
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 *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
connect(clipboardQuantityAction, &QAction::triggered, this, &CoinControlDialog::clipboardQuantity);
connect(clipboardAmountAction, &QAction::triggered, this, &CoinControlDialog::clipboardAmount);
connect(clipboardFeeAction, &QAction::triggered, this, &CoinControlDialog::clipboardFee);
connect(clipboardAfterFeeAction, &QAction::triggered, this, &CoinControlDialog::clipboardAfterFee);
connect(clipboardBytesAction, &QAction::triggered, this, &CoinControlDialog::clipboardBytes);
connect(clipboardLowOutputAction, &QAction::triggered, this, &CoinControlDialog::clipboardLowOutput);
connect(clipboardChangeAction, &QAction::triggered, this, &CoinControlDialog::clipboardChange);
ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
ui->labelCoinControlAmount->addAction(clipboardAmountAction);
ui->labelCoinControlFee->addAction(clipboardFeeAction);
ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
ui->labelCoinControlBytes->addAction(clipboardBytesAction);
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
// toggle tree/list mode
connect(ui->radioTreeMode, &QRadioButton::toggled, this, &CoinControlDialog::radioTreeMode);
connect(ui->radioListMode, &QRadioButton::toggled, this, &CoinControlDialog::radioListMode);
// click on checkbox
connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &CoinControlDialog::viewItemChanged);
// click on header
ui->treeWidget->header()->setSectionsClickable(true);
connect(ui->treeWidget->header(), &QHeaderView::sectionClicked, this, &CoinControlDialog::headerSectionClicked);
// ok button
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &CoinControlDialog::buttonBoxClicked);
// (un)select all
connect(ui->pushButtonSelectAll, &QPushButton::clicked, this, &CoinControlDialog::buttonSelectAllClicked);
ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84);
ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 110);
ui->treeWidget->setColumnWidth(COLUMN_LABEL, 190);
ui->treeWidget->setColumnWidth(COLUMN_ADDRESS, 320);
ui->treeWidget->setColumnWidth(COLUMN_DATE, 130);
ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
// restore list mode and sortorder as a convenience feature
QSettings settings;
if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool())
ui->radioTreeMode->click();
if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt())));
GUIUtil::handleCloseWindowShortcut(this);
if(_model->getOptionsModel() && _model->getAddressTableModel())
{
updateView();
updateLabelLocked();
CoinControlDialog::updateLabels(m_coin_control, _model, this);
}
}
CoinControlDialog::~CoinControlDialog()
{
QSettings settings;
settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
settings.setValue("nCoinControlSortColumn", sortColumn);
settings.setValue("nCoinControlSortOrder", (int)sortOrder);
delete ui;
}
// ok button
void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
{
if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
done(QDialog::Accepted); // closes the dialog
}
// (un)select all
void CoinControlDialog::buttonSelectAllClicked()
{
Qt::CheckState state = Qt::Checked;
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
{
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
{
state = Qt::Unchecked;
break;
}
}
ui->treeWidget->setEnabled(false);
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
ui->treeWidget->setEnabled(true);
if (state == Qt::Unchecked)
m_coin_control.UnSelectAll(); // just to be sure
CoinControlDialog::updateLabels(m_coin_control, model, this);
}
// context menu
void CoinControlDialog::showMenu(const QPoint &point)
{
QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
if(item)
{
contextMenuItem = item;
// disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
copyTransactionHashAction->setEnabled(true);
if (model->wallet().isLockedCoin(COutPoint(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())))
{
lockAction->setEnabled(false);
unlockAction->setEnabled(true);
}
else
{
lockAction->setEnabled(true);
unlockAction->setEnabled(false);
}
}
else // this means click on parent node in tree mode -> disable all
{
copyTransactionHashAction->setEnabled(false);
lockAction->setEnabled(false);
unlockAction->setEnabled(false);
}
// show context menu
contextMenu->exec(QCursor::pos());
}
}
// context menu action: copy amount
void CoinControlDialog::copyAmount()
{
GUIUtil::setClipboard(DigiByteUnits::removeSpaces(contextMenuItem->text(COLUMN_AMOUNT)));
}
// context menu action: copy label
void CoinControlDialog::copyLabel()
{
if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_LABEL));
else
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_LABEL));
}
// context menu action: copy address
void CoinControlDialog::copyAddress()
{
if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
GUIUtil::setClipboard(contextMenuItem->parent()->text(COLUMN_ADDRESS));
else
GUIUtil::setClipboard(contextMenuItem->text(COLUMN_ADDRESS));
}
// context menu action: copy transaction id
void CoinControlDialog::copyTransactionHash()
{
GUIUtil::setClipboard(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString());
}
// context menu action: lock coin
void CoinControlDialog::lockCoin()
{
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
model->wallet().lockCoin(outpt);
contextMenuItem->setDisabled(true);
contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
updateLabelLocked();
}
// context menu action: unlock coin
void CoinControlDialog::unlockCoin()
{
COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
model->wallet().unlockCoin(outpt);
contextMenuItem->setDisabled(false);
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
updateLabelLocked();
}
// copy label "Quantity" to clipboard
void CoinControlDialog::clipboardQuantity()
{
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
}
// copy label "Amount" to clipboard
void CoinControlDialog::clipboardAmount()
{
GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
}
// copy label "Fee" to clipboard
void CoinControlDialog::clipboardFee()
{
GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
}
// copy label "After fee" to clipboard
void CoinControlDialog::clipboardAfterFee()
{
GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
}
// copy label "Bytes" to clipboard
void CoinControlDialog::clipboardBytes()
{
GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
}
// copy label "Dust" to clipboard
void CoinControlDialog::clipboardLowOutput()
{
GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
}
// copy label "Change" to clipboard
void CoinControlDialog::clipboardChange()
{
GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
}
// treeview: sort
void CoinControlDialog::sortView(int column, Qt::SortOrder order)
{
sortColumn = column;
sortOrder = order;
ui->treeWidget->sortItems(column, order);
ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
}
// treeview: clicked on header
void CoinControlDialog::headerSectionClicked(int logicalIndex)
{
if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
{
ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
}
else
{
if (sortColumn == logicalIndex)
sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
else
{
sortColumn = logicalIndex;
sortOrder = ((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS) ? Qt::AscendingOrder : Qt::DescendingOrder); // if label or address then default => asc, else default => desc
}
sortView(sortColumn, sortOrder);
}
}
// toggle tree mode
void CoinControlDialog::radioTreeMode(bool checked)
{
if (checked && model)
updateView();
}
// toggle list mode
void CoinControlDialog::radioListMode(bool checked)
{
if (checked && model)
updateView();
}
// checkbox clicked by user
void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
{
if (column == COLUMN_CHECKBOX && item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means it is a child node, so it is not a parent node in tree mode)
{
COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
m_coin_control.UnSelect(outpt);
else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
else
m_coin_control.Select(outpt);
// selection changed -> update labels
if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
CoinControlDialog::updateLabels(m_coin_control, model, this);
}
}
// shows count of locked unspent outputs
void CoinControlDialog::updateLabelLocked()
{
std::vector<COutPoint> vOutpts;
model->wallet().listLockedCoins(vOutpts);
if (vOutpts.size() > 0)
{
ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
ui->labelLocked->setVisible(true);
}
else ui->labelLocked->setVisible(false);
}
void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *model, QDialog* dialog)
{
if (!model)
return;
// nPayAmount
CAmount nPayAmount = 0;
bool fDust = false;
for (const CAmount &amount : CoinControlDialog::payAmounts)
{
nPayAmount += amount;
if (amount > 0)
{
// Assumes a p2pkh script size
CTxOut txout(amount, CScript() << std::vector<unsigned char>(24, 0));
fDust |= IsDust(txout, model->node().getDustRelayFee());
}
}
CAmount nAmount = 0;
CAmount nPayFee = 0;
CAmount nAfterFee = 0;
CAmount nChange = 0;
unsigned int nBytes = 0;
unsigned int nBytesInputs = 0;
unsigned int nQuantity = 0;
bool fWitness = false;
std::vector<COutPoint> vCoinControl;
m_coin_control.ListSelected(vCoinControl);
size_t i = 0;
for (const auto& out : model->wallet().getCoins(vCoinControl)) {
if (out.depth_in_main_chain < 0) continue;
// unselect already spent, very unlikely scenario, this could happen
// when selected are spent elsewhere, like rpc or another computer
const COutPoint& outpt = vCoinControl[i++];
if (out.is_spent)
{
m_coin_control.UnSelect(outpt);
continue;
}
// Quantity
nQuantity++;
// Amount
nAmount += out.txout.nValue;
// Bytes
CTxDestination address;
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
if (out.txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
{
nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
fWitness = true;
}
else if(ExtractDestination(out.txout.scriptPubKey, address))
{
CPubKey pubkey;
PKHash* pkhash = std::get_if<PKHash>(&address);
if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
else
nBytesInputs += 148; // in all error cases, simply assume 148 here
}
else nBytesInputs += 148;
}
// calculation
if (nQuantity > 0)
{
// Bytes
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
if (fWitness)
{
// there is some fudging in these numbers related to the actual virtual transaction size calculation that will keep this estimate from being exact.
// usually, the result will be an overestimate within a couple of satoshis so that the confirmation dialog ends up displaying a slightly smaller fee.
// also, the witness stack size value is a variable sized integer. usually, the number of stack items will be well under the single byte var int limit.
nBytes += 2; // account for the serialized marker and flag bytes
nBytes += nQuantity; // account for the witness byte that holds the number of stack items for each input.
}
// 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)
nBytes -= 34;
// Fee
nPayFee = model->wallet().getMinimumFee(nBytes, m_coin_control, nullptr /* returned_target */, nullptr /* reason */);
if (nPayAmount > 0)
{
nChange = nAmount - nPayAmount;
if (!CoinControlDialog::fSubtractFeeFromAmount)
nChange -= nPayFee;
// Never create dust outputs; if we would, just add the dust to the fee.
if (nChange > 0 && nChange < MIN_CHANGE)
{
// Assumes a p2pkh script size
CTxOut txout(nChange, CScript() << std::vector<unsigned char>(24, 0));
if (IsDust(txout, model->node().getDustRelayFee()))
{
nPayFee += nChange;
nChange = 0;
if (CoinControlDialog::fSubtractFeeFromAmount)
nBytes -= 34; // we didn't detect lack of change above
}
}
if (nChange == 0 && !CoinControlDialog::fSubtractFeeFromAmount)
nBytes -= 34;
}
// after fee
nAfterFee = std::max<CAmount>(nAmount - nPayFee, 0);
}
// actually update labels
int nDisplayUnit = DigiByteUnits::DGB;
if (model && model->getOptionsModel())
nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
// enable/disable "dust" and "change"
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
// stats
l1->setText(QString::number(nQuantity)); // Quantity
l2->setText(DigiByteUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
l3->setText(DigiByteUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
l4->setText(DigiByteUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes
l7->setText(fDust ? tr("yes") : tr("no")); // Dust
l8->setText(DigiByteUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
if (nPayFee > 0)
{
l3->setText(ASYMP_UTF8 + l3->text());
l4->setText(ASYMP_UTF8 + l4->text());
if (nChange > 0 && !CoinControlDialog::fSubtractFeeFromAmount)
l8->setText(ASYMP_UTF8 + l8->text());
}
// turn label red when dust
l7->setStyleSheet((fDust) ? "color:red;" : "");
// tool tips
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 = (nBytes != 0) ? (double)nPayFee / nBytes : 0;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
l4->setToolTip(toolTip4);
l7->setToolTip(toolTipDust);
l8->setToolTip(toolTip4);
dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip());
dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip());
dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
// Insufficient funds
QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
if (label)
label->setVisible(nChange < 0);
}
void CoinControlDialog::changeEvent(QEvent* e)
{
if (e->type() == QEvent::PaletteChange) {
updateView();
}
QDialog::changeEvent(e);
}
void CoinControlDialog::updateView()
{
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())
return;
bool treeMode = ui->radioTreeMode->isChecked();
ui->treeWidget->clear();
ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
ui->treeWidget->setAlternatingRowColors(!treeMode);
QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
for (const auto& coins : model->wallet().listCoins()) {
CCoinControlWidgetItem* itemWalletAddress{nullptr};
QString sWalletAddress = QString::fromStdString(EncodeDestination(coins.first));
QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
if (sWalletLabel.isEmpty())
sWalletLabel = tr("(no label)");
if (treeMode)
{
// wallet address
itemWalletAddress = new CCoinControlWidgetItem(ui->treeWidget);
itemWalletAddress->setFlags(flgTristate);
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
// label
itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
// address
itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
}
CAmount nSum = 0;
int nChildren = 0;
for (const auto& outpair : coins.second) {
const COutPoint& output = std::get<0>(outpair);
const interfaces::WalletTxOut& out = std::get<1>(outpair);
nSum += out.txout.nValue;
nChildren++;
CCoinControlWidgetItem *itemOutput;
if (treeMode) itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
else itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
itemOutput->setFlags(flgCheckbox);
itemOutput->setCheckState(COLUMN_CHECKBOX,Qt::Unchecked);
// address
CTxDestination outputAddress;
QString sAddress = "";
if(ExtractDestination(out.txout.scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(EncodeDestination(outputAddress));
// if listMode or change => show Vexta address. In tree mode, address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress)))
itemOutput->setText(COLUMN_ADDRESS, sAddress);
}
// label
if (!(sAddress == sWalletAddress)) // change
{
// tooltip from where the change comes from
itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
itemOutput->setText(COLUMN_LABEL, tr("(change)"));
}
else if (!treeMode)
{
QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
if (sLabel.isEmpty())
sLabel = tr("(no label)");
itemOutput->setText(COLUMN_LABEL, sLabel);
}
// amount
itemOutput->setText(COLUMN_AMOUNT, DigiByteUnits::format(nDisplayUnit, out.txout.nValue));
itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.txout.nValue)); // padding so that sorting works correctly
// date
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.time));
itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.time));
// confirmations
itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.depth_in_main_chain));
itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.depth_in_main_chain));
// transaction hash
itemOutput->setData(COLUMN_ADDRESS, TxHashRole, QString::fromStdString(output.hash.GetHex()));
// vout index
itemOutput->setData(COLUMN_ADDRESS, VOutRole, output.n);
// disable locked coins
if (model->wallet().isLockedCoin(output))
{
m_coin_control.UnSelect(output); // just to be sure
itemOutput->setDisabled(true);
itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed"));
}
// set checkbox
if (m_coin_control.IsSelected(output))
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
}
// amount
if (treeMode)
{
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, DigiByteUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)nSum));
}
}
// expand all partially selected
if (treeMode)
{
for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
ui->treeWidget->topLevelItem(i)->setExpanded(true);
}
// sort view
sortView(sortColumn, sortOrder);
ui->treeWidget->setEnabled(true);
}

118
src/qt/coincontroldialog.h Normal file
View file

@ -0,0 +1,118 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_COINCONTROLDIALOG_H
#define DIGIBYTE_QT_COINCONTROLDIALOG_H
#include <amount.h>
#include <QAbstractButton>
#include <QAction>
#include <QDialog>
#include <QList>
#include <QMenu>
#include <QPoint>
#include <QString>
#include <QTreeWidgetItem>
class PlatformStyle;
class WalletModel;
class CCoinControl;
namespace Ui {
class CoinControlDialog;
}
#define ASYMP_UTF8 "\xE2\x89\x88"
class CCoinControlWidgetItem : public QTreeWidgetItem
{
public:
explicit CCoinControlWidgetItem(QTreeWidget *parent, int type = Type) : QTreeWidgetItem(parent, type) {}
explicit CCoinControlWidgetItem(QTreeWidgetItem *parent, int type = Type) : QTreeWidgetItem(parent, type) {}
bool operator<(const QTreeWidgetItem &other) const override;
};
class CoinControlDialog : public QDialog
{
Q_OBJECT
public:
explicit CoinControlDialog(CCoinControl& coin_control, WalletModel* model, const PlatformStyle *platformStyle, QWidget *parent = nullptr);
~CoinControlDialog();
// static because also called from sendcoinsdialog
static void updateLabels(CCoinControl& m_coin_control, WalletModel*, QDialog*);
static QList<CAmount> payAmounts;
static bool fSubtractFeeFromAmount;
protected:
void changeEvent(QEvent* e) override;
private:
Ui::CoinControlDialog *ui;
CCoinControl& m_coin_control;
WalletModel *model;
int sortColumn;
Qt::SortOrder sortOrder;
QMenu *contextMenu;
QTreeWidgetItem *contextMenuItem;
QAction *copyTransactionHashAction;
QAction *lockAction;
QAction *unlockAction;
const PlatformStyle *platformStyle;
void sortView(int, Qt::SortOrder);
void updateView();
enum
{
COLUMN_CHECKBOX = 0,
COLUMN_AMOUNT,
COLUMN_LABEL,
COLUMN_ADDRESS,
COLUMN_DATE,
COLUMN_CONFIRMATIONS,
};
enum
{
TxHashRole = Qt::UserRole,
VOutRole
};
friend class CCoinControlWidgetItem;
private Q_SLOTS:
void showMenu(const QPoint &);
void copyAmount();
void copyLabel();
void copyAddress();
void copyTransactionHash();
void lockCoin();
void unlockCoin();
void clipboardQuantity();
void clipboardAmount();
void clipboardFee();
void clipboardAfterFee();
void clipboardBytes();
void clipboardLowOutput();
void clipboardChange();
void radioTreeMode(bool);
void radioListMode(bool);
void viewItemChanged(QTreeWidgetItem*, int);
void headerSectionClicked(int);
void buttonBoxClicked(QAbstractButton*);
void buttonSelectAllClicked();
void updateLabelLocked();
};
#endif // DIGIBYTE_QT_COINCONTROLDIALOG_H

View file

@ -0,0 +1,35 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/coincontroltreewidget.h>
#include <qt/coincontroldialog.h>
CoinControlTreeWidget::CoinControlTreeWidget(QWidget *parent) :
QTreeWidget(parent)
{
}
void CoinControlTreeWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Space) // press spacebar -> select checkbox
{
event->ignore();
if (this->currentItem()) {
int COLUMN_CHECKBOX = 0;
this->currentItem()->setCheckState(COLUMN_CHECKBOX, ((this->currentItem()->checkState(COLUMN_CHECKBOX) == Qt::Checked) ? Qt::Unchecked : Qt::Checked));
}
}
else if (event->key() == Qt::Key_Escape) // press esc -> close dialog
{
event->ignore();
CoinControlDialog *coinControlDialog = static_cast<CoinControlDialog*>(this->parentWidget());
coinControlDialog->done(QDialog::Accepted);
}
else
{
this->QTreeWidget::keyPressEvent(event);
}
}

View file

@ -0,0 +1,23 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_COINCONTROLTREEWIDGET_H
#define DIGIBYTE_QT_COINCONTROLTREEWIDGET_H
#include <QKeyEvent>
#include <QTreeWidget>
class CoinControlTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
explicit CoinControlTreeWidget(QWidget *parent = nullptr);
protected:
virtual void keyPressEvent(QKeyEvent *event) override;
};
#endif // DIGIBYTE_QT_COINCONTROLTREEWIDGET_H

View file

@ -0,0 +1,149 @@
// Copyright (c) 2019-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <external_signer.h>
#include <qt/createwalletdialog.h>
#include <qt/forms/ui_createwalletdialog.h>
#include <qt/guiutil.h>
#include <QPushButton>
CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::CreateWalletDialog)
{
ui->setupUi(this);
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Create"));
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
ui->wallet_name_line_edit->setFocus(Qt::ActiveWindowFocusReason);
connect(ui->wallet_name_line_edit, &QLineEdit::textEdited, [this](const QString& text) {
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
});
connect(ui->encrypt_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) {
// Disable the disable_privkeys_checkbox and external_signer_checkbox when isEncryptWalletChecked is
// set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
#ifdef ENABLE_EXTERNAL_SIGNER
ui->external_signer_checkbox->setEnabled(!checked);
#endif
// When the disable_privkeys_checkbox is disabled, uncheck it.
if (!ui->disable_privkeys_checkbox->isEnabled()) {
ui->disable_privkeys_checkbox->setChecked(false);
}
// When the external_signer_checkbox box is disabled, uncheck it.
if (!ui->external_signer_checkbox->isEnabled()) {
ui->external_signer_checkbox->setChecked(false);
}
});
connect(ui->external_signer_checkbox, &QCheckBox::toggled, [this](bool checked) {
ui->encrypt_wallet_checkbox->setEnabled(!checked);
ui->blank_wallet_checkbox->setEnabled(!checked);
ui->disable_privkeys_checkbox->setEnabled(!checked);
// The external signer checkbox is only enabled when a device is detected.
// In that case it is checked by default. Toggling it restores the other
// options to their default.
ui->encrypt_wallet_checkbox->setChecked(false);
ui->disable_privkeys_checkbox->setChecked(checked);
// The blank check box is ambiguous. This flag is always true for a
// watch-only wallet, even though we immedidately fetch keys from the
// external signer.
ui->blank_wallet_checkbox->setChecked(checked);
});
connect(ui->disable_privkeys_checkbox, &QCheckBox::toggled, [this](bool checked) {
// Disable the encrypt_wallet_checkbox when isDisablePrivateKeysChecked is
// set to true, enable it when isDisablePrivateKeysChecked is false.
ui->encrypt_wallet_checkbox->setEnabled(!checked);
// Wallets without private keys start out blank
if (checked) {
ui->blank_wallet_checkbox->setChecked(true);
}
// When the encrypt_wallet_checkbox is disabled, uncheck it.
if (!ui->encrypt_wallet_checkbox->isEnabled()) {
ui->encrypt_wallet_checkbox->setChecked(false);
}
});
connect(ui->blank_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) {
if (!checked) {
ui->disable_privkeys_checkbox->setChecked(false);
}
});
#ifndef USE_SQLITE
ui->external_signer_checkbox->setEnabled(false);
ui->external_signer_checkbox->setChecked(false);
#endif
#ifndef ENABLE_EXTERNAL_SIGNER
//: "External signing" means using devices such as hardware wallets.
ui->external_signer_checkbox->setToolTip(tr("Compiled without external signing support (required for external signing)"));
ui->external_signer_checkbox->setEnabled(false);
ui->external_signer_checkbox->setChecked(false);
#endif
}
CreateWalletDialog::~CreateWalletDialog()
{
delete ui;
}
void CreateWalletDialog::setSigners(const std::vector<ExternalSigner>& signers)
{
if (!signers.empty()) {
ui->external_signer_checkbox->setEnabled(true);
ui->external_signer_checkbox->setChecked(true);
ui->encrypt_wallet_checkbox->setEnabled(false);
ui->encrypt_wallet_checkbox->setChecked(false);
// The order matters, because connect() is called when toggling a checkbox:
ui->blank_wallet_checkbox->setEnabled(false);
ui->blank_wallet_checkbox->setChecked(false);
ui->disable_privkeys_checkbox->setEnabled(false);
ui->disable_privkeys_checkbox->setChecked(true);
const std::string label = signers[0].m_name;
ui->wallet_name_line_edit->setText(QString::fromStdString(label));
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
} else {
ui->external_signer_checkbox->setEnabled(false);
}
}
QString CreateWalletDialog::walletName() const
{
return ui->wallet_name_line_edit->text();
}
bool CreateWalletDialog::isEncryptWalletChecked() const
{
return ui->encrypt_wallet_checkbox->isChecked();
}
bool CreateWalletDialog::isDisablePrivateKeysChecked() const
{
return ui->disable_privkeys_checkbox->isChecked();
}
bool CreateWalletDialog::isMakeBlankWalletChecked() const
{
return ui->blank_wallet_checkbox->isChecked();
}
bool CreateWalletDialog::isExternalSignerChecked() const
{
return ui->external_signer_checkbox->isChecked();
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2019 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_CREATEWALLETDIALOG_H
#define DIGIBYTE_QT_CREATEWALLETDIALOG_H
#include <QDialog>
class ExternalSigner;
class WalletModel;
namespace Ui {
class CreateWalletDialog;
}
/** Dialog for creating wallets
*/
class CreateWalletDialog : public QDialog
{
Q_OBJECT
public:
explicit CreateWalletDialog(QWidget* parent);
virtual ~CreateWalletDialog();
void setSigners(const std::vector<ExternalSigner>& signers);
QString walletName() const;
bool isEncryptWalletChecked() const;
bool isDisablePrivateKeysChecked() const;
bool isMakeBlankWalletChecked() const;
bool isDescriptorWalletChecked() const;
bool isExternalSignerChecked() const;
private:
Ui::CreateWalletDialog *ui;
};
#endif // DIGIBYTE_QT_CREATEWALLETDIALOG_H

92
src/qt/csvmodelwriter.cpp Normal file
View file

@ -0,0 +1,92 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/csvmodelwriter.h>
#include <QAbstractItemModel>
#include <QFile>
#include <QTextStream>
CSVModelWriter::CSVModelWriter(const QString &_filename, QObject *parent) :
QObject(parent),
filename(_filename), model(nullptr)
{
}
void CSVModelWriter::setModel(const QAbstractItemModel *_model)
{
this->model = _model;
}
void CSVModelWriter::addColumn(const QString &title, int column, int role)
{
Column col;
col.title = title;
col.column = column;
col.role = role;
columns.append(col);
}
static void writeValue(QTextStream &f, const QString &value)
{
QString escaped = value;
escaped.replace('"', "\"\"");
f << "\"" << escaped << "\"";
}
static void writeSep(QTextStream &f)
{
f << ",";
}
static void writeNewline(QTextStream &f)
{
f << "\n";
}
bool CSVModelWriter::write()
{
QFile file(filename);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QTextStream out(&file);
int numRows = 0;
if(model)
{
numRows = model->rowCount();
}
// Header row
for(int i=0; i<columns.size(); ++i)
{
if(i!=0)
{
writeSep(out);
}
writeValue(out, columns[i].title);
}
writeNewline(out);
// Data rows
for(int j=0; j<numRows; ++j)
{
for(int i=0; i<columns.size(); ++i)
{
if(i!=0)
{
writeSep(out);
}
QVariant data = model->index(j, columns[i].column).data(columns[i].role);
writeValue(out, data.toString());
}
writeNewline(out);
}
file.close();
return file.error() == QFile::NoError;
}

47
src/qt/csvmodelwriter.h Normal file
View file

@ -0,0 +1,47 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_CSVMODELWRITER_H
#define DIGIBYTE_QT_CSVMODELWRITER_H
#include <QList>
#include <QObject>
QT_BEGIN_NAMESPACE
class QAbstractItemModel;
QT_END_NAMESPACE
/** Export a Qt table model to a CSV file. This is useful for analyzing or post-processing the data in
a spreadsheet.
*/
class CSVModelWriter : public QObject
{
Q_OBJECT
public:
explicit CSVModelWriter(const QString &filename, QObject *parent = nullptr);
void setModel(const QAbstractItemModel *model);
void addColumn(const QString &title, int column, int role=Qt::EditRole);
/** Perform export of the model to CSV.
@returns true on success, false otherwise
*/
bool write();
private:
QString filename;
const QAbstractItemModel *model;
struct Column
{
QString title;
int column;
int role;
};
QList<Column> columns;
};
#endif // DIGIBYTE_QT_CSVMODELWRITER_H

599
src/qt/digibyte.cpp Normal file
View file

@ -0,0 +1,599 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <qt/digibyte.h>
#include <chainparams.h>
#include <init.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <node/context.h>
#include <node/ui_interface.h>
#include <noui.h>
#include <qt/digibytegui.h>
#include <qt/clientmodel.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/initexecutor.h>
#include <qt/intro.h>
#include <qt/networkstyle.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/splashscreen.h>
#include <qt/utilitydialog.h>
#include <qt/winshutdownmonitor.h>
#include <uint256.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
#ifdef ENABLE_WALLET
#include <qt/paymentserver.h>
#include <qt/walletcontroller.h>
#include <qt/walletmodel.h>
#endif // ENABLE_WALLET
#include <boost/signals2/connection.hpp>
#include <memory>
#include <QApplication>
#include <QDebug>
#include <QFontDatabase>
#include <QLatin1String>
#include <QLibraryInfo>
#include <QLocale>
#include <QMessageBox>
#include <QSettings>
#include <QThread>
#include <QTimer>
#include <QTranslator>
#if defined(QT_STATICPLUGIN)
#include <QtPlugin>
#if defined(QT_QPA_PLATFORM_XCB)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
#elif defined(QT_QPA_PLATFORM_WINDOWS)
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);
#elif defined(QT_QPA_PLATFORM_COCOA)
Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
Q_IMPORT_PLUGIN(QMacStylePlugin);
#endif
#endif
// Declare meta types used for QMetaObject::invokeMethod
Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
Q_DECLARE_METATYPE(SynchronizationState)
Q_DECLARE_METATYPE(uint256)
static void RegisterMetaTypes()
{
// Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
qRegisterMetaType<bool*>();
qRegisterMetaType<SynchronizationState>();
#ifdef ENABLE_WALLET
qRegisterMetaType<WalletModel*>();
#endif
// Register typedefs (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT: if CAmount is no longer a typedef use the normal variant above (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
qRegisterMetaType<CAmount>("CAmount");
qRegisterMetaType<size_t>("size_t");
qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
}
static QString GetLangTerritory()
{
QSettings settings;
// Get desired locale (e.g. "de_DE")
// 1) System default language
QString lang_territory = QLocale::system().name();
// 2) Language from QSettings
QString lang_territory_qsettings = settings.value("language", "").toString();
if(!lang_territory_qsettings.isEmpty())
lang_territory = lang_territory_qsettings;
// 3) -lang command line argument
lang_territory = QString::fromStdString(gArgs.GetArg("-lang", lang_territory.toStdString()));
return lang_territory;
}
/** Set up translations */
static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator)
{
// Remove old translators
QApplication::removeTranslator(&qtTranslatorBase);
QApplication::removeTranslator(&qtTranslator);
QApplication::removeTranslator(&translatorBase);
QApplication::removeTranslator(&translator);
// Get desired locale (e.g. "de_DE")
// 1) System default language
QString lang_territory = GetLangTerritory();
// Convert to "de" only by truncating "_DE"
QString lang = lang_territory;
lang.truncate(lang_territory.lastIndexOf('_'));
// Load language files for configured locale:
// - First load the translator for the base language, without territory
// - Then load the more specific locale translator
// Load e.g. qt_de.qm
if (qtTranslatorBase.load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
QApplication::installTranslator(&qtTranslatorBase);
// Load e.g. qt_de_DE.qm
if (qtTranslator.load("qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
QApplication::installTranslator(&qtTranslator);
// Load e.g. digibyte_de.qm (shortcut "de" needs to be defined in vexta.qrc)
if (translatorBase.load(lang, ":/translations/"))
QApplication::installTranslator(&translatorBase);
// Load e.g. digibyte_de_DE.qm (shortcut "de_DE" needs to be defined in vexta.qrc)
if (translator.load(lang_territory, ":/translations/"))
QApplication::installTranslator(&translator);
}
/* qDebug() message handler --> debug.log */
void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
{
Q_UNUSED(context);
if (type == QtDebugMsg) {
LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString());
} else {
LogPrintf("GUI: %s\n", msg.toStdString());
}
}
static int qt_argc = 1;
static const char* qt_argv = "vexta-qt";
DigiByteApplication::DigiByteApplication():
QApplication(qt_argc, const_cast<char **>(&qt_argv)),
optionsModel(nullptr),
clientModel(nullptr),
window(nullptr),
pollShutdownTimer(nullptr),
returnValue(0),
platformStyle(nullptr)
{
// Qt runs setlocale(LC_ALL, "") on initialization.
RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
}
void DigiByteApplication::setupPlatformStyle()
{
// UI per-platform customization
// This must be done inside the DigiByteApplication constructor, or after it, because
// PlatformStyle::instantiate requires a QApplication
std::string platformName;
platformName = gArgs.GetArg("-uiplatform", DigiByteGUI::DEFAULT_UIPLATFORM);
platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName));
if (!platformStyle) // Fall back to "other" if specified name not found
platformStyle = PlatformStyle::instantiate("other");
assert(platformStyle);
}
DigiByteApplication::~DigiByteApplication()
{
m_executor.reset();
delete window;
window = nullptr;
delete platformStyle;
platformStyle = nullptr;
}
#ifdef ENABLE_WALLET
void DigiByteApplication::createPaymentServer()
{
paymentServer = new PaymentServer(this);
}
#endif
void DigiByteApplication::createOptionsModel(bool resetSettings)
{
optionsModel = new OptionsModel(this, resetSettings);
}
void DigiByteApplication::createWindow(const NetworkStyle *networkStyle)
{
window = new DigiByteGUI(node(), platformStyle, networkStyle, nullptr);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, &QTimer::timeout, window, &DigiByteGUI::detectShutdown);
}
void DigiByteApplication::createSplashScreen(const NetworkStyle *networkStyle)
{
assert(!m_splash);
m_splash = new SplashScreen(networkStyle);
// We don't hold a direct pointer to the splash screen after creation, but the splash
// screen will take care of deleting itself when finish() happens.
m_splash->show();
connect(this, &DigiByteApplication::requestedInitialize, m_splash, &SplashScreen::handleLoadWallet);
connect(this, &DigiByteApplication::splashFinished, m_splash, &SplashScreen::finish);
connect(this, &DigiByteApplication::requestedShutdown, m_splash, &QWidget::close);
}
void DigiByteApplication::setNode(interfaces::Node& node)
{
assert(!m_node);
m_node = &node;
if (optionsModel) optionsModel->setNode(*m_node);
if (m_splash) m_splash->setNode(*m_node);
}
bool DigiByteApplication::baseInitialize()
{
return node().baseInitialize();
}
void DigiByteApplication::startThread()
{
assert(!m_executor);
m_executor.emplace(node());
/* communication to and from thread */
connect(&m_executor.value(), &InitExecutor::initializeResult, this, &DigiByteApplication::initializeResult);
connect(&m_executor.value(), &InitExecutor::shutdownResult, this, &DigiByteApplication::shutdownResult);
connect(&m_executor.value(), &InitExecutor::runawayException, this, &DigiByteApplication::handleRunawayException);
connect(this, &DigiByteApplication::requestedInitialize, &m_executor.value(), &InitExecutor::initialize);
connect(this, &DigiByteApplication::requestedShutdown, &m_executor.value(), &InitExecutor::shutdown);
}
void DigiByteApplication::parameterSetup()
{
// Default printtoconsole to false for the GUI. GUI programs should not
// print to the console unnecessarily.
gArgs.SoftSetBoolArg("-printtoconsole", false);
InitLogging(gArgs);
InitParameterInteraction(gArgs);
}
void DigiByteApplication::InitPruneSetting(int64_t prune_MiB)
{
optionsModel->SetPruneTargetGB(PruneMiBtoGB(prune_MiB), true);
}
void DigiByteApplication::requestInitialize()
{
qDebug() << __func__ << ": Requesting initialize";
startThread();
Q_EMIT requestedInitialize();
}
void DigiByteApplication::requestShutdown()
{
// Show a simple window indicating shutdown status
// Do this first as some of the steps may take some time below,
// for example the RPC console may still be executing a command.
shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window));
qDebug() << __func__ << ": Requesting shutdown";
window->hide();
// Must disconnect node signals otherwise current thread can deadlock since
// no event loop is running.
window->unsubscribeFromCoreSignals();
// Request node shutdown, which can interrupt long operations, like
// rescanning a wallet.
node().startShutdown();
// Unsetting the client model can cause the current thread to wait for node
// to complete an operation, like wait for a RPC execution to complete.
window->setClientModel(nullptr);
pollShutdownTimer->stop();
delete clientModel;
clientModel = nullptr;
// Request shutdown from core thread
Q_EMIT requestedShutdown();
}
void DigiByteApplication::initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info)
{
qDebug() << __func__ << ": Initialization result: " << success;
// Set exit result.
returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE;
if(success)
{
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
qInfo() << "Platform customization:" << platformStyle->getName();
clientModel = new ClientModel(node(), optionsModel);
window->setClientModel(clientModel, &tip_info);
#ifdef ENABLE_WALLET
if (WalletModel::isWalletEnabled()) {
m_wallet_controller = new WalletController(*clientModel, platformStyle, this);
window->setWalletController(m_wallet_controller);
if (paymentServer) {
paymentServer->setOptionsModel(optionsModel);
}
}
#endif // ENABLE_WALLET
// If -min option passed, start window minimized (iconified) or minimized to tray
if (!gArgs.GetBoolArg("-min", false)) {
window->show();
} else if (clientModel->getOptionsModel()->getMinimizeToTray() && window->hasTrayIcon()) {
// do nothing as the window is managed by the tray icon
} else {
window->showMinimized();
}
Q_EMIT splashFinished();
Q_EMIT windowShown(window);
#ifdef ENABLE_WALLET
// Now that initialization/startup is done, process any command-line
// vexta: URIs or payment requests:
if (paymentServer) {
connect(paymentServer, &PaymentServer::receivedPaymentRequest, window, &DigiByteGUI::handlePaymentRequest);
connect(window, &DigiByteGUI::receivedURI, paymentServer, &PaymentServer::handleURIOrFile);
connect(paymentServer, &PaymentServer::message, [this](const QString& title, const QString& message, unsigned int style) {
window->message(title, message, style);
});
QTimer::singleShot(100, paymentServer, &PaymentServer::uiReady);
}
#endif
pollShutdownTimer->start(200);
} else {
Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
quit(); // Exit first main loop invocation
}
}
void DigiByteApplication::shutdownResult()
{
quit(); // Exit second main loop invocation after shutdown finished
}
void DigiByteApplication::handleRunawayException(const QString &message)
{
QMessageBox::critical(
nullptr, tr("Runaway exception"),
tr("A fatal error occurred. %1 can no longer continue safely and will quit.").arg(PACKAGE_NAME) +
QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
::exit(EXIT_FAILURE);
}
void DigiByteApplication::handleNonFatalException(const QString& message)
{
assert(QThread::currentThread() == thread());
QMessageBox::warning(
nullptr, tr("Internal error"),
tr("An internal error occurred. %1 will attempt to continue safely. This is "
"an unexpected bug which can be reported as described below.").arg(PACKAGE_NAME) +
QLatin1String("<br><br>") + GUIUtil::MakeHtmlLink(message, PACKAGE_BUGREPORT));
}
WId DigiByteApplication::getMainWinId() const
{
if (!window)
return 0;
return window->winId();
}
static void SetupUIArgs(ArgsManager& argsman)
{
argsman.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
argsman.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
argsman.AddArg("-min", "Start minimized", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
argsman.AddArg("-resetguisettings", "Reset all settings changed in the GUI", ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
argsman.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
argsman.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", DigiByteGUI::DEFAULT_UIPLATFORM), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::GUI);
}
int GuiMain(int argc, char* argv[])
{
#ifdef WIN32
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
SetupEnvironment();
util::ThreadSetInternalName("main");
NodeContext node_context;
std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(&node_context);
// Subscribe to global signals from core
boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage);
// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
/// 1. Basic Qt initialization (not dependent on parameters or configuration)
Q_INIT_RESOURCE(digibyte);
Q_INIT_RESOURCE(vexta_locale);
// Generate high-dpi pixmaps
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#if defined(QT_QPA_PLATFORM_ANDROID)
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
#endif
DigiByteApplication app;
QFontDatabase::addApplicationFont(":/fonts/monospace");
/// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these
// Command-line options take precedence:
node_context.args = &gArgs;
SetupServerArgs(gArgs);
SetupUIArgs(gArgs);
std::string error;
if (!gArgs.ParseParameters(argc, argv, error)) {
InitError(strprintf(Untranslated("Error parsing command line arguments: %s\n"), error));
// Create a message box, because the gui has neither been created nor has subscribed to core signals
QMessageBox::critical(nullptr, PACKAGE_NAME,
// message can not be translated because translations have not been initialized
QString::fromStdString("Error parsing command line arguments: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
// Now that the QApplication is setup and we have parsed our parameters, we can set the platform style
app.setupPlatformStyle();
/// 3. Application identification
// must be set before OptionsModel is initialized or translations are loaded,
// as it is used to locate QSettings
QApplication::setOrganizationName(QAPP_ORG_NAME);
QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN);
QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT);
/// 4. Initialization of translations, so that intro dialog is in user's language
// Now that QSettings are accessible, initialize translations
QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator;
initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
// Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen.
if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version"));
help.showOrPrint();
return EXIT_SUCCESS;
}
// Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
bool did_show_intro = false;
int64_t prune_MiB = 0; // Intro dialog prune configuration
// Gracefully exit if the user cancels
if (!Intro::showIfNeeded(did_show_intro, prune_MiB)) return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse Vexta.conf
/// - Do not call gArgs.GetDataDirNet() before this step finishes
if (!CheckDataDirOption()) {
InitError(strprintf(Untranslated("Specified data directory \"%s\" does not exist.\n"), gArgs.GetArg("-datadir", "")));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", ""))));
return EXIT_FAILURE;
}
if (!gArgs.ReadConfigFiles(error, true)) {
InitError(strprintf(Untranslated("Error reading configuration file: %s\n"), error));
QMessageBox::critical(nullptr, PACKAGE_NAME,
QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
/// 7. Determine network (and switch to network specific options)
// - Do not call Params() before this step
// - Do this after parsing the configuration file, as the network can be switched there
// - QSettings() will use the new application name after this, resulting in network-specific settings
// - Needs to be done before createOptionsModel
// Check for chain settings (Params() calls are only valid after this clause)
try {
SelectParams(gArgs.GetChainName());
} catch(std::exception &e) {
InitError(Untranslated(strprintf("%s\n", e.what())));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: %1").arg(e.what()));
return EXIT_FAILURE;
}
#ifdef ENABLE_WALLET
// Parse URIs on command line -- this can affect Params()
PaymentServer::ipcParseCommandLine(argc, argv);
#endif
if (!gArgs.InitSettings(error)) {
InitError(Untranslated(error));
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error initializing settings: %1").arg(QString::fromStdString(error)));
return EXIT_FAILURE;
}
QScopedPointer<const NetworkStyle> networkStyle(NetworkStyle::instantiate(Params().NetworkIDString()));
assert(!networkStyle.isNull());
// Allow for separate UI settings for testnets
QApplication::setApplicationName(networkStyle->getAppName());
// Re-initialize translations after changing application name (language in network-specific settings can be different)
initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
#ifdef ENABLE_WALLET
/// 8. URI IPC sending
// - Do this early as we don't want to bother initializing if we are just calling IPC
// - Do this *after* setting up the data directory, as the data directory hash is used in the name
// of the server.
// - Do this after creating app and setting up translations, so errors are
// translated properly.
if (PaymentServer::ipcSendCommandLine())
exit(EXIT_SUCCESS);
// Start up the payment server early, too, so impatient users that click on
// vexta: links repeatedly have their payment requests routed to this process:
if (WalletModel::isWalletEnabled()) {
app.createPaymentServer();
}
#endif // ENABLE_WALLET
/// 9. Main GUI initialization
// Install global event filter that makes sure that out-of-focus labels do not contain text cursor.
app.installEventFilter(new GUIUtil::LabelOutOfFocusEventFilter(&app));
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
#endif
// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler);
// Allow parameter interaction before we create the options model
app.parameterSetup();
GUIUtil::LogQtInfo();
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
if (did_show_intro) {
// Store intro dialog settings other than datadir (network specific)
app.InitPruneSetting(prune_MiB);
}
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
app.setNode(*node);
int rv = EXIT_SUCCESS;
try
{
app.createWindow(networkStyle.data());
// Perform base initialization before spinning up initialization/shutdown thread
// This is acceptable because this function only contains steps that are quick to execute,
// so the GUI thread won't be held up.
if (app.baseInitialize()) {
app.requestInitialize();
#if defined(Q_OS_WIN)
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely…").arg(PACKAGE_NAME), (HWND)app.getMainWinId());
#endif
app.exec();
app.requestShutdown();
app.exec();
rv = app.getReturnValue();
} else {
// A dialog with detailed error will have been shown by InitError()
rv = EXIT_FAILURE;
}
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
} catch (...) {
PrintExceptionContinue(nullptr, "Runaway exception");
app.handleRunawayException(QString::fromStdString(app.node().getWarnings().translated));
}
return rv;
}

114
src/qt/digibyte.h Normal file
View file

@ -0,0 +1,114 @@
// Copyright (c) 2011-2020 The DigiByte Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_DIGIBYTE_H
#define DIGIBYTE_QT_DIGIBYTE_H
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <interfaces/node.h>
#include <qt/initexecutor.h>
#include <assert.h>
#include <memory>
#include <optional>
#include <QApplication>
class DigiByteGUI;
class ClientModel;
class NetworkStyle;
class OptionsModel;
class PaymentServer;
class PlatformStyle;
class SplashScreen;
class WalletController;
class WalletModel;
/** Main DigiByte application object */
class DigiByteApplication: public QApplication
{
Q_OBJECT
public:
explicit DigiByteApplication();
~DigiByteApplication();
#ifdef ENABLE_WALLET
/// Create payment server
void createPaymentServer();
#endif
/// parameter interaction/setup based on rules
void parameterSetup();
/// Create options model
void createOptionsModel(bool resetSettings);
/// Initialize prune setting
void InitPruneSetting(int64_t prune_MiB);
/// Create main window
void createWindow(const NetworkStyle *networkStyle);
/// Create splash screen
void createSplashScreen(const NetworkStyle *networkStyle);
/// Basic initialization, before starting initialization/shutdown thread. Return true on success.
bool baseInitialize();
/// Request core initialization
void requestInitialize();
/// Request core shutdown
void requestShutdown();
/// Get process return value
int getReturnValue() const { return returnValue; }
/// Get window identifier of QMainWindow (DigiByteGUI)
WId getMainWinId() const;
/// Setup platform style
void setupPlatformStyle();
interfaces::Node& node() const { assert(m_node); return *m_node; }
void setNode(interfaces::Node& node);
public Q_SLOTS:
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
/// Handle runaway exceptions. Shows a message box with the problem and quits the program.
void handleRunawayException(const QString &message);
/**
* A helper function that shows a message box
* with details about a non-fatal exception.
*/
void handleNonFatalException(const QString& message);
Q_SIGNALS:
void requestedInitialize();
void requestedShutdown();
void splashFinished();
void windowShown(DigiByteGUI* window);
private:
std::optional<InitExecutor> m_executor;
OptionsModel *optionsModel;
ClientModel *clientModel;
DigiByteGUI *window;
QTimer *pollShutdownTimer;
#ifdef ENABLE_WALLET
PaymentServer* paymentServer{nullptr};
WalletController* m_wallet_controller{nullptr};
#endif
int returnValue;
const PlatformStyle *platformStyle;
std::unique_ptr<QWidget> shutdownWindow;
SplashScreen* m_splash = nullptr;
interfaces::Node* m_node = nullptr;
void startThread();
};
int GuiMain(int argc, char* argv[]);
#endif // DIGIBYTE_QT_DIGIBYTE_H

View file

@ -0,0 +1,97 @@
// Copyright (c) 2011-2018 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/digibyteaddressvalidator.h>
#include <key_io.h>
/* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
This is:
- All numbers except for '0'
- All upper-case letters except for 'I' and 'O'
- All lower-case letters except for 'l'
*/
DigiByteAddressEntryValidator::DigiByteAddressEntryValidator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State DigiByteAddressEntryValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
// Empty address is "intermediate" input
if (input.isEmpty())
return QValidator::Intermediate;
// Correction
for (int idx = 0; idx < input.size();)
{
bool removeChar = false;
QChar ch = input.at(idx);
// Corrections made are very conservative on purpose, to avoid
// users unexpectedly getting away with typos that would normally
// be detected, and thus sending to the wrong address.
switch(ch.unicode())
{
// Qt categorizes these as "Other_Format" not "Separator_Space"
case 0x200B: // ZERO WIDTH SPACE
case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
removeChar = true;
break;
default:
break;
}
// Remove whitespace
if (ch.isSpace())
removeChar = true;
// To next character
if (removeChar)
input.remove(idx, 1);
else
++idx;
}
// Validation
QValidator::State state = QValidator::Acceptable;
for (int idx = 0; idx < input.size(); ++idx)
{
int ch = input.at(idx).unicode();
if (((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') ||
(ch >= 'A' && ch<='Z')) &&
ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32
{
// Alphanumeric and not a 'forbidden' character
}
else
{
state = QValidator::Invalid;
}
}
return state;
}
DigiByteAddressCheckValidator::DigiByteAddressCheckValidator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State DigiByteAddressCheckValidator::validate(QString &input, int &pos) const
{
Q_UNUSED(pos);
// Validate the passed Vexta address
if (IsValidDestinationString(input.toStdString())) {
return QValidator::Acceptable;
}
return QValidator::Invalid;
}

View file

@ -0,0 +1,36 @@
// Copyright (c) 2011-2014 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_DIGIBYTEADDRESSVALIDATOR_H
#define DIGIBYTE_QT_DIGIBYTEADDRESSVALIDATOR_H
#include <QValidator>
/** Base58 entry widget validator, checks for valid characters and
* removes some whitespace.
*/
class DigiByteAddressEntryValidator : public QValidator
{
Q_OBJECT
public:
explicit DigiByteAddressEntryValidator(QObject *parent);
State validate(QString &input, int &pos) const override;
};
/** Vexta address widget validator, checks for a valid Vexta address.
*/
class DigiByteAddressCheckValidator : public QValidator
{
Q_OBJECT
public:
explicit DigiByteAddressCheckValidator(QObject *parent);
State validate(QString &input, int &pos) const override;
};
#endif // DIGIBYTE_QT_DIGIBYTEADDRESSVALIDATOR_H

View file

@ -0,0 +1,343 @@
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/digibyteamountfield.h>
#include <qt/digibyteunits.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/qvaluecombobox.h>
#include <QApplication>
#include <QAbstractSpinBox>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLineEdit>
/** QSpinBox that uses fixed-point numbers internally and uses our own
* formatting/parsing functions.
*/
class AmountSpinBox: public QAbstractSpinBox
{
Q_OBJECT
public:
explicit AmountSpinBox(QWidget *parent):
QAbstractSpinBox(parent)
{
setAlignment(Qt::AlignRight);
connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::valueChanged);
}
QValidator::State validate(QString &text, int &pos) const override
{
if(text.isEmpty())
return QValidator::Intermediate;
bool valid = false;
parse(text, &valid);
/* Make sure we return Intermediate so that fixup() is called on defocus */
return valid ? QValidator::Intermediate : QValidator::Invalid;
}
void fixup(QString &input) const override
{
bool valid;
CAmount val;
if (input.isEmpty() && !m_allow_empty) {
valid = true;
val = m_min_amount;
} else {
valid = false;
val = parse(input, &valid);
}
if (valid) {
val = qBound(m_min_amount, val, m_max_amount);
input = DigiByteUnits::format(currentUnit, val, false, DigiByteUnits::SeparatorStyle::ALWAYS);
lineEdit()->setText(input);
}
}
CAmount value(bool *valid_out=nullptr) const
{
return parse(text(), valid_out);
}
void setValue(const CAmount& value)
{
lineEdit()->setText(DigiByteUnits::format(currentUnit, value, false, DigiByteUnits::SeparatorStyle::ALWAYS));
Q_EMIT valueChanged();
}
void SetAllowEmpty(bool allow)
{
m_allow_empty = allow;
}
void SetMinValue(const CAmount& value)
{
m_min_amount = value;
}
void SetMaxValue(const CAmount& value)
{
m_max_amount = value;
}
void stepBy(int steps) override
{
bool valid = false;
CAmount val = value(&valid);
val = val + steps * singleStep;
val = qBound(m_min_amount, val, m_max_amount);
setValue(val);
}
void setDisplayUnit(int unit)
{
bool valid = false;
CAmount val = value(&valid);
currentUnit = unit;
lineEdit()->setPlaceholderText(DigiByteUnits::format(currentUnit, m_min_amount, false, DigiByteUnits::SeparatorStyle::ALWAYS));
if(valid)
setValue(val);
else
clear();
}
void setSingleStep(const CAmount& step)
{
singleStep = step;
}
QSize minimumSizeHint() const override
{
if(cachedMinimumSizeHint.isEmpty())
{
ensurePolished();
const QFontMetrics fm(fontMetrics());
int h = lineEdit()->minimumSizeHint().height();
int w = GUIUtil::TextWidth(fm, DigiByteUnits::format(DigiByteUnits::DGB, DigiByteUnits::maxMoney(), false, DigiByteUnits::SeparatorStyle::ALWAYS));
w += 2; // cursor blinking space
QStyleOptionSpinBox opt;
initStyleOption(&opt);
QSize hint(w, h);
QSize extra(35, 6);
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
// get closer to final result by repeating the calculation
opt.rect.setSize(hint + extra);
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
QStyle::SC_SpinBoxEditField, this).size();
hint += extra;
hint.setHeight(h);
opt.rect = rect();
cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
.expandedTo(QApplication::globalStrut());
}
return cachedMinimumSizeHint;
}
private:
int currentUnit{DigiByteUnits::DGB};
CAmount singleStep{CAmount(100000)}; // satoshis
mutable QSize cachedMinimumSizeHint;
bool m_allow_empty{true};
CAmount m_min_amount{CAmount(0)};
CAmount m_max_amount{DigiByteUnits::maxMoney()};
/**
* Parse a string into a number of base monetary units and
* return validity.
* @note Must return 0 if !valid.
*/
CAmount parse(const QString &text, bool *valid_out=nullptr) const
{
CAmount val = 0;
bool valid = DigiByteUnits::parse(currentUnit, text, &val);
if(valid)
{
if(val < 0 || val > DigiByteUnits::maxMoney())
valid = false;
}
if(valid_out)
*valid_out = valid;
return valid ? val : 0;
}
protected:
bool event(QEvent *event) override
{
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Comma)
{
// Translate a comma into a period
QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
return QAbstractSpinBox::event(&periodKeyEvent);
}
}
return QAbstractSpinBox::event(event);
}
StepEnabled stepEnabled() const override
{
if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
return StepNone;
if (text().isEmpty()) // Allow step-up with empty field
return StepUpEnabled;
StepEnabled rv = StepNone;
bool valid = false;
CAmount val = value(&valid);
if (valid) {
if (val > m_min_amount)
rv |= StepDownEnabled;
if (val < m_max_amount)
rv |= StepUpEnabled;
}
return rv;
}
Q_SIGNALS:
void valueChanged();
};
#include <qt/digibyteamountfield.moc>
DigiByteAmountField::DigiByteAmountField(QWidget *parent) :
QWidget(parent),
amount(nullptr)
{
amount = new AmountSpinBox(this);
amount->setLocale(QLocale::c());
amount->installEventFilter(this);
amount->setMaximumWidth(240);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(amount);
unit = new QValueComboBox(this);
unit->setModel(new DigiByteUnits(this));
layout->addWidget(unit);
layout->addStretch(1);
layout->setContentsMargins(0,0,0,0);
setLayout(layout);
setFocusPolicy(Qt::TabFocus);
setFocusProxy(amount);
// If one if the widgets changes, the combined content changes as well
connect(amount, &AmountSpinBox::valueChanged, this, &DigiByteAmountField::valueChanged);
connect(unit, qOverload<int>(&QComboBox::currentIndexChanged), this, &DigiByteAmountField::unitChanged);
// Set default based on configuration
unitChanged(unit->currentIndex());
}
void DigiByteAmountField::clear()
{
amount->clear();
unit->setCurrentIndex(0);
}
void DigiByteAmountField::setEnabled(bool fEnabled)
{
amount->setEnabled(fEnabled);
unit->setEnabled(fEnabled);
}
bool DigiByteAmountField::validate()
{
bool valid = false;
value(&valid);
setValid(valid);
return valid;
}
void DigiByteAmountField::setValid(bool valid)
{
if (valid)
amount->setStyleSheet("");
else
amount->setStyleSheet(STYLE_INVALID);
}
bool DigiByteAmountField::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::FocusIn)
{
// Clear invalid flag on focus
setValid(true);
}
return QWidget::eventFilter(object, event);
}
QWidget *DigiByteAmountField::setupTabChain(QWidget *prev)
{
QWidget::setTabOrder(prev, amount);
QWidget::setTabOrder(amount, unit);
return unit;
}
CAmount DigiByteAmountField::value(bool *valid_out) const
{
return amount->value(valid_out);
}
void DigiByteAmountField::setValue(const CAmount& value)
{
amount->setValue(value);
}
void DigiByteAmountField::SetAllowEmpty(bool allow)
{
amount->SetAllowEmpty(allow);
}
void DigiByteAmountField::SetMinValue(const CAmount& value)
{
amount->SetMinValue(value);
}
void DigiByteAmountField::SetMaxValue(const CAmount& value)
{
amount->SetMaxValue(value);
}
void DigiByteAmountField::setReadOnly(bool fReadOnly)
{
amount->setReadOnly(fReadOnly);
}
void DigiByteAmountField::unitChanged(int idx)
{
// Use description tooltip for current unit for the combobox
unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
// Determine new unit ID
int newUnit = unit->itemData(idx, DigiByteUnits::UnitRole).toInt();
amount->setDisplayUnit(newUnit);
}
void DigiByteAmountField::setDisplayUnit(int newUnit)
{
unit->setValue(newUnit);
}
void DigiByteAmountField::setSingleStep(const CAmount& step)
{
amount->setSingleStep(step);
}

View file

@ -0,0 +1,85 @@
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_DIGIBYTEAMOUNTFIELD_H
#define DIGIBYTE_QT_DIGIBYTEAMOUNTFIELD_H
#include <amount.h>
#include <QWidget>
class AmountSpinBox;
QT_BEGIN_NAMESPACE
class QValueComboBox;
QT_END_NAMESPACE
/** Widget for entering Vexta amounts.
*/
class DigiByteAmountField: public QWidget
{
Q_OBJECT
// ugly hack: for some unknown reason CAmount (instead of qint64) does not work here as expected
// discussion: https://github.com/digibyte/digibyte/pull/5117
Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY valueChanged USER true)
public:
explicit DigiByteAmountField(QWidget *parent = nullptr);
CAmount value(bool *value=nullptr) const;
void setValue(const CAmount& value);
/** If allow empty is set to false the field will be set to the minimum allowed value if left empty. **/
void SetAllowEmpty(bool allow);
/** Set the minimum value in satoshis **/
void SetMinValue(const CAmount& value);
/** Set the maximum value in satoshis **/
void SetMaxValue(const CAmount& value);
/** Set single step in satoshis **/
void setSingleStep(const CAmount& step);
/** Make read-only **/
void setReadOnly(bool fReadOnly);
/** Mark current value as invalid in UI. */
void setValid(bool valid);
/** Perform input validation, mark field as invalid if entered value is not valid. */
bool validate();
/** Change unit used to display amount. */
void setDisplayUnit(int unit);
/** Make field empty and ready for new input. */
void clear();
/** Enable/Disable. */
void setEnabled(bool fEnabled);
/** Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907),
in these cases we have to set it up manually.
*/
QWidget *setupTabChain(QWidget *prev);
Q_SIGNALS:
void valueChanged();
protected:
/** Intercept focus-in event and ',' key presses */
bool eventFilter(QObject *object, QEvent *event) override;
private:
AmountSpinBox *amount;
QValueComboBox *unit;
private Q_SLOTS:
void unitChanged(int idx);
};
#endif // DIGIBYTE_QT_DIGIBYTEAMOUNTFIELD_H

1568
src/qt/digibytegui.cpp Normal file

File diff suppressed because it is too large Load diff

359
src/qt/digibytegui.h Normal file
View file

@ -0,0 +1,359 @@
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Copyright (c) 2011-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_DIGIBYTEGUI_H
#define DIGIBYTE_QT_DIGIBYTEGUI_H
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <qt/guiutil.h>
#include <qt/optionsdialog.h>
#include <amount.h>
#include <QLabel>
#include <QMainWindow>
#include <QMap>
#include <QMenu>
#include <QPoint>
#include <QSystemTrayIcon>
#ifdef Q_OS_MAC
#include <qt/macos_appnap.h>
#endif
#include <memory>
class ClientModel;
class NetworkStyle;
class Notificator;
class OptionsModel;
class PlatformStyle;
class RPCConsole;
class SendCoinsRecipient;
class UnitDisplayStatusBarControl;
class WalletController;
class WalletFrame;
class WalletModel;
class HelpMessageDialog;
class ModalOverlay;
enum class SynchronizationState;
namespace interfaces {
class Handler;
class Node;
struct BlockAndHeaderTipInfo;
}
QT_BEGIN_NAMESPACE
class QAction;
class QComboBox;
class QDateTime;
class QProgressBar;
class QProgressDialog;
QT_END_NAMESPACE
namespace GUIUtil {
class ClickableLabel;
class ClickableProgressBar;
}
/**
DigiByte GUI main class. This class represents the main window of the DigiByte UI. It communicates with both the client and
wallet models to give the user an up-to-date view of the current core state.
*/
class DigiByteGUI : public QMainWindow
{
Q_OBJECT
public:
static const std::string DEFAULT_UIPLATFORM;
explicit DigiByteGUI(interfaces::Node& node, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = nullptr);
~DigiByteGUI();
/** Set the client model.
The client model represents the part of the core that communicates with the P2P network, and is wallet-agnostic.
*/
void setClientModel(ClientModel *clientModel = nullptr, interfaces::BlockAndHeaderTipInfo* tip_info = nullptr);
#ifdef ENABLE_WALLET
void setWalletController(WalletController* wallet_controller);
WalletController* getWalletController();
#endif
#ifdef ENABLE_WALLET
/** Set the wallet model.
The wallet model represents a Vexta wallet, and offers access to the list of transactions, address book and sending
functionality.
*/
void addWallet(WalletModel* walletModel);
void removeWallet(WalletModel* walletModel);
void removeAllWallets();
#endif // ENABLE_WALLET
bool enableWallet = false;
/** Get the tray icon status.
Some systems have not "system tray" or "notification area" available.
*/
bool hasTrayIcon() const { return trayIcon; }
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
bool isPrivacyModeActivated() const;
protected:
void changeEvent(QEvent *e) override;
void closeEvent(QCloseEvent *event) override;
void showEvent(QShowEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
bool eventFilter(QObject *object, QEvent *event) override;
private:
interfaces::Node& m_node;
WalletController* m_wallet_controller{nullptr};
std::unique_ptr<interfaces::Handler> m_handler_message_box;
std::unique_ptr<interfaces::Handler> m_handler_question;
ClientModel* clientModel = nullptr;
WalletFrame* walletFrame = nullptr;
UnitDisplayStatusBarControl* unitDisplayControl = nullptr;
GUIUtil::ThemedLabel* labelWalletEncryptionIcon = nullptr;
GUIUtil::ThemedLabel* labelWalletHDStatusIcon = nullptr;
GUIUtil::ClickableLabel* labelProxyIcon = nullptr;
GUIUtil::ClickableLabel* connectionsControl = nullptr;
GUIUtil::ClickableLabel* labelBlocksIcon = nullptr;
QLabel* progressBarLabel = nullptr;
GUIUtil::ClickableProgressBar* progressBar = nullptr;
QProgressDialog* progressDialog = nullptr;
QMenuBar* appMenuBar = nullptr;
QToolBar* appToolBar = nullptr;
QAction* overviewAction = nullptr;
QAction* historyAction = nullptr;
QAction* quitAction = nullptr;
QAction* sendCoinsAction = nullptr;
QAction* sendCoinsMenuAction = nullptr;
QAction* usedSendingAddressesAction = nullptr;
QAction* usedReceivingAddressesAction = nullptr;
QAction* signMessageAction = nullptr;
QAction* verifyMessageAction = nullptr;
QAction* m_load_psbt_action = nullptr;
QAction* m_load_psbt_clipboard_action = nullptr;
QAction* aboutAction = nullptr;
QAction* receiveCoinsAction = nullptr;
QAction* receiveCoinsMenuAction = nullptr;
QAction* optionsAction = nullptr;
QAction* toggleHideAction = nullptr;
QAction* encryptWalletAction = nullptr;
QAction* backupWalletAction = nullptr;
QAction* changePassphraseAction = nullptr;
QAction* aboutQtAction = nullptr;
QAction* openRPCConsoleAction = nullptr;
QAction* openAction = nullptr;
QAction* showHelpMessageAction = nullptr;
QAction* m_create_wallet_action{nullptr};
QAction* m_open_wallet_action{nullptr};
QMenu* m_open_wallet_menu{nullptr};
QAction* m_close_wallet_action{nullptr};
QAction* m_close_all_wallets_action{nullptr};
QAction* m_wallet_selector_label_action = nullptr;
QAction* m_wallet_selector_action = nullptr;
QAction* m_mask_values_action{nullptr};
QLabel *m_wallet_selector_label = nullptr;
QComboBox* m_wallet_selector = nullptr;
QSystemTrayIcon* trayIcon = nullptr;
const std::unique_ptr<QMenu> trayIconMenu;
Notificator* notificator = nullptr;
RPCConsole* rpcConsole = nullptr;
HelpMessageDialog* helpMessageDialog = nullptr;
ModalOverlay* modalOverlay = nullptr;
QMenu* m_network_context_menu = new QMenu(this);
#ifdef Q_OS_MAC
CAppNapInhibitor* m_app_nap_inhibitor = nullptr;
#endif
/** Keep track of previous number of blocks, to detect progress */
int prevBlocks = 0;
int spinnerFrame = 0;
const PlatformStyle *platformStyle;
const NetworkStyle* const m_network_style;
/** Create the main UI actions. */
void createActions();
/** Create the menu bar and sub-menus. */
void createMenuBar();
/** Create the toolbars */
void createToolBars();
/** Create system tray icon and notification */
void createTrayIcon();
/** Create system tray menu (or setup the dock menu) */
void createTrayIconMenu();
/** Enable or disable all wallet-related actions */
void setWalletActionsEnabled(bool enabled);
/** Connect core signals to GUI client */
void subscribeToCoreSignals();
/** Update UI with latest network info from model. */
void updateNetworkState();
void updateHeadersSyncProgressLabel();
/** Open the OptionsDialog on the specified tab index */
void openOptionsDialogWithTab(OptionsDialog::Tab tab);
Q_SIGNALS:
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
/** Signal raised when RPC console shown */
void consoleShown(RPCConsole* console);
void setPrivacy(bool privacy);
public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
/** Set network state shown in the UI */
void setNetworkActive(bool network_active);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers, SynchronizationState sync_state);
/** Notify the user of an event from the core network or transaction handling code.
@param[in] title the message box / notification title
@param[in] message the displayed text
@param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes)
@see CClientUIInterface::MessageBoxFlags
@param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only)
@param[in] detailed_message the text to be displayed in the details area
*/
void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr, const QString& detailed_message = QString());
#ifdef ENABLE_WALLET
void setCurrentWallet(WalletModel* wallet_model);
void setCurrentWalletBySelectorIndex(int index);
/** Set the UI status indicators based on the currently selected wallet.
*/
void updateWalletStatus();
private:
/** Set the encryption status as shown in the UI.
@param[in] status current encryption status
@see WalletModel::EncryptionStatus
*/
void setEncryptionStatus(int status);
/** Set the hd-enabled status as shown in the UI.
@param[in] hdEnabled current hd enabled status
@see WalletModel::EncryptionStatus
*/
void setHDStatus(bool privkeyDisabled, int hdEnabled);
public Q_SLOTS:
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
/** Show incoming transaction notification for new transactions. */
void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName);
#endif // ENABLE_WALLET
private:
/** Set the proxy-enabled icon as shown in the UI. */
void updateProxyIcon();
void updateWindowTitle();
public Q_SLOTS:
#ifdef ENABLE_WALLET
/** Switch to overview (home) page */
void gotoOverviewPage();
/** Switch to history (transactions) page */
void gotoHistoryPage();
/** Switch to receive coins page */
void gotoReceiveCoinsPage();
/** Switch to send coins page */
void gotoSendCoinsPage(QString addr = "");
/** Show Sign/Verify Message dialog and switch to sign message tab */
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Vexta Transaction from file or clipboard */
void gotoLoadPSBT(bool from_clipboard = false);
/** Show open dialog */
void openClicked();
#endif // ENABLE_WALLET
/** Show configuration dialog */
void optionsClicked();
/** Show about dialog */
void aboutClicked();
/** Show debug window */
void showDebugWindow();
/** Show debug window and set focus to the console */
void showDebugWindowActivateConsole();
/** Show help message dialog */
void showHelpMessageClicked();
#ifndef Q_OS_MAC
/** Handle tray icon clicked */
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
#else
/** Handle macOS Dock icon clicked */
void macosDockIconActivated();
#endif
/** Show window if hidden, unminimize when minimized, rise when obscured or show if hidden and fToggleHidden is true */
void showNormalIfMinimized() { showNormalIfMinimized(false); }
void showNormalIfMinimized(bool fToggleHidden);
/** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */
void toggleHidden();
/** called by a timer to check if ShutdownRequested() has been set **/
void detectShutdown();
/** Show progress dialog e.g. for verifychain */
void showProgress(const QString &title, int nProgress);
void showModalOverlay();
};
class UnitDisplayStatusBarControl : public QLabel
{
Q_OBJECT
public:
explicit UnitDisplayStatusBarControl(const PlatformStyle *platformStyle);
/** Lets the control know about the Options Model (and its signals) */
void setOptionsModel(OptionsModel *optionsModel);
protected:
/** So that it responds to left-button clicks */
void mousePressEvent(QMouseEvent *event) override;
void changeEvent(QEvent* e) override;
private:
OptionsModel *optionsModel;
QMenu* menu;
const PlatformStyle* m_platform_style;
/** Shows context menu with Display Unit options by the mouse coordinates */
void onDisplayUnitsClicked(const QPoint& point);
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
void createContextMenu();
private Q_SLOTS:
/** When Display Units are changed on OptionsModel it will refresh the display text of the control on the status bar */
void updateDisplayUnit(int newUnits);
/** Tells underlying optionsModel to update its current display unit. */
void onMenuSelection(QAction* action);
};
#endif // DIGIBYTE_QT_DIGIBYTEGUI_H

241
src/qt/digibytestrings.cpp Normal file
View file

@ -0,0 +1,241 @@
#include <QtGlobal>
// Automatically generated by extract_strings_qt.py
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
static const char UNUSED *digibyte_strings[] = {
QT_TRANSLATE_NOOP("digibyte-core", "The %s developers"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"%s corrupt. Try using the wallet tool digibyte-wallet to salvage or restoring "
"a backup."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"-maxtxfee is set very high! Fees this large could be paid on a single "
"transaction."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Cannot downgrade wallet from version %i to version %i. Wallet version "
"unchanged."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Cannot obtain a lock on data directory %s. %s is probably already running."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Cannot provide specific connections and have addrman find outgoing "
"connections at the same."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Cannot upgrade a non HD split wallet from version %i to version %i without "
"upgrading to support pre-split keypool. Please use version %i or no version "
"specified."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Distributed under the MIT software license, see the accompanying file %s or "
"%s"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Error reading %s! All keys read correctly, but transaction data or address "
"book entries might be missing or incorrect."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Error: Dumpfile format record is incorrect. Got \"%s\", expected \"format\"."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Error: Dumpfile identifier record is incorrect. Got \"%s\", expected \"%s\"."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Error: Dumpfile version is not supported. This version of digibyte-wallet "
"only supports version 1 dumpfiles. Got dumpfile with version %s"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and "
"\"bech32\" address types"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Error: Listening for incoming connections failed (listen returned error %s)"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -"
"fallbackfee."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"File %s already exists. If you are sure this is what you want, move it out "
"of the way first."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay "
"fee of %s to prevent stuck transactions)"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"More than one onion bind address is provided. Using %s for the automatically "
"created Tor onion service."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"No dump file provided. To use createfromdump, -dumpfile=<filename> must be "
"provided."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"No dump file provided. To use dump, -dumpfile=<filename> must be provided."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"No wallet file format provided. To use createfromdump, -format=<format> must "
"be provided."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Please check that your computer's date and time are correct! If your clock "
"is wrong, %s will not work properly."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Please contribute if you find %s useful. Visit %s for further information "
"about the software."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Prune configured below the minimum of %d MiB. Please use a higher number."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Prune: last wallet synchronisation goes beyond pruned data. You need to -"
"reindex (download the whole blockchain again in case of pruned node)"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is "
"supported"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"The block database contains a block which appears to be from the future. "
"This may be due to your computer's date and time being set incorrectly. Only "
"rebuild the block database if you are sure that your computer's date and "
"time are correct"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"The transaction amount is too small to send after the fee has been deducted"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"This error could occur if this wallet was not shutdown cleanly and was last "
"loaded using a build with a newer version of Berkeley DB. If so, please use "
"the software that last loaded this wallet"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"This is a pre-release test build - use at your own risk - do not use for "
"mining or merchant applications"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"This is the maximum transaction fee you pay (in addition to the normal fee) "
"to prioritize partial spend avoidance over regular coin selection."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"This is the transaction fee you may discard if change is smaller than dust "
"at this level"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"This is the transaction fee you may pay when fee estimates are not available."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Total length of network version string (%i) exceeds maximum length (%i). "
"Reduce the number or size of uacomments."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Unable to replay blocks. You will need to rebuild the database using -"
"reindex-chainstate."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Unknown wallet file format \"%s\" provided. Please provide one of \"bdb\" or "
"\"sqlite\"."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Warning: Dumpfile wallet format \"%s\" does not match command line specified "
"format \"%s\"."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Warning: Private keys detected in wallet {%s} with disabled private keys"),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Warning: We do not appear to fully agree with our peers! You may need to "
"upgrade, or other nodes may need to upgrade."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"Witness data for blocks after height %d requires validation. Please restart "
"with -reindex."),
QT_TRANSLATE_NOOP("digibyte-core", ""
"You need to rebuild the database using -reindex to go back to unpruned "
"mode. This will redownload the entire blockchain"),
QT_TRANSLATE_NOOP("digibyte-core", "%s is set very high!"),
QT_TRANSLATE_NOOP("digibyte-core", "-maxmempool must be at least %d MB"),
QT_TRANSLATE_NOOP("digibyte-core", "A fatal internal error occurred, see debug.log for details"),
QT_TRANSLATE_NOOP("digibyte-core", "Cannot resolve -%s address: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Cannot set -peerblockfilters without -blockfilterindex."),
QT_TRANSLATE_NOOP("digibyte-core", "Cannot write to data directory '%s'; check permissions."),
QT_TRANSLATE_NOOP("digibyte-core", "Change index out of range"),
QT_TRANSLATE_NOOP("digibyte-core", "Config setting for %s only applied on %s network when in [%s] section."),
QT_TRANSLATE_NOOP("digibyte-core", "Copyright (C) %i-%i"),
QT_TRANSLATE_NOOP("digibyte-core", "Corrupted block database detected"),
QT_TRANSLATE_NOOP("digibyte-core", "Could not find asmap file %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Could not parse asmap file %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Disk space is too low!"),
QT_TRANSLATE_NOOP("digibyte-core", "Do you want to rebuild the block database now?"),
QT_TRANSLATE_NOOP("digibyte-core", "Done loading"),
QT_TRANSLATE_NOOP("digibyte-core", "Dump file %s does not exist."),
QT_TRANSLATE_NOOP("digibyte-core", "Error creating %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error initializing block database"),
QT_TRANSLATE_NOOP("digibyte-core", "Error initializing wallet database environment %s!"),
QT_TRANSLATE_NOOP("digibyte-core", "Error loading %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error loading %s: Private keys can only be disabled during creation"),
QT_TRANSLATE_NOOP("digibyte-core", "Error loading %s: Wallet corrupted"),
QT_TRANSLATE_NOOP("digibyte-core", "Error loading %s: Wallet requires newer version of %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error loading block database"),
QT_TRANSLATE_NOOP("digibyte-core", "Error opening block database"),
QT_TRANSLATE_NOOP("digibyte-core", "Error reading from database, shutting down."),
QT_TRANSLATE_NOOP("digibyte-core", "Error reading next record from wallet database"),
QT_TRANSLATE_NOOP("digibyte-core", "Error upgrading chainstate database"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Couldn't create cursor into database"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Disk space is low for %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Dumpfile checksum does not match. Computed %s, expected %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Got key that was not hex: %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Got value that was not hex: %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Keypool ran out, please call keypoolrefill first"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Missing checksum"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: No %s addresses available."),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Unable to parse version %u as a uint32_t"),
QT_TRANSLATE_NOOP("digibyte-core", "Error: Unable to write record to new wallet"),
QT_TRANSLATE_NOOP("digibyte-core", "Failed to listen on any port. Use -listen=0 if you want this."),
QT_TRANSLATE_NOOP("digibyte-core", "Failed to rescan the wallet during initialization"),
QT_TRANSLATE_NOOP("digibyte-core", "Failed to verify database"),
QT_TRANSLATE_NOOP("digibyte-core", "Fee rate (%s) is lower than the minimum fee rate setting (%s)"),
QT_TRANSLATE_NOOP("digibyte-core", "Ignoring duplicate -wallet %s."),
QT_TRANSLATE_NOOP("digibyte-core", "Importing…"),
QT_TRANSLATE_NOOP("digibyte-core", "Incorrect or no genesis block found. Wrong datadir for network?"),
QT_TRANSLATE_NOOP("digibyte-core", "Initialization sanity check failed. %s is shutting down."),
QT_TRANSLATE_NOOP("digibyte-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid -i2psam address or hostname: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid -onion address or hostname: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid -proxy address or hostname: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid P2P permission: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid amount for -%s=<amount>: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid amount for -discardfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid amount for -fallbackfee=<amount>: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
QT_TRANSLATE_NOOP("digibyte-core", "Invalid netmask specified in -whitelist: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Loading P2P addresses…"),
QT_TRANSLATE_NOOP("digibyte-core", "Loading banlist…"),
QT_TRANSLATE_NOOP("digibyte-core", "Loading block index…"),
QT_TRANSLATE_NOOP("digibyte-core", "Loading wallet…"),
QT_TRANSLATE_NOOP("digibyte-core", "Need to specify a port with -whitebind: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."),
QT_TRANSLATE_NOOP("digibyte-core", "Not enough file descriptors available."),
QT_TRANSLATE_NOOP("digibyte-core", "Prune cannot be configured with a negative value."),
QT_TRANSLATE_NOOP("digibyte-core", "Prune mode is incompatible with -coinstatsindex."),
QT_TRANSLATE_NOOP("digibyte-core", "Prune mode is incompatible with -txindex."),
QT_TRANSLATE_NOOP("digibyte-core", "Pruning blockstore…"),
QT_TRANSLATE_NOOP("digibyte-core", "Reducing -maxconnections from %d to %d, because of system limitations."),
QT_TRANSLATE_NOOP("digibyte-core", "Replaying blocks…"),
QT_TRANSLATE_NOOP("digibyte-core", "Rescanning…"),
QT_TRANSLATE_NOOP("digibyte-core", "SQLiteDatabase: Failed to execute statement to verify database: %s"),
QT_TRANSLATE_NOOP("digibyte-core", "SQLiteDatabase: Failed to prepare statement to verify database: %s"),
QT_TRANSLATE_NOOP("digibyte-core", "SQLiteDatabase: Failed to read database verification error: %s"),
QT_TRANSLATE_NOOP("digibyte-core", "SQLiteDatabase: Unexpected application id. Expected %u, got %u"),
QT_TRANSLATE_NOOP("digibyte-core", "Section [%s] is not recognized."),
QT_TRANSLATE_NOOP("digibyte-core", "Signing transaction failed"),
QT_TRANSLATE_NOOP("digibyte-core", "Specified -walletdir \"%s\" does not exist"),
QT_TRANSLATE_NOOP("digibyte-core", "Specified -walletdir \"%s\" is a relative path"),
QT_TRANSLATE_NOOP("digibyte-core", "Specified -walletdir \"%s\" is not a directory"),
QT_TRANSLATE_NOOP("digibyte-core", "Specified blocks directory \"%s\" does not exist."),
QT_TRANSLATE_NOOP("digibyte-core", "Starting network threads…"),
QT_TRANSLATE_NOOP("digibyte-core", "The source code is available from %s."),
QT_TRANSLATE_NOOP("digibyte-core", "The specified config file %s does not exist"),
QT_TRANSLATE_NOOP("digibyte-core", "The transaction amount is too small to pay the fee"),
QT_TRANSLATE_NOOP("digibyte-core", "The wallet will avoid paying less than the minimum relay fee."),
QT_TRANSLATE_NOOP("digibyte-core", "This is experimental software."),
QT_TRANSLATE_NOOP("digibyte-core", "This is the minimum transaction fee you pay on every transaction."),
QT_TRANSLATE_NOOP("digibyte-core", "This is the transaction fee you will pay if you send a transaction."),
QT_TRANSLATE_NOOP("digibyte-core", "Transaction amount too small"),
QT_TRANSLATE_NOOP("digibyte-core", "Transaction amounts must not be negative"),
QT_TRANSLATE_NOOP("digibyte-core", "Transaction has too long of a mempool chain"),
QT_TRANSLATE_NOOP("digibyte-core", "Transaction must have at least one recipient"),
QT_TRANSLATE_NOOP("digibyte-core", "Transaction needs a change address, but we can't generate it. %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Transaction too large"),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to bind to %s on this computer (bind returned error %s)"),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to bind to %s on this computer. %s is probably already running."),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to create the PID file '%s': %s"),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to generate initial keys"),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to generate keys"),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to open %s for writing"),
QT_TRANSLATE_NOOP("digibyte-core", "Unable to start HTTP server. See debug log for details."),
QT_TRANSLATE_NOOP("digibyte-core", "Unknown -blockfilterindex value %s."),
QT_TRANSLATE_NOOP("digibyte-core", "Unknown address type '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Unknown change type '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Unknown network specified in -onlynet: '%s'"),
QT_TRANSLATE_NOOP("digibyte-core", "Unknown new rules activated (versionbit %i)"),
QT_TRANSLATE_NOOP("digibyte-core", "Unsupported logging category %s=%s."),
QT_TRANSLATE_NOOP("digibyte-core", "Upgrading UTXO database"),
QT_TRANSLATE_NOOP("digibyte-core", "Upgrading txindex database"),
QT_TRANSLATE_NOOP("digibyte-core", "User Agent comment (%s) contains unsafe characters."),
QT_TRANSLATE_NOOP("digibyte-core", "Verifying blocks…"),
QT_TRANSLATE_NOOP("digibyte-core", "Verifying wallet(s)…"),
QT_TRANSLATE_NOOP("digibyte-core", "Wallet needed to be rewritten: restart %s to complete"),
};

253
src/qt/digibyteunits.cpp Normal file
View file

@ -0,0 +1,253 @@
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// Source: https://dgbwiki.com/index.php?title=DigiByte#Subunits
#include <qt/digibyteunits.h>
#include <QStringList>
#include <cassert>
static constexpr auto MAX_DIGITS_DGB = 16;
DigiByteUnits::DigiByteUnits(QObject *parent):
QAbstractListModel(parent),
unitlist(availableUnits())
{
}
QList<DigiByteUnits::Unit> DigiByteUnits::availableUnits()
{
QList<DigiByteUnits::Unit> unitlist;
unitlist.append(DGB);
unitlist.append(mDGB);
unitlist.append(uDGB);
unitlist.append(SAT);
return unitlist;
}
bool DigiByteUnits::valid(int unit)
{
switch(unit)
{
case DGB:
case mDGB:
case uDGB:
case SAT:
return true;
default:
return false;
}
}
QString DigiByteUnits::longName(int unit)
{
switch(unit)
{
case DGB: return QString("VTX");
case mDGB: return QString("mVTX");
case uDGB: return QString::fromUtf8("µVTX");
case SAT: return QString("vextoshi");
default: return QString("???");
}
}
QString DigiByteUnits::shortName(int unit)
{
switch(unit)
{
case uDGB: return QString::fromUtf8("µVTX");
case SAT: return QString("vextoshi");
default: return longName(unit);
}
}
QString DigiByteUnits::description(int unit)
{
switch(unit)
{
case DGB: return QString("Vexta");
case mDGB: return QString("Milli-Vexta (1 / 1" THIN_SP_UTF8 "000)");
case uDGB: return QString("Micro-Vexta (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
case SAT: return QString("Vextoshi (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)");
default: return QString("???");
}
}
qint64 DigiByteUnits::factor(int unit)
{
switch(unit)
{
case DGB: return 100000000;
case mDGB: return 100000;
case uDGB: return 100;
case SAT: return 1;
default: return 100000000;
}
}
int DigiByteUnits::decimals(int unit)
{
switch(unit)
{
case DGB: return 8;
case mDGB: return 5;
case uDGB: return 2;
case SAT: return 0;
default: return 0;
}
}
QString DigiByteUnits::format(int unit, const CAmount& nIn, bool fPlus, SeparatorStyle separators, bool justify)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
if(!valid(unit))
return QString(); // Refuse to format invalid unit
qint64 n = (qint64)nIn;
qint64 coin = factor(unit);
int num_decimals = decimals(unit);
qint64 n_abs = (n > 0 ? n : -n);
qint64 quotient = n_abs / coin;
QString quotient_str = QString::number(quotient);
if (justify) {
quotient_str = quotient_str.rightJustified(MAX_DIGITS_DGB - num_decimals, ' ');
}
// Use SI-style thin space separators as these are locale independent and can't be
// confused with the decimal marker.
QChar thin_sp(THIN_SP_CP);
int q_size = quotient_str.size();
if (separators == SeparatorStyle::ALWAYS || (separators == SeparatorStyle::STANDARD && q_size > 4))
for (int i = 3; i < q_size; i += 3)
quotient_str.insert(q_size - i, thin_sp);
if (n < 0)
quotient_str.insert(0, '-');
else if (fPlus && n > 0)
quotient_str.insert(0, '+');
if (num_decimals > 0) {
qint64 remainder = n_abs % coin;
QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
return quotient_str + QString(".") + remainder_str;
} else {
return quotient_str;
}
}
// NOTE: Using formatWithUnit in an HTML context risks wrapping
// quantities at the thousands separator. More subtly, it also results
// in a standard space rather than a thin space, due to a bug in Qt's
// XML whitespace canonicalisation
//
// Please take care to use formatHtmlWithUnit instead, when
// appropriate.
QString DigiByteUnits::formatWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
return format(unit, amount, plussign, separators) + QString(" ") + shortName(unit);
}
QString DigiByteUnits::formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign, SeparatorStyle separators)
{
QString str(formatWithUnit(unit, amount, plussign, separators));
str.replace(QChar(THIN_SP_CP), QString(THIN_SP_HTML));
return QString("<span style='white-space: nowrap;'>%1</span>").arg(str);
}
QString DigiByteUnits::formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy)
{
assert(amount >= 0);
QString value;
if (privacy) {
value = format(unit, 0, false, separators, true).replace('0', '#');
} else {
value = format(unit, amount, false, separators, true);
}
return value + QString(" ") + shortName(unit);
}
bool DigiByteUnits::parse(int unit, const QString &value, CAmount *val_out)
{
if(!valid(unit) || value.isEmpty())
return false; // Refuse to parse invalid unit or empty string
int num_decimals = decimals(unit);
// Ignore spaces and thin spaces when parsing
QStringList parts = removeSpaces(value).split(".");
if(parts.size() > 2)
{
return false; // More than one dot
}
QString whole = parts[0];
QString decimals;
if(parts.size() > 1)
{
decimals = parts[1];
}
if(decimals.size() > num_decimals)
{
return false; // Exceeds max precision
}
bool ok = false;
QString str = whole + decimals.leftJustified(num_decimals, '0');
if(str.size() > 18)
{
return false; // Longer numbers will exceed 63 bits
}
CAmount retvalue(str.toLongLong(&ok));
if(val_out)
{
*val_out = retvalue;
}
return ok;
}
QString DigiByteUnits::getAmountColumnTitle(int unit)
{
QString amountTitle = QObject::tr("Amount");
if (DigiByteUnits::valid(unit))
{
amountTitle += " ("+DigiByteUnits::shortName(unit) + ")";
}
return amountTitle;
}
int DigiByteUnits::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return unitlist.size();
}
QVariant DigiByteUnits::data(const QModelIndex &index, int role) const
{
int row = index.row();
if(row >= 0 && row < unitlist.size())
{
Unit unit = unitlist.at(row);
switch(role)
{
case Qt::EditRole:
case Qt::DisplayRole:
return QVariant(longName(unit));
case Qt::ToolTipRole:
return QVariant(description(unit));
case UnitRole:
return QVariant(static_cast<int>(unit));
}
}
return QVariant();
}
CAmount DigiByteUnits::maxMoney()
{
return MAX_MONEY;
}

115
src/qt/digibyteunits.h Normal file
View file

@ -0,0 +1,115 @@
// Copyright (c) 2011-2018 The Bitcoin Core developers
// Copyright (c) 2011-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_DIGIBYTEUNITS_H
#define DIGIBYTE_QT_DIGIBYTEUNITS_H
#include <amount.h>
#include <QAbstractListModel>
#include <QString>
// U+2009 THIN SPACE = UTF-8 E2 80 89
#define REAL_THIN_SP_CP 0x2009
#define REAL_THIN_SP_UTF8 "\xE2\x80\x89"
// QMessageBox seems to have a bug whereby it doesn't display thin/hair spaces
// correctly. Workaround is to display a space in a small font. If you
// change this, please test that it doesn't cause the parent span to start
// wrapping.
#define HTML_HACK_SP "<span style='white-space: nowrap; font-size: 6pt'> </span>"
// Define THIN_SP_* variables to be our preferred type of thin space
#define THIN_SP_CP REAL_THIN_SP_CP
#define THIN_SP_UTF8 REAL_THIN_SP_UTF8
#define THIN_SP_HTML HTML_HACK_SP
/** DigiByte unit definitions. Encapsulates parsing and formatting
and serves as list model for drop-down selection boxes.
*/
class DigiByteUnits: public QAbstractListModel
{
Q_OBJECT
public:
explicit DigiByteUnits(QObject *parent);
/** DigiByte units.
@note Source: https://dgbwiki.com/index.php?title=DigiByte#Subunits . Please add only sensible ones
*/
enum Unit
{
DGB,
mDGB,
uDGB,
SAT
};
enum class SeparatorStyle
{
NEVER,
STANDARD,
ALWAYS
};
//! @name Static API
//! Unit conversion and formatting
///@{
//! Get list of units, for drop-down box
static QList<Unit> availableUnits();
//! Is unit ID valid?
static bool valid(int unit);
//! Long name
static QString longName(int unit);
//! Short name
static QString shortName(int unit);
//! Longer description
static QString description(int unit);
//! Number of Vextoshis (1e-8) per unit
static qint64 factor(int unit);
//! Number of decimals left
static int decimals(int unit);
//! Format as string
static QString format(int unit, const CAmount& amount, bool plussign = false, SeparatorStyle separators = SeparatorStyle::STANDARD, bool justify = false);
//! Format as string (with unit)
static QString formatWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
//! Format as HTML string (with unit)
static QString formatHtmlWithUnit(int unit, const CAmount& amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD);
//! Format as string (with unit) of fixed length to preserve privacy, if it is set.
static QString formatWithPrivacy(int unit, const CAmount& amount, SeparatorStyle separators, bool privacy);
//! Parse string to coin amount
static bool parse(int unit, const QString &value, CAmount *val_out);
//! Gets title for amount column including current display unit if optionsModel reference available */
static QString getAmountColumnTitle(int unit);
///@}
//! @name AbstractListModel implementation
//! List model for unit drop-down selection box.
///@{
enum RoleIndex {
/** Unit identifier */
UnitRole = Qt::UserRole
};
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
///@}
static QString removeSpaces(QString text)
{
text.remove(' ');
text.remove(QChar(THIN_SP_CP));
return text;
}
//! Return maximum number of base units (Vextoshis)
static CAmount maxMoney();
private:
QList<DigiByteUnits::Unit> unitlist;
};
typedef DigiByteUnits::Unit DigiByteUnit;
#endif // DIGIBYTE_QT_DIGIBYTEUNITS_H

View file

@ -0,0 +1,167 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/editaddressdialog.h>
#include <qt/forms/ui_editaddressdialog.h>
#include <qt/addresstablemodel.h>
#include <qt/guiutil.h>
#include <QDataWidgetMapper>
#include <QMessageBox>
EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::EditAddressDialog),
mapper(nullptr),
mode(_mode),
model(nullptr)
{
ui->setupUi(this);
GUIUtil::setupAddressWidget(ui->addressEdit, this);
switch(mode)
{
case NewSendingAddress:
setWindowTitle(tr("New sending address"));
break;
case EditReceivingAddress:
setWindowTitle(tr("Edit receiving address"));
ui->addressEdit->setEnabled(false);
break;
case EditSendingAddress:
setWindowTitle(tr("Edit sending address"));
break;
}
mapper = new QDataWidgetMapper(this);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
GUIUtil::ItemDelegate* delegate = new GUIUtil::ItemDelegate(mapper);
connect(delegate, &GUIUtil::ItemDelegate::keyEscapePressed, this, &EditAddressDialog::reject);
mapper->setItemDelegate(delegate);
GUIUtil::handleCloseWindowShortcut(this);
}
EditAddressDialog::~EditAddressDialog()
{
delete ui;
}
void EditAddressDialog::setModel(AddressTableModel *_model)
{
this->model = _model;
if(!_model)
return;
mapper->setModel(_model);
mapper->addMapping(ui->labelEdit, AddressTableModel::Label);
mapper->addMapping(ui->addressEdit, AddressTableModel::Address);
}
void EditAddressDialog::loadRow(int row)
{
mapper->setCurrentIndex(row);
}
bool EditAddressDialog::saveCurrentRow()
{
if(!model)
return false;
switch(mode)
{
case NewSendingAddress:
address = model->addRow(
AddressTableModel::Send,
ui->labelEdit->text(),
ui->addressEdit->text(),
model->GetDefaultAddressType());
break;
case EditReceivingAddress:
case EditSendingAddress:
if(mapper->submit())
{
address = ui->addressEdit->text();
}
break;
}
return !address.isEmpty();
}
void EditAddressDialog::accept()
{
if(!model)
return;
if(!saveCurrentRow())
{
switch(model->getEditStatus())
{
case AddressTableModel::OK:
// Failed with unknown reason. Just reject.
break;
case AddressTableModel::NO_CHANGES:
// No changes were made during edit operation. Just reject.
break;
case AddressTableModel::INVALID_ADDRESS:
QMessageBox::warning(this, windowTitle(),
tr("The entered address \"%1\" is not a valid Vexta address.").arg(ui->addressEdit->text()),
QMessageBox::Ok, QMessageBox::Ok);
break;
case AddressTableModel::DUPLICATE_ADDRESS:
QMessageBox::warning(this, windowTitle(),
getDuplicateAddressWarning(),
QMessageBox::Ok, QMessageBox::Ok);
break;
case AddressTableModel::WALLET_UNLOCK_FAILURE:
QMessageBox::critical(this, windowTitle(),
tr("Could not unlock wallet."),
QMessageBox::Ok, QMessageBox::Ok);
break;
case AddressTableModel::KEY_GENERATION_FAILURE:
QMessageBox::critical(this, windowTitle(),
tr("New key generation failed."),
QMessageBox::Ok, QMessageBox::Ok);
break;
}
return;
}
QDialog::accept();
}
QString EditAddressDialog::getDuplicateAddressWarning() const
{
QString dup_address = ui->addressEdit->text();
QString existing_label = model->labelForAddress(dup_address);
QString existing_purpose = model->purposeForAddress(dup_address);
if (existing_purpose == "receive" &&
(mode == NewSendingAddress || mode == EditSendingAddress)) {
return tr(
"Address \"%1\" already exists as a receiving address with label "
"\"%2\" and so cannot be added as a sending address."
).arg(dup_address).arg(existing_label);
}
return tr(
"The entered address \"%1\" is already in the address book with "
"label \"%2\"."
).arg(dup_address).arg(existing_label);
}
QString EditAddressDialog::getAddress() const
{
return address;
}
void EditAddressDialog::setAddress(const QString &_address)
{
this->address = _address;
ui->addressEdit->setText(_address);
}

View file

@ -0,0 +1,60 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_EDITADDRESSDIALOG_H
#define DIGIBYTE_QT_EDITADDRESSDIALOG_H
#include <QDialog>
class AddressTableModel;
namespace Ui {
class EditAddressDialog;
}
QT_BEGIN_NAMESPACE
class QDataWidgetMapper;
QT_END_NAMESPACE
/** Dialog for editing an address and associated information.
*/
class EditAddressDialog : public QDialog
{
Q_OBJECT
public:
enum Mode {
NewSendingAddress,
EditReceivingAddress,
EditSendingAddress
};
explicit EditAddressDialog(Mode mode, QWidget *parent = nullptr);
~EditAddressDialog();
void setModel(AddressTableModel *model);
void loadRow(int row);
QString getAddress() const;
void setAddress(const QString &address);
public Q_SLOTS:
void accept() override;
private:
bool saveCurrentRow();
/** Return a descriptive string when adding an already-existing address fails. */
QString getDuplicateAddressWarning() const;
Ui::EditAddressDialog *ui;
QDataWidgetMapper *mapper;
Mode mode;
AddressTableModel *model;
QString address;
};
#endif // DIGIBYTE_QT_EDITADDRESSDIALOG_H

View file

@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddressBookPage</class>
<widget class="QWidget" name="AddressBookPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>760</width>
<height>380</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelExplanation">
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchLineEdit">
<property name="placeholderText">
<string>Enter address or label to search</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="toolTip">
<string>Right-click to edit address or label</string>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="newAddress">
<property name="toolTip">
<string>Create a new address</string>
</property>
<property name="text">
<string>&amp;New</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/add</normaloff>:/icons/add</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyAddress">
<property name="toolTip">
<string>Copy the currently selected address to the system clipboard</string>
</property>
<property name="text">
<string>&amp;Copy</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/editcopy</normaloff>:/icons/editcopy</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteAddress">
<property name="toolTip">
<string>Delete the currently selected address from the list</string>
</property>
<property name="text">
<string>&amp;Delete</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="exportButton">
<property name="toolTip">
<string>Export the data in the current tab to a file</string>
</property>
<property name="text">
<string>&amp;Export</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/export</normaloff>:/icons/export</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>C&amp;lose</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../vexta.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AskPassphraseDialog</class>
<widget class="QDialog" name="AskPassphraseDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>598</width>
<height>222</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>550</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Passphrase Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<item>
<widget class="QLabel" name="warningLabel">
<property name="text">
<string notr="true">Placeholder text</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="passLabel1">
<property name="text">
<string>Enter passphrase</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="passEdit1">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="passLabel2">
<property name="text">
<string>New passphrase</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="passEdit2">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="passLabel3">
<property name="text">
<string>Repeat new passphrase</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="passEdit3">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="toggleShowPasswordButton">
<property name="text">
<string>Show passphrase</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="capsLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AskPassphraseDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AskPassphraseDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,489 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CoinControlDialog</class>
<widget class="QDialog" name="CoinControlDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1000</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string>Coin Selection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayoutTop" stretch="0,0,0,0">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl1">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelCoinControlQuantityText">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Quantity:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelCoinControlQuantity">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">0</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelCoinControlBytesText">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Bytes:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelCoinControlBytes">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">0</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl2">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelCoinControlAmountText">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Amount:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelCoinControlAmount">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">0.00 VTX</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelCoinControlLowOutputText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Dust:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelCoinControlLowOutput">
<property name="enabled">
<bool>false</bool>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">no</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl3">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelCoinControlFeeText">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Fee:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelCoinControlFee">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">0.00 VTX</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayoutCoinControl4">
<property name="horizontalSpacing">
<number>10</number>
</property>
<property name="verticalSpacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelCoinControlAfterFeeText">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>After Fee:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelCoinControlAfterFee">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">0.00 VTX</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelCoinControlChangeText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Change:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelCoinControlChange">
<property name="enabled">
<bool>false</bool>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ActionsContextMenu</enum>
</property>
<property name="text">
<string notr="true">0.00 VTX</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayoutPanel" stretch="0,0,0,0,0">
<property name="spacing">
<number>14</number>
</property>
<item>
<widget class="QPushButton" name="pushButtonSelectAll">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>(un)select all</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioTreeMode">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Tree mode</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioListMode">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>List mode</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelLocked">
<property name="text">
<string notr="true">(1 locked)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="CoinControlTreeWidget" name="treeWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<property name="columnCount">
<number>6</number>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string>Amount</string>
</property>
</column>
<column>
<property name="text">
<string>Received with label</string>
</property>
</column>
<column>
<property name="text">
<string>Received with address</string>
</property>
</column>
<column>
<property name="text">
<string>Date</string>
</property>
</column>
<column>
<property name="text">
<string>Confirmations</string>
</property>
<property name="toolTip">
<string>Confirmed</string>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
</column>
<column>
<property name="text">
<string/>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CoinControlTreeWidget</class>
<extends>QTreeWidget</extends>
<header>qt/coincontroltreewidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateWalletDialog</class>
<widget class="QDialog" name="CreateWalletDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>364</width>
<height>249</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Wallet</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="wallet_name_label">
<property name="text">
<string>Wallet Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="wallet_name_line_edit">
<property name="minimumSize">
<size>
<width>262</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>Wallet</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="encrypt_wallet_checkbox">
<property name="toolTip">
<string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string>
</property>
<property name="text">
<string>Encrypt Wallet</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_groupbox">
<item>
<widget class="QCheckBox" name="disable_privkeys_checkbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Disable private keys for this wallet. Wallets with private keys disabled will have no private keys and cannot have an HD seed or imported private keys. This is ideal for watch-only wallets.</string>
</property>
<property name="text">
<string>Disable Private Keys</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="blank_wallet_checkbox">
<property name="toolTip">
<string>Make a blank wallet. Blank wallets do not initially have private keys or scripts. Private keys and addresses can be imported, or an HD seed can be set, at a later time.</string>
</property>
<property name="text">
<string>Make Blank Wallet</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="external_signer_checkbox">
<property name="toolTip">
<string>Use an external signing device such as a hardware wallet. Configure the external signer script in wallet preferences first.</string>
</property>
<property name="text">
<string>External signer</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>wallet_name_line_edit</tabstop>
<tabstop>encrypt_wallet_checkbox</tabstop>
<tabstop>disable_privkeys_checkbox</tabstop>
<tabstop>blank_wallet_checkbox</tabstop>
<tabstop>external_signer_checkbox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CreateWalletDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CreateWalletDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

1636
src/qt/forms/debugwindow.ui Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditAddressDialog</class>
<widget class="QDialog" name="EditAddressDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>457</width>
<height>126</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Address</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;Label</string>
</property>
<property name="buddy">
<cstring>labelEdit</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="labelEdit">
<property name="toolTip">
<string>The label associated with this address list entry</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Address</string>
</property>
<property name="buddy">
<cstring>addressEdit</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QValidatedLineEdit" name="addressEdit">
<property name="toolTip">
<string>The address associated with this address list entry. This can only be modified for sending addresses.</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qt/qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EditAddressDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EditAddressDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HelpMessageDialog</class>
<widget class="QDialog" name="HelpMessageDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>780</width>
<height>400</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayoutLogo" stretch="0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="aboutLogo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="../vexta.qrc">:/icons/vexta</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="helpMessage">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="aboutMessage">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>4</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="okButton">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../vexta.qrc"/>
</resources>
<connections>
<connection>
<sender>okButton</sender>
<signal>accepted()</signal>
<receiver>HelpMessageDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>okButton</sender>
<signal>rejected()</signal>
<receiver>HelpMessageDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

334
src/qt/forms/intro.ui Normal file
View file

@ -0,0 +1,334 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Intro</class>
<widget class="QDialog" name="Intro">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>674</width>
<height>447</height>
</rect>
</property>
<property name="windowTitle">
<string>Welcome</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="welcomeLabel">
<property name="styleSheet">
<string notr="true">QLabel { font-style:italic; }</string>
</property>
<property name="text">
<string>Welcome to %1.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>15</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="storageLabel">
<property name="text">
<string>As this is the first time the program is launched, you can choose where %1 will store its data.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sizeWarningLabel">
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dataDirDefault">
<property name="text">
<string>Use the default data directory</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="dataDirCustom">
<property name="text">
<string>Use a custom data directory:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>60</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="dataDirectory"/>
</item>
<item>
<widget class="QPushButton" name="ellipsisButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string notr="true">…</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="freeSpace">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="errorMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblExplanation1">
<property name="text">
<string>When you click OK, %1 will begin to download and process the full %4 blockchain (%2 GB), starting with the earliest transactions from %3.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblExplanation2">
<property name="text">
<string>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblExplanation3">
<property name="text">
<string>If you have chosen to limit block chain storage (pruning), the historical data must still be downloaded and processed, but will be deleted afterward to keep your disk usage low.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="pruneOptLayout">
<item>
<widget class="QCheckBox" name="prune">
<property name="text">
<string>Limit block chain storage to</string>
</property>
<property name="toolTip">
<string>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="pruneGB">
<property name="suffix">
<string> GB</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblPruneSuffix">
<property name="buddy">
<cstring>pruneGB</cstring>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Intro</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Intro</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -0,0 +1,367 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModalOverlay</class>
<widget class="QWidget" name="ModalOverlay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>385</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="bgWidget" native="true">
<property name="styleSheet">
<string notr="true">#bgWidget { background: rgba(0,0,0,220); }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutMain" stretch="1">
<property name="leftMargin">
<number>60</number>
</property>
<property name="topMargin">
<number>60</number>
</property>
<property name="rightMargin">
<number>60</number>
</property>
<property name="bottomMargin">
<number>60</number>
</property>
<item>
<widget class="QWidget" name="contentWidget" native="true">
<property name="styleSheet">
<string notr="true">#contentWidget { background: rgba(255,255,255,240); border-radius: 6px; }
QLabel { color: rgb(40,40,40); }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutSub" stretch="1,0,0,0">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutIconText" stretch="0,1">
<property name="topMargin">
<number>20</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayoutIcon">
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="warningIcon">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/warning</normaloff>
<disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacerWarningIcon">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayoutInfoText">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="infoText">
<property name="text">
<string>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 Vexta network, as detailed below.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoTextStrong">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Attempting to spend VTX affected by not-yet-displayed transactions will not be accepted by the network.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacerInTextSpace">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacerAfterText">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelNumberOfBlocksLeft">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Number of blocks left</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="numberOfBlocksLeft">
<property name="text">
<string>Unknown…</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelLastBlockTime">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Last block time</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="newestBlockDate">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Unknown…</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelSyncDone">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Progress</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayoutSync" stretch="0,1">
<item>
<widget class="QLabel" name="percentageProgress">
<property name="text">
<string notr="true">~</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelProgressIncrease">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Progress increase per hour</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="progressIncreasePerH">
<property name="text">
<string>calculating…</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelEstimatedTimeLeft">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Estimated time left until synced</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="expectedTimeLeft">
<property name="text">
<string>calculating…</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutButtons">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Hide</string>
</property>
<property name="shortcut">
<string>Esc</string>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OpenURIDialog</class>
<widget class="QDialog" name="OpenURIDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>564</width>
<height>109</height>
</rect>
</property>
<property name="windowTitle">
<string>Open Vexta URI</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>URI:</string>
</property>
</widget>
</item>
<item>
<widget class="QValidatedLineEdit" name="uriEdit">
<property name="placeholderText">
<string notr="true">vexta:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qt/qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>OpenURIDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>OpenURIDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,503 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OverviewPage</class>
<widget class="QWidget" name="OverviewPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>798</width>
<height>318</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="topLayout">
<item>
<widget class="QLabel" name="labelAlerts">
<property name="visible">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QLabel { background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop:0 #F0D0A0, stop:1 #F8D488); color:#000000; }</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>3</number>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Balances</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="labelWalletStatus">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>The displayed information may be out of date. Your wallet automatically synchronizes with the Vexta network after a connection is established, but this process has not completed yet.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/warning</normaloff>
<disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>12</number>
</property>
<item row="2" column="2">
<widget class="QLabel" name="labelWatchPending">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Unconfirmed transactions to watch-only addresses</string>
</property>
<property name="text">
<string notr="true">0.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelUnconfirmed">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
</property>
<property name="text">
<string notr="true">0.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="labelWatchImmature">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Mined balance in watch-only addresses that has not yet matured</string>
</property>
<property name="text">
<string notr="true">0.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="Line" name="lineWatchBalance">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelTotalText">
<property name="text">
<string>Total:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="labelImmature">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Mined balance that has not yet matured</string>
</property>
<property name="text">
<string notr="true">0.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelImmatureText">
<property name="text">
<string>Immature:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="labelTotal">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current total balance</string>
</property>
<property name="text">
<string notr="true">21 000 000 000.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="labelWatchTotal">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Current total balance in watch-only addresses</string>
</property>
<property name="text">
<string notr="true">21 000 000 000.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labelWatchonly">
<property name="text">
<string>Watch-only:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelBalanceText">
<property name="text">
<string>Available:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelBalance">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current spendable balance</string>
</property>
<property name="text">
<string notr="true">21 000 000 000.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="labelWatchAvailable">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current balance in watch-only addresses</string>
</property>
<property name="text">
<string notr="true">21 000 000 000.00000000 VTX</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelPendingText">
<property name="text">
<string>Pending:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelSpendable">
<property name="text">
<string>Spendable:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Recent transactions</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="labelTransactionsStatus">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>The displayed information may be out of date. Your wallet automatically synchronizes with the Vexta network after a connection is established, but this process has not completed yet.</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/warning</normaloff>
<disabledoff>:/icons/warning</disabledoff>:/icons/warning</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="TransactionOverviewWidget" name="listTransactions">
<property name="styleSheet">
<string notr="true">QListView { background: transparent; }</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>TransactionOverviewWidget</class>
<extends>QListView</extends>
<header>qt/transactionoverviewwidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PSBTOperationsDialog</class>
<widget class="QDialog" name="PSBTOperationsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>585</width>
<height>327</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>12</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<layout class="QVBoxLayout" name="mainDialogLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="statusBar">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="transactionDescription">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonRowLayout">
<property name="spacing">
<number>5</number>
</property>
<item>
<widget class="QPushButton" name="signTransactionButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Sign Tx</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="broadcastTransactionButton">
<property name="text">
<string>Broadcast Tx</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="copyToClipboardButton">
<property name="text">
<string>Copy to Clipboard</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveButton">
<property name="text">
<string>Save…</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,381 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReceiveCoinsDialog</class>
<widget class="QDialog" name="ReceiveCoinsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>776</width>
<height>396</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<item>
<widget class="QFrame" name="frame2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>An optional amount to request. Leave this empty or zero to not request a specific amount.</string>
</property>
<property name="text">
<string>&amp;Amount:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>reqAmount</cstring>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="toolTip">
<string>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Vexta network.</string>
</property>
<property name="text">
<string>&amp;Message:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>reqMessage</cstring>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="reqLabel">
<property name="toolTip">
<string>An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Use this form to request payments. All fields are &lt;b&gt;optional&lt;/b&gt;.</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>An optional label to associate with the new receiving address.</string>
</property>
<property name="text">
<string>&amp;Label:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>reqLabel</cstring>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLineEdit" name="reqMessage">
<property name="toolTip">
<string>An optional message that is attached to the payment request and may be displayed to the sender.</string>
</property>
</widget>
</item>
<item row="7" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="receiveButton">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>&amp;Create new receiving address</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/receiving_addresses</normaloff>:/icons/receiving_addresses</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Clear all fields of the form.</string>
</property>
<property name="text">
<string>Clear</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="DigiByteAmountField" name="reqAmount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1000</width>
<height>100</height>
</size>
</property>
<property name="toolTip">
<string>An optional amount to request. Leave this empty or zero to not request a specific amount.</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useBech32">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>1000</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</string>
</property>
<property name="text">
<string>Generate native segwit (Bech32) address</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_6">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Requested payments history</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="recentRequestsView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="tabKeyNavigation">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="showRequestButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Show the selected request (does the same as double clicking an entry)</string>
</property>
<property name="text">
<string>Show</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/edit</normaloff>:/icons/edit</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeRequestButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Remove the selected entries from the list</string>
</property>
<property name="text">
<string>Remove</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>DigiByteAmountField</class>
<extends>QLineEdit</extends>
<header>qt/digibyteamountfield.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>reqLabel</tabstop>
<tabstop>reqAmount</tabstop>
<tabstop>useBech32</tabstop>
<tabstop>reqMessage</tabstop>
<tabstop>receiveButton</tabstop>
<tabstop>clearButton</tabstop>
<tabstop>recentRequestsView</tabstop>
<tabstop>showRequestButton</tabstop>
<tabstop>removeRequestButton</tabstop>
</tabstops>
<resources>
<include location="../vexta.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReceiveRequestDialog</class>
<widget class="QDialog" name="ReceiveRequestDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>413</width>
<height>229</height>
</rect>
</property>
<property name="windowTitle">
<string>Request payment to …</string>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="0" column="0" colspan="2" alignment="Qt::AlignHCenter">
<widget class="QRImageWidget" name="qr_code">
<property name="text">
<string notr="true">QR image</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="payment_header">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Payment information</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="2" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="uri_tag">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true">URI:</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="2" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="uri_content">
<property name="text">
<string notr="true">vexta:BC1…</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="address_tag">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Address:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="3" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="address_content">
<property name="text">
<string notr="true">bc1…</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="amount_tag">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Amount:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="4" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="amount_content">
<property name="text">
<string notr="true">0.00000000 VTX</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="label_tag">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Label:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="5" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="label_content">
<property name="text">
<string notr="true">label content</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="6" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="message_tag">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Message:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="6" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="message_content">
<property name="text">
<string notr="true">message content</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="0" alignment="Qt::AlignRight|Qt::AlignTop">
<widget class="QLabel" name="wallet_tag">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Wallet:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
<item row="7" column="1" alignment="Qt::AlignTop">
<widget class="QLabel" name="wallet_content">
<property name="text">
<string notr="true">wallet name</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnCopyURI">
<property name="text">
<string>Copy &amp;URI</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCopyAddress">
<property name="text">
<string>Copy &amp;Address</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnVerify">
<property name="text">
<string>&amp;Verify</string>
</property>
<property name="toolTip">
<string>Verify this address on e.g. a hardware wallet screen</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSaveAs">
<property name="text">
<string>&amp;Save Image…</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QRImageWidget</class>
<extends>QLabel</extends>
<header>qt/qrimagewidget.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>buttonBox</tabstop>
<tabstop>btnCopyURI</tabstop>
<tabstop>btnCopyAddress</tabstop>
<tabstop>btnSaveAs</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ReceiveRequestDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>135</x>
<y>230</y>
</hint>
<hint type="destinationlabel">
<x>135</x>
<y>126</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,410 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SignVerifyMessageDialog</class>
<widget class="QDialog" name="SignVerifyMessageDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>380</height>
</rect>
</property>
<property name="windowTitle">
<string>Signatures - Sign / Verify a Message</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabSignMessage">
<attribute name="title">
<string>&amp;Sign Message</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_SM">
<item>
<widget class="QLabel" name="infoLabel_SM">
<property name="text">
<string>You can sign messages/agreements with your addresses to prove you can receive VTX sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1_SM">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QValidatedLineEdit" name="addressIn_SM">
<property name="toolTip">
<string>The Vexta address to sign the message with</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addressBookButton_SM">
<property name="toolTip">
<string>Choose previously used address</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/address-book</normaloff>:/icons/address-book</iconset>
</property>
<property name="shortcut">
<string>Alt+A</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pasteButton_SM">
<property name="toolTip">
<string>Paste address from clipboard</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/editpaste</normaloff>:/icons/editpaste</iconset>
</property>
<property name="shortcut">
<string>Alt+P</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="messageIn_SM">
<property name="toolTip">
<string>Enter the message you want to sign here</string>
</property>
<property name="placeholderText">
<string>Enter the message you want to sign here</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="signatureLabel_SM">
<property name="text">
<string>Signature</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2_SM">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="signatureOut_SM">
<property name="placeholderText">
<string>Click "Sign Message" to generate signature</string>
</property>
<property name="font">
<font>
<italic>true</italic>
</font>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copySignatureButton_SM">
<property name="toolTip">
<string>Copy the current signature to the system clipboard</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/editcopy</normaloff>:/icons/editcopy</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3_SM">
<item>
<widget class="QPushButton" name="signMessageButton_SM">
<property name="toolTip">
<string>Sign the message to prove you own this Vexta address</string>
</property>
<property name="text">
<string>Sign &amp;Message</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/edit</normaloff>:/icons/edit</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearButton_SM">
<property name="toolTip">
<string>Reset all sign message fields</string>
</property>
<property name="text">
<string>Clear &amp;All</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_1_SM">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>48</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="statusLabel_SM">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2_SM">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>48</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabVerifyMessage">
<attribute name="title">
<string>&amp;Verify Message</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_VM">
<item>
<widget class="QLabel" name="infoLabel_VM">
<property name="text">
<string>Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction!</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_1_VM">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QValidatedLineEdit" name="addressIn_VM">
<property name="toolTip">
<string>The Vexta address the message was signed with</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addressBookButton_VM">
<property name="toolTip">
<string>Choose previously used address</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/address-book</normaloff>:/icons/address-book</iconset>
</property>
<property name="shortcut">
<string>Alt+A</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="messageIn_VM">
<property name="toolTip">
<string>The signed message to verify</string>
</property>
<property name="placeholderText">
<string>The signed message to verify</string>
</property>
</widget>
</item>
<item>
<widget class="QValidatedLineEdit" name="signatureIn_VM">
<property name="toolTip">
<string>The signature given when the message was signed</string>
</property>
<property name="placeholderText">
<string>The signature given when the message was signed</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2_VM">
<item>
<widget class="QPushButton" name="verifyMessageButton_VM">
<property name="toolTip">
<string>Verify the message to ensure it was signed with the specified Vexta address</string>
</property>
<property name="text">
<string>Verify &amp;Message</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/transaction_0</normaloff>:/icons/transaction_0</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="clearButton_VM">
<property name="toolTip">
<string>Reset all verify message fields</string>
</property>
<property name="text">
<string>Clear &amp;All</string>
</property>
<property name="icon">
<iconset resource="../vexta.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_1_VM">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>48</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="statusLabel_VM">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2_VM">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>48</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qt/qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../vexta.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TransactionDescDialog</class>
<widget class="QDialog" name="TransactionDescDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>620</width>
<height>250</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Transaction details</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="detailText">
<property name="toolTip">
<string>This pane shows a detailed description of the transaction</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>TransactionDescDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>TransactionDescDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui>

59
src/qt/guiconstants.h Normal file
View file

@ -0,0 +1,59 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_GUICONSTANTS_H
#define DIGIBYTE_QT_GUICONSTANTS_H
#include <cstdint>
/* Milliseconds between model updates */
static const int MODEL_UPDATE_DELAY = 250;
/* AskPassphraseDialog -- Maximum passphrase length */
static const int MAX_PASSPHRASE_SIZE = 1024;
/* DigiByteGUI -- Size of icons in status bar */
static const int STATUSBAR_ICONSIZE = 16;
static const bool DEFAULT_SPLASHSCREEN = true;
/* Invalid field background style */
#define STYLE_INVALID "background:#FF8080"
/* Transaction list -- unconfirmed transaction */
#define COLOR_UNCONFIRMED QColor(128, 128, 128)
/* Transaction list -- negative amount */
#define COLOR_NEGATIVE QColor(255, 0, 0)
/* Transaction list -- bare address (without label) */
#define COLOR_BAREADDRESS QColor(140, 140, 140)
/* Transaction list -- TX status decoration - open until date */
#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
/* 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)
/* Tooltips longer than this (in characters) are converted into rich text,
so that they can be word-wrapped.
*/
static const int TOOLTIP_WRAP_THRESHOLD = 80;
/* Number of frames in spinner animation */
#define SPINNER_FRAMES 36
#define QAPP_ORG_NAME "Vexta"
#define QAPP_ORG_DOMAIN "vexta.org"
#define QAPP_APP_NAME_DEFAULT "Vexta-Qt"
#define QAPP_APP_NAME_TESTNET "Vexta-Qt-testnet"
#define QAPP_APP_NAME_SIGNET "Vexta-Qt-signet"
#define QAPP_APP_NAME_REGTEST "Vexta-Qt-regtest"
/* One gigabyte (GB) in bytes */
static constexpr uint64_t GB_BYTES{1000000000};
// Default prune target displayed in GUI.
static constexpr int DEFAULT_PRUNE_TARGET_GB{2};
#endif // DIGIBYTE_QT_GUICONSTANTS_H

962
src/qt/guiutil.cpp Normal file
View file

@ -0,0 +1,962 @@
// Copyright (c) 2011-2020 The Bitcoin Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/guiutil.h>
#include <qt/digibyteaddressvalidator.h>
#include <qt/digibyteunits.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
#include <qt/sendcoinsrecipient.h>
#include <base58.h>
#include <chainparams.h>
#include <interfaces/node.h>
#include <key_io.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <protocol.h>
#include <script/script.h>
#include <script/standard.h>
#include <util/system.h>
#ifdef WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <shellapi.h>
#include <shlobj.h>
#include <shlwapi.h>
#endif
#include <QAbstractButton>
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QDateTime>
#include <QDesktopServices>
#include <QDoubleValidator>
#include <QFileDialog>
#include <QFont>
#include <QFontDatabase>
#include <QFontMetrics>
#include <QGuiApplication>
#include <QJsonObject>
#include <QKeyEvent>
#include <QLatin1String>
#include <QLineEdit>
#include <QList>
#include <QLocale>
#include <QMenu>
#include <QMouseEvent>
#include <QPluginLoader>
#include <QProgressDialog>
#include <QScreen>
#include <QSettings>
#include <QShortcut>
#include <QSize>
#include <QString>
#include <QTextDocument> // for Qt::mightBeRichText
#include <QThread>
#include <QUrlQuery>
#include <QtGlobal>
#include <cassert>
#include <chrono>
#if defined(Q_OS_MAC)
#include <QProcess>
void ForceActivation();
#endif
namespace GUIUtil {
QString dateTimeStr(const QDateTime &date)
{
return QLocale::system().toString(date.date(), QLocale::ShortFormat) + QString(" ") + date.toString("hh:mm");
}
QString dateTimeStr(qint64 nTime)
{
return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}
QFont fixedPitchFont(bool use_embedded_font)
{
if (use_embedded_font) {
return {"Roboto Mono"};
}
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
}
// Just some dummy data to generate a convincing random-looking (but consistent) address
static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47};
// Generate a dummy address with invalid CRC, starting with the network prefix.
static std::string DummyAddress(const CChainParams &params)
{
std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte
std::string s = EncodeBase58(sourcedata);
if (!IsValidDestinationString(s)) {
return s;
}
sourcedata[sourcedata.size()-1] += 1;
}
return "";
}
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
{
parent->setFocusProxy(widget);
widget->setFont(fixedPitchFont());
// We don't want translators to use own addresses in translations
// and this is the only place, where this address is supplied.
widget->setPlaceholderText(QObject::tr("Enter a Vexta address (e.g. %1)").arg(
QString::fromStdString(DummyAddress(Params()))));
widget->setValidator(new DigiByteAddressEntryValidator(parent));
widget->setCheckValidator(new DigiByteAddressCheckValidator(parent));
}
void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut)
{
QObject::connect(new QShortcut(shortcut, button), &QShortcut::activated, [button]() { button->animateClick(); });
}
bool parseDigiByteURI(const QUrl &uri, SendCoinsRecipient *out)
{
// return if URI is not valid or is no vexta: URI
if(!uri.isValid() || uri.scheme() != QString("vexta"))
return false;
SendCoinsRecipient rv;
rv.address = uri.path();
// Trim any following forward slash which may have been added by the OS
if (rv.address.endsWith("/")) {
rv.address.truncate(rv.address.length() - 1);
}
rv.amount = 0;
QUrlQuery uriQuery(uri);
QList<QPair<QString, QString> > items = uriQuery.queryItems();
for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
{
bool fShouldReturnFalse = false;
if (i->first.startsWith("req-"))
{
i->first.remove(0, 4);
fShouldReturnFalse = true;
}
if (i->first == "label")
{
rv.label = i->second;
fShouldReturnFalse = false;
}
if (i->first == "message")
{
rv.message = i->second;
fShouldReturnFalse = false;
}
else if (i->first == "amount")
{
if(!i->second.isEmpty())
{
if(!DigiByteUnits::parse(DigiByteUnits::DGB, i->second, &rv.amount))
{
return false;
}
}
fShouldReturnFalse = false;
}
if (fShouldReturnFalse)
return false;
}
if(out)
{
*out = rv;
}
return true;
}
bool parseDigiByteURI(QString uri, SendCoinsRecipient *out)
{
QUrl uriInstance(uri);
return parseDigiByteURI(uriInstance, out);
}
QString formatDigiByteURI(const SendCoinsRecipient &info)
{
bool bech_32 = info.address.startsWith(QString::fromStdString(Params().Bech32HRP() + "1"));
QString ret = QString("vexta:%1").arg(bech_32 ? info.address.toUpper() : info.address);
int paramCount = 0;
if (info.amount)
{
ret += QString("?amount=%1").arg(DigiByteUnits::format(DigiByteUnits::DGB, info.amount, false, DigiByteUnits::SeparatorStyle::NEVER));
paramCount++;
}
if (!info.label.isEmpty())
{
QString lbl(QUrl::toPercentEncoding(info.label));
ret += QString("%1label=%2").arg(paramCount == 0 ? "?" : "&").arg(lbl);
paramCount++;
}
if (!info.message.isEmpty())
{
QString msg(QUrl::toPercentEncoding(info.message));
ret += QString("%1message=%2").arg(paramCount == 0 ? "?" : "&").arg(msg);
paramCount++;
}
return ret;
}
bool isDust(interfaces::Node& node, const QString& address, const CAmount& amount)
{
CTxDestination dest = DecodeDestination(address.toStdString());
CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script);
return IsDust(txOut, node.getDustRelayFee());
}
QString HtmlEscape(const QString& str, bool fMultiLine)
{
QString escaped = str.toHtmlEscaped();
if(fMultiLine)
{
escaped = escaped.replace("\n", "<br>\n");
}
return escaped;
}
QString HtmlEscape(const std::string& str, bool fMultiLine)
{
return HtmlEscape(QString::fromStdString(str), fMultiLine);
}
void copyEntryData(const QAbstractItemView *view, int column, int role)
{
if(!view || !view->selectionModel())
return;
QModelIndexList selection = view->selectionModel()->selectedRows(column);
if(!selection.isEmpty())
{
// Copy first item
setClipboard(selection.at(0).data(role).toString());
}
}
QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column)
{
if(!view || !view->selectionModel())
return QList<QModelIndex>();
return view->selectionModel()->selectedRows(column);
}
bool hasEntryData(const QAbstractItemView *view, int column, int role)
{
QModelIndexList selection = getEntryData(view, column);
if (selection.isEmpty()) return false;
return !selection.at(0).data(role).toString().isEmpty();
}
QString getDefaultDataDirectory()
{
return boostPathToQString(GetDefaultDataDir());
}
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut)
{
QString selectedFilter;
QString myDir;
if(dir.isEmpty()) // Default to user documents location
{
myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
else
{
myDir = dir;
}
/* Directly convert path to native OS path separators */
QString result = QDir::toNativeSeparators(QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter));
/* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
QString selectedSuffix;
if(filter_re.exactMatch(selectedFilter))
{
selectedSuffix = filter_re.cap(1);
}
/* Add suffix if needed */
QFileInfo info(result);
if(!result.isEmpty())
{
if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
{
/* No suffix specified, add selected suffix */
if(!result.endsWith("."))
result.append(".");
result.append(selectedSuffix);
}
}
/* Return selected suffix if asked to */
if(selectedSuffixOut)
{
*selectedSuffixOut = selectedSuffix;
}
return result;
}
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut)
{
QString selectedFilter;
QString myDir;
if(dir.isEmpty()) // Default to user documents location
{
myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
else
{
myDir = dir;
}
/* Directly convert path to native OS path separators */
QString result = QDir::toNativeSeparators(QFileDialog::getOpenFileName(parent, caption, myDir, filter, &selectedFilter));
if(selectedSuffixOut)
{
/* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
QString selectedSuffix;
if(filter_re.exactMatch(selectedFilter))
{
selectedSuffix = filter_re.cap(1);
}
*selectedSuffixOut = selectedSuffix;
}
return result;
}
Qt::ConnectionType blockingGUIThreadConnection()
{
if(QThread::currentThread() != qApp->thread())
{
return Qt::BlockingQueuedConnection;
}
else
{
return Qt::DirectConnection;
}
}
bool checkPoint(const QPoint &p, const QWidget *w)
{
QWidget *atW = QApplication::widgetAt(w->mapToGlobal(p));
if (!atW) return false;
return atW->window() == w;
}
bool isObscured(QWidget *w)
{
return !(checkPoint(QPoint(0, 0), w)
&& checkPoint(QPoint(w->width() - 1, 0), w)
&& checkPoint(QPoint(0, w->height() - 1), w)
&& checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
&& checkPoint(QPoint(w->width() / 2, w->height() / 2), w));
}
void bringToFront(QWidget* w)
{
#ifdef Q_OS_MAC
ForceActivation();
#endif
if (w) {
// activateWindow() (sometimes) helps with keyboard focus on Windows
if (w->isMinimized()) {
w->showNormal();
} else {
w->show();
}
w->activateWindow();
w->raise();
}
}
void handleCloseWindowShortcut(QWidget* w)
{
QObject::connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), w), &QShortcut::activated, w, &QWidget::close);
}
void openDebugLogfile()
{
fs::path pathDebug = gArgs.GetDataDirNet() / "debug.log";
/* Open debug.log with the associated application */
if (fs::exists(pathDebug))
QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathDebug)));
}
bool openDigiByteConf()
{
fs::path pathConfig = GetConfigFile(gArgs.GetArg("-conf", DIGIBYTE_CONF_FILENAME));
/* Create the file */
fsbridge::ofstream configFile(pathConfig, std::ios_base::app);
if (!configFile.good())
return false;
configFile.close();
/* Open Vexta.conf with the associated application */
bool res = QDesktopServices::openUrl(QUrl::fromLocalFile(boostPathToQString(pathConfig)));
#ifdef Q_OS_MAC
// Workaround for macOS-specific behavior; see #15409.
if (!res) {
res = QProcess::startDetached("/usr/bin/open", QStringList{"-t", boostPathToQString(pathConfig)});
}
#endif
return res;
}
ToolTipToRichTextFilter::ToolTipToRichTextFilter(int _size_threshold, QObject *parent) :
QObject(parent),
size_threshold(_size_threshold)
{
}
bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
{
if(evt->type() == QEvent::ToolTipChange)
{
QWidget *widget = static_cast<QWidget*>(obj);
QString tooltip = widget->toolTip();
if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt") && !Qt::mightBeRichText(tooltip))
{
// Envelop with <qt></qt> to make sure Qt detects this as rich text
// Escape the current message as HTML and replace \n by <br>
tooltip = "<qt>" + HtmlEscape(tooltip, true) + "</qt>";
widget->setToolTip(tooltip);
return true;
}
}
return QObject::eventFilter(obj, evt);
}
LabelOutOfFocusEventFilter::LabelOutOfFocusEventFilter(QObject* parent)
: QObject(parent)
{
}
bool LabelOutOfFocusEventFilter::eventFilter(QObject* watched, QEvent* event)
{
if (event->type() == QEvent::FocusOut) {
auto focus_out = static_cast<QFocusEvent*>(event);
if (focus_out->reason() != Qt::PopupFocusReason) {
auto label = qobject_cast<QLabel*>(watched);
if (label) {
auto flags = label->textInteractionFlags();
label->setTextInteractionFlags(Qt::NoTextInteraction);
label->setTextInteractionFlags(flags);
}
}
}
return QObject::eventFilter(watched, event);
}
#ifdef WIN32
fs::path static StartupShortcutPath()
{
std::string chain = gArgs.GetChainName();
if (chain == CBaseChainParams::MAIN)
return GetSpecialFolderPath(CSIDL_STARTUP) / "Vexta.lnk";
if (chain == CBaseChainParams::TESTNET) // Remove this special case when CBaseChainParams::TESTNET = "testnet4"
return GetSpecialFolderPath(CSIDL_STARTUP) / "Vexta (testnet).lnk";
return GetSpecialFolderPath(CSIDL_STARTUP) / strprintf("Vexta (%s).lnk", chain);
}
bool GetStartOnSystemStartup()
{
// check for DigiByte*.lnk
return fs::exists(StartupShortcutPath());
}
bool SetStartOnSystemStartup(bool fAutoStart)
{
// If the shortcut exists already, remove it for updating
fs::remove(StartupShortcutPath());
if (fAutoStart)
{
CoInitialize(nullptr);
// Get a pointer to the IShellLink interface.
IShellLinkW* psl = nullptr;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr,
CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&psl));
if (SUCCEEDED(hres))
{
// Get the current executable path
WCHAR pszExePath[MAX_PATH];
GetModuleFileNameW(nullptr, pszExePath, ARRAYSIZE(pszExePath));
// Start client minimized
QString strArgs = "-min";
// Set -testnet /-regtest options
strArgs += QString::fromStdString(strprintf(" -chain=%s", gArgs.GetChainName()));
// Set the path to the shortcut target
psl->SetPath(pszExePath);
PathRemoveFileSpecW(pszExePath);
psl->SetWorkingDirectory(pszExePath);
psl->SetShowCmd(SW_SHOWMINNOACTIVE);
psl->SetArguments(strArgs.toStdWString().c_str());
// Query IShellLink for the IPersistFile interface for
// saving the shortcut in persistent storage.
IPersistFile* ppf = nullptr;
hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf));
if (SUCCEEDED(hres))
{
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE);
ppf->Release();
psl->Release();
CoUninitialize();
return true;
}
psl->Release();
}
CoUninitialize();
return false;
}
return true;
}
#elif defined(Q_OS_LINUX)
// Follow the Desktop Application Autostart Spec:
// https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html
fs::path static GetAutostartDir()
{
char* pszConfigHome = getenv("XDG_CONFIG_HOME");
if (pszConfigHome) return fs::path(pszConfigHome) / "autostart";
char* pszHome = getenv("HOME");
if (pszHome) return fs::path(pszHome) / ".config" / "autostart";
return fs::path();
}
fs::path static GetAutostartFilePath()
{
std::string chain = gArgs.GetChainName();
if (chain == CBaseChainParams::MAIN)
return GetAutostartDir() / "digibyte.desktop";
return GetAutostartDir() / strprintf("digibyte-%s.desktop", chain);
}
bool GetStartOnSystemStartup()
{
fsbridge::ifstream optionFile(GetAutostartFilePath());
if (!optionFile.good())
return false;
// Scan through file for "Hidden=true":
std::string line;
while (!optionFile.eof())
{
getline(optionFile, line);
if (line.find("Hidden") != std::string::npos &&
line.find("true") != std::string::npos)
return false;
}
optionFile.close();
return true;
}
bool SetStartOnSystemStartup(bool fAutoStart)
{
if (!fAutoStart)
fs::remove(GetAutostartFilePath());
else
{
char pszExePath[MAX_PATH+1];
ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1);
if (r == -1)
return false;
pszExePath[r] = '\0';
fs::create_directories(GetAutostartDir());
fsbridge::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out | std::ios_base::trunc);
if (!optionFile.good())
return false;
std::string chain = gArgs.GetChainName();
// Write a Vexta.desktop file to the autostart directory:
optionFile << "[Desktop Entry]\n";
optionFile << "Type=Application\n";
if (chain == CBaseChainParams::MAIN)
optionFile << "Name=Vexta\n";
else
optionFile << strprintf("Name=Vexta (%s)\n", chain);
optionFile << "Exec=" << pszExePath << strprintf(" -min -chain=%s\n", chain);
optionFile << "Terminal=false\n";
optionFile << "Hidden=false\n";
optionFile.close();
}
return true;
}
#else
bool GetStartOnSystemStartup() { return false; }
bool SetStartOnSystemStartup(bool fAutoStart) { return false; }
#endif
void setClipboard(const QString& str)
{
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(str, QClipboard::Clipboard);
if (clipboard->supportsSelection()) {
clipboard->setText(str, QClipboard::Selection);
}
}
fs::path qstringToBoostPath(const QString &path)
{
return fs::path(path.toStdString());
}
QString boostPathToQString(const fs::path &path)
{
return QString::fromStdString(path.string());
}
QString NetworkToQString(Network net)
{
switch (net) {
case NET_UNROUTABLE: return QObject::tr("Unroutable");
case NET_IPV4: return "IPv4";
case NET_IPV6: return "IPv6";
case NET_ONION: return "Onion";
case NET_I2P: return "I2P";
case NET_CJDNS: return "CJDNS";
case NET_INTERNAL: return QObject::tr("Internal");
case NET_MAX: assert(false);
} // no default case, so the compiler can warn about missing cases
assert(false);
}
QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction)
{
QString prefix;
if (prepend_direction) {
prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " ";
}
switch (conn_type) {
case ConnectionType::INBOUND: return prefix;
case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay");
case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay");
case ConnectionType::MANUAL: return prefix + QObject::tr("Manual");
case ConnectionType::FEELER: return prefix + QObject::tr("Feeler");
case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch");
} // no default case, so the compiler can warn about missing cases
assert(false);
}
QString formatDurationStr(int secs)
{
QStringList strList;
int days = secs / 86400;
int hours = (secs % 86400) / 3600;
int mins = (secs % 3600) / 60;
int seconds = secs % 60;
if (days)
strList.append(QObject::tr("%1 d").arg(days));
if (hours)
strList.append(QObject::tr("%1 h").arg(hours));
if (mins)
strList.append(QObject::tr("%1 m").arg(mins));
if (seconds || (!days && !hours && !mins))
strList.append(QObject::tr("%1 s").arg(seconds));
return strList.join(" ");
}
QString formatServicesStr(quint64 mask)
{
QStringList strList;
for (const auto& flag : serviceFlagsToStr(mask)) {
strList.append(QString::fromStdString(flag));
}
if (strList.size())
return strList.join(", ");
else
return QObject::tr("None");
}
QString formatPingTime(std::chrono::microseconds ping_time)
{
return (ping_time == std::chrono::microseconds::max() || ping_time == 0us) ?
QObject::tr("N/A") :
QObject::tr("%1 ms").arg(QString::number((int)(count_microseconds(ping_time) / 1000), 10));
}
QString formatTimeOffset(int64_t nTimeOffset)
{
return QObject::tr("%1 s").arg(QString::number((int)nTimeOffset, 10));
}
QString formatNiceTimeOffset(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 second(s)","",secs);
}
else if(secs < 2*HOUR_IN_SECONDS)
{
timeBehindText = QObject::tr("%n minute(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;
}
QString formatBytes(uint64_t bytes)
{
if (bytes < 1'000)
return QObject::tr("%1 B").arg(bytes);
if (bytes < 1'000'000)
return QObject::tr("%1 kB").arg(bytes / 1'000);
if (bytes < 1'000'000'000)
return QObject::tr("%1 MB").arg(bytes / 1'000'000);
return QObject::tr("%1 GB").arg(bytes / 1'000'000'000);
}
qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) {
while(font_size >= minPointSize) {
font.setPointSizeF(font_size);
QFontMetrics fm(font);
if (TextWidth(fm, text) < width) {
break;
}
font_size -= 0.5;
}
return font_size;
}
ThemedLabel::ThemedLabel(const PlatformStyle* platform_style, QWidget* parent)
: QLabel{parent}, m_platform_style{platform_style}
{
assert(m_platform_style);
}
void ThemedLabel::setThemedPixmap(const QString& image_filename, int width, int height)
{
m_image_filename = image_filename;
m_pixmap_width = width;
m_pixmap_height = height;
updateThemedPixmap();
}
void ThemedLabel::changeEvent(QEvent* e)
{
if (e->type() == QEvent::PaletteChange) {
updateThemedPixmap();
}
QLabel::changeEvent(e);
}
void ThemedLabel::updateThemedPixmap()
{
setPixmap(m_platform_style->SingleColorIcon(m_image_filename).pixmap(m_pixmap_width, m_pixmap_height));
}
ClickableLabel::ClickableLabel(const PlatformStyle* platform_style, QWidget* parent)
: ThemedLabel{platform_style, parent}
{
}
void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
}
void ClickableProgressBar::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
}
bool ItemDelegate::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) {
Q_EMIT keyEscapePressed();
}
}
return QItemDelegate::eventFilter(object, event);
}
void PolishProgressDialog(QProgressDialog* dialog)
{
#ifdef Q_OS_MAC
// Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
const int margin = TextWidth(dialog->fontMetrics(), ("X"));
dialog->resize(dialog->width() + 2 * margin, dialog->height());
#endif
// QProgressDialog estimates the time the operation will take (based on time
// for steps), and only shows itself if that estimate is beyond minimumDuration.
// The default minimumDuration value is 4 seconds, and it could make users
// think that the GUI is frozen.
dialog->setMinimumDuration(0);
}
int TextWidth(const QFontMetrics& fm, const QString& text)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
return fm.horizontalAdvance(text);
#else
return fm.width(text);
#endif
}
void LogQtInfo()
{
#ifdef QT_STATIC
const std::string qt_link{"static"};
#else
const std::string qt_link{"dynamic"};
#endif
#ifdef QT_STATICPLUGIN
const std::string plugin_link{"static"};
#else
const std::string plugin_link{"dynamic"};
#endif
LogPrintf("Qt %s (%s), plugin=%s (%s)\n", qVersion(), qt_link, QGuiApplication::platformName().toStdString(), plugin_link);
const auto static_plugins = QPluginLoader::staticPlugins();
if (static_plugins.empty()) {
LogPrintf("No static plugins.\n");
} else {
LogPrintf("Static plugins:\n");
for (const QStaticPlugin& p : static_plugins) {
QJsonObject meta_data = p.metaData();
const std::string plugin_class = meta_data.take(QString("className")).toString().toStdString();
const int plugin_version = meta_data.take(QString("version")).toInt();
LogPrintf(" %s, version %d\n", plugin_class, plugin_version);
}
}
LogPrintf("Style: %s / %s\n", QApplication::style()->objectName().toStdString(), QApplication::style()->metaObject()->className());
LogPrintf("System: %s, %s\n", QSysInfo::prettyProductName().toStdString(), QSysInfo::buildAbi().toStdString());
for (const QScreen* s : QGuiApplication::screens()) {
LogPrintf("Screen: %s %dx%d, pixel ratio=%.1f\n", s->name().toStdString(), s->size().width(), s->size().height(), s->devicePixelRatio());
}
}
void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action)
{
// The qminimal plugin does not provide window system integration.
if (QApplication::platformName() == "minimal") return;
menu->popup(point, at_action);
}
QDateTime StartOfDay(const QDate& date)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
return date.startOfDay();
#else
return QDateTime(date);
#endif
}
bool HasPixmap(const QLabel* label)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
return !label->pixmap(Qt::ReturnByValue).isNull();
#else
return label->pixmap() != nullptr;
#endif
}
QImage GetImage(const QLabel* label)
{
if (!HasPixmap(label)) {
return QImage();
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
return label->pixmap(Qt::ReturnByValue).toImage();
#else
return label->pixmap()->toImage();
#endif
}
QString MakeHtmlLink(const QString& source, const QString& link)
{
return QString(source).replace(
link,
QLatin1String("<a href=\"") + link + QLatin1String("\">") + link + QLatin1String("</a>"));
}
void PrintSlotException(
const std::exception* exception,
const QObject* sender,
const QObject* receiver)
{
std::string description = sender->metaObject()->className();
description += "->";
description += receiver->metaObject()->className();
PrintExceptionContinue(exception, description.c_str());
}
} // namespace GUIUtil

423
src/qt/guiutil.h Normal file
View file

@ -0,0 +1,423 @@
// Copyright (c) 2011-2020 The DigiByte Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_GUIUTIL_H
#define DIGIBYTE_QT_GUIUTIL_H
#include <amount.h>
#include <fs.h>
#include <net.h>
#include <netaddress.h>
#include <util/check.h>
#include <QApplication>
#include <QEvent>
#include <QHeaderView>
#include <QItemDelegate>
#include <QLabel>
#include <QMessageBox>
#include <QMetaObject>
#include <QObject>
#include <QProgressBar>
#include <QString>
#include <QTableView>
#include <cassert>
#include <chrono>
#include <utility>
class PlatformStyle;
class QValidatedLineEdit;
class SendCoinsRecipient;
namespace interfaces
{
class Node;
}
QT_BEGIN_NAMESPACE
class QAbstractButton;
class QAbstractItemView;
class QAction;
class QDateTime;
class QFont;
class QKeySequence;
class QLineEdit;
class QMenu;
class QPoint;
class QProgressDialog;
class QUrl;
class QWidget;
QT_END_NAMESPACE
/** Utility functions used by the DigiByte Qt UI.
*/
namespace GUIUtil
{
// Use this flags to prevent a "What's This" button in the title bar of the dialog on Windows.
constexpr auto dialog_flags = Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
// Create human-readable string from date
QString dateTimeStr(const QDateTime &datetime);
QString dateTimeStr(qint64 nTime);
// Return a monospace font
QFont fixedPitchFont(bool use_embedded_font = false);
// Set up widget for address
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
/**
* Connects an additional shortcut to a QAbstractButton. Works around the
* one shortcut limitation of the button's shortcut property.
* @param[in] button QAbstractButton to assign shortcut to
* @param[in] shortcut QKeySequence to use as shortcut
*/
void AddButtonShortcut(QAbstractButton* button, const QKeySequence& shortcut);
// Parse "vexta:" URI into recipient object, return true on successful parsing
bool parseDigiByteURI(const QUrl &uri, SendCoinsRecipient *out);
bool parseDigiByteURI(QString uri, SendCoinsRecipient *out);
QString formatDigiByteURI(const SendCoinsRecipient &info);
// Returns true if given address+amount meets "dust" definition
bool isDust(interfaces::Node& node, const QString& address, const CAmount& amount);
// HTML escaping for rich text controls
QString HtmlEscape(const QString& str, bool fMultiLine=false);
QString HtmlEscape(const std::string& str, bool fMultiLine=false);
/** Copy a field of the currently selected entry of a view to the clipboard. Does nothing if nothing
is selected.
@param[in] column Data column to extract from the model
@param[in] role Data role to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
void copyEntryData(const QAbstractItemView *view, int column, int role=Qt::EditRole);
/** Return a field of the currently selected entry as a QString. Does nothing if nothing
is selected.
@param[in] column Data column to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
QList<QModelIndex> getEntryData(const QAbstractItemView *view, int column);
/** Returns true if the specified field of the currently selected view entry is not empty.
@param[in] column Data column to extract from the model
@param[in] role Data role to extract from the model
@see TransactionView::contextualMenu
*/
bool hasEntryData(const QAbstractItemView *view, int column, int role);
void setClipboard(const QString& str);
/**
* Determine default data directory for operating system.
*/
QString getDefaultDataDirectory();
/** Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix
when no suffix is provided by the user.
@param[in] parent Parent window (or 0)
@param[in] caption Window caption (or empty, for default)
@param[in] dir Starting directory (or empty, to default to documents directory)
@param[in] filter Filter specification such as "Comma Separated Files (*.csv)"
@param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0).
Can be useful when choosing the save file format based on suffix.
*/
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut);
/** Get open filename, convenience wrapper for QFileDialog::getOpenFileName.
@param[in] parent Parent window (or 0)
@param[in] caption Window caption (or empty, for default)
@param[in] dir Starting directory (or empty, to default to documents directory)
@param[in] filter Filter specification such as "Comma Separated Files (*.csv)"
@param[out] selectedSuffixOut Pointer to return the suffix (file type) that was selected (or 0).
Can be useful when choosing the save file format based on suffix.
*/
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter,
QString *selectedSuffixOut);
/** Get connection type to call object slot in GUI thread with invokeMethod. The call will be blocking.
@returns If called from the GUI thread, return a Qt::DirectConnection.
If called from another thread, return a Qt::BlockingQueuedConnection.
*/
Qt::ConnectionType blockingGUIThreadConnection();
// Determine whether a widget is hidden behind other windows
bool isObscured(QWidget *w);
// Activate, show and raise the widget
void bringToFront(QWidget* w);
// Set shortcut to close window
void handleCloseWindowShortcut(QWidget* w);
// Open debug.log
void openDebugLogfile();
// Open the config file
bool openDigiByteConf();
/** Qt event filter that intercepts ToolTipChange events, and replaces the tooltip with a rich text
representation if needed. This assures that Qt can word-wrap long tooltip messages.
Tooltips longer than the provided size threshold (in characters) are wrapped.
*/
class ToolTipToRichTextFilter : public QObject
{
Q_OBJECT
public:
explicit ToolTipToRichTextFilter(int size_threshold, QObject *parent = nullptr);
protected:
bool eventFilter(QObject *obj, QEvent *evt) override;
private:
int size_threshold;
};
/**
* Qt event filter that intercepts QEvent::FocusOut events for QLabel objects, and
* resets their `textInteractionFlags' property to get rid of the visible cursor.
*
* This is a temporary fix of QTBUG-59514.
*/
class LabelOutOfFocusEventFilter : public QObject
{
Q_OBJECT
public:
explicit LabelOutOfFocusEventFilter(QObject* parent);
bool eventFilter(QObject* watched, QEvent* event) override;
};
bool GetStartOnSystemStartup();
bool SetStartOnSystemStartup(bool fAutoStart);
/** Convert QString to OS specific boost path through UTF-8 */
fs::path qstringToBoostPath(const QString &path);
/** Convert OS specific boost path to QString through UTF-8 */
QString boostPathToQString(const fs::path &path);
/** Convert enum Network to QString */
QString NetworkToQString(Network net);
/** Convert enum ConnectionType to QString */
QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction);
/** Convert seconds into a QString with days, hours, mins, secs */
QString formatDurationStr(int secs);
/** Format CNodeStats.nServices bitmask into a user-readable string */
QString formatServicesStr(quint64 mask);
/** Format a CNodeStats.m_last_ping_time into a user-readable string or display N/A, if 0 */
QString formatPingTime(std::chrono::microseconds ping_time);
/** Format a CNodeCombinedStats.nTimeOffset into a user-readable string */
QString formatTimeOffset(int64_t nTimeOffset);
QString formatNiceTimeOffset(qint64 secs);
QString formatBytes(uint64_t bytes);
qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);
class ThemedLabel : public QLabel
{
Q_OBJECT
public:
explicit ThemedLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
void setThemedPixmap(const QString& image_filename, int width, int height);
protected:
void changeEvent(QEvent* e) override;
private:
const PlatformStyle* m_platform_style;
QString m_image_filename;
int m_pixmap_width;
int m_pixmap_height;
void updateThemedPixmap();
};
class ClickableLabel : public ThemedLabel
{
Q_OBJECT
public:
explicit ClickableLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
Q_SIGNALS:
/** Emitted when the label is clicked. The relative mouse coordinates of the click are
* passed to the signal.
*/
void clicked(const QPoint& point);
protected:
void mouseReleaseEvent(QMouseEvent *event) override;
};
class ClickableProgressBar : public QProgressBar
{
Q_OBJECT
Q_SIGNALS:
/** Emitted when the progressbar is clicked. The relative mouse coordinates of the click are
* passed to the signal.
*/
void clicked(const QPoint& point);
protected:
void mouseReleaseEvent(QMouseEvent *event) override;
};
typedef ClickableProgressBar ProgressBar;
class ItemDelegate : public QItemDelegate
{
Q_OBJECT
public:
ItemDelegate(QObject* parent) : QItemDelegate(parent) {}
Q_SIGNALS:
void keyEscapePressed();
private:
bool eventFilter(QObject *object, QEvent *event) override;
};
// Fix known bugs in QProgressDialog class.
void PolishProgressDialog(QProgressDialog* dialog);
/**
* Returns the distance in pixels appropriate for drawing a subsequent character after text.
*
* In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 5.13.
* In Qt 5.11 the QFontMetrics::horizontalAdvance() was introduced.
*/
int TextWidth(const QFontMetrics& fm, const QString& text);
/**
* Writes to debug.log short info about the used Qt and the host system.
*/
void LogQtInfo();
/**
* Call QMenu::popup() only on supported QT_QPA_PLATFORM.
*/
void PopupMenu(QMenu* menu, const QPoint& point, QAction* at_action = nullptr);
/**
* Returns the start-moment of the day in local time.
*
* QDateTime::QDateTime(const QDate& date) is deprecated since Qt 5.15.
* QDate::startOfDay() was introduced in Qt 5.14.
*/
QDateTime StartOfDay(const QDate& date);
/**
* Returns true if pixmap has been set.
*
* QPixmap* QLabel::pixmap() is deprecated since Qt 5.15.
*/
bool HasPixmap(const QLabel* label);
QImage GetImage(const QLabel* label);
/**
* Splits the string into substrings wherever separator occurs, and returns
* the list of those strings. Empty strings do not appear in the result.
*
* QString::split() signature differs in different Qt versions:
* - QString::SplitBehavior is deprecated since Qt 5.15
* - Qt::SplitBehavior was introduced in Qt 5.14
* If {QString|Qt}::SkipEmptyParts behavior is required, use this
* function instead of QString::split().
*/
template <typename SeparatorType>
QStringList SplitSkipEmptyParts(const QString& string, const SeparatorType& separator)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
return string.split(separator, Qt::SkipEmptyParts);
#else
return string.split(separator, QString::SkipEmptyParts);
#endif
}
/**
* Queue a function to run in an object's event loop. This can be
* replaced by a call to the QMetaObject::invokeMethod functor overload after Qt 5.10, but
* for now use a QObject::connect for compatibility with older Qt versions, based on
* https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style
*/
template <typename Fn>
void ObjectInvoke(QObject* object, Fn&& function, Qt::ConnectionType connection = Qt::QueuedConnection)
{
QObject source;
QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection);
}
/**
* Replaces a plain text link with an HTML tagged one.
*/
QString MakeHtmlLink(const QString& source, const QString& link);
void PrintSlotException(
const std::exception* exception,
const QObject* sender,
const QObject* receiver);
/**
* A drop-in replacement of QObject::connect function
* (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that
* guaranties that all exceptions are handled within the slot.
*
* NOTE: This function is incompatible with Qt private signals.
*/
template <typename Sender, typename Signal, typename Receiver, typename Slot>
auto ExceptionSafeConnect(
Sender sender, Signal signal, Receiver receiver, Slot method,
Qt::ConnectionType type = Qt::AutoConnection)
{
return QObject::connect(
sender, signal, receiver,
[sender, receiver, method](auto&&... args) {
bool ok{true};
try {
(receiver->*method)(std::forward<decltype(args)>(args)...);
} catch (const NonFatalCheckError& e) {
PrintSlotException(&e, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleNonFatalException",
blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(e.what())));
} catch (const std::exception& e) {
PrintSlotException(&e, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleRunawayException",
blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(e.what())));
} catch (...) {
PrintSlotException(nullptr, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleRunawayException",
blockingGUIThreadConnection(),
Q_ARG(QString, "Unknown failure occurred."));
}
assert(ok);
},
type);
}
} // namespace GUIUtil
#endif // DIGIBYTE_QT_GUIUTIL_H

67
src/qt/initexecutor.cpp Normal file
View file

@ -0,0 +1,67 @@
// Copyright (c) 2014-2021 The Bitcoin Core developers
// Copyright (c) 2013-2021 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <qt/initexecutor.h>
#include <interfaces/node.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <exception>
#include <QDebug>
#include <QObject>
#include <QString>
#include <QThread>
InitExecutor::InitExecutor(interfaces::Node& node)
: QObject(), m_node(node)
{
this->moveToThread(&m_thread);
m_thread.start();
}
InitExecutor::~InitExecutor()
{
qDebug() << __func__ << ": Stopping thread";
m_thread.quit();
m_thread.wait();
qDebug() << __func__ << ": Stopped thread";
}
void InitExecutor::handleRunawayException(const std::exception* e)
{
PrintExceptionContinue(e, "Runaway exception");
Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings().translated));
}
void InitExecutor::initialize()
{
try {
util::ThreadRename("qt-init");
qDebug() << __func__ << ": Running initialization in thread";
interfaces::BlockAndHeaderTipInfo tip_info;
bool rv = m_node.appInitMain(&tip_info);
Q_EMIT initializeResult(rv, tip_info);
} catch (const std::exception& e) {
handleRunawayException(&e);
} catch (...) {
handleRunawayException(nullptr);
}
}
void InitExecutor::shutdown()
{
try {
qDebug() << __func__ << ": Running Shutdown in thread";
m_node.appShutdown();
qDebug() << __func__ << ": Shutdown finished";
Q_EMIT shutdownResult();
} catch (const std::exception& e) {
handleRunawayException(&e);
} catch (...) {
handleRunawayException(nullptr);
}
}

47
src/qt/initexecutor.h Normal file
View file

@ -0,0 +1,47 @@
// Copyright (c) 2014-2021 The Bitcoin Core developers
// Copyright (c) 2014-2021 The DigiByte 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_INITEXECUTOR_H
#define BITCOIN_QT_INITEXECUTOR_H
#include <interfaces/node.h>
#include <exception>
#include <QObject>
#include <QThread>
QT_BEGIN_NAMESPACE
class QString;
QT_END_NAMESPACE
/** Class encapsulating Vexta Core startup and shutdown.
* Allows running startup and shutdown in a different thread from the UI thread.
*/
class InitExecutor : public QObject
{
Q_OBJECT
public:
explicit InitExecutor(interfaces::Node& node);
~InitExecutor();
public Q_SLOTS:
void initialize();
void shutdown();
Q_SIGNALS:
void initializeResult(bool success, interfaces::BlockAndHeaderTipInfo tip_info);
void shutdownResult();
void runawayException(const QString& message);
private:
/// Pass fatal exception message to UI thread
void handleRunawayException(const std::exception* e);
interfaces::Node& m_node;
QThread m_thread;
};
#endif // DIGIBYTE_QT_INITEXECUTOR_H

398
src/qt/intro.cpp Normal file
View file

@ -0,0 +1,398 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include <config/vexta-config.h>
#endif
#include <chainparams.h>
#include <fs.h>
#include <qt/intro.h>
#include <qt/forms/ui_intro.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <interfaces/node.h>
#include <util/system.h>
#include <validation.h>
#include <QFileDialog>
#include <QSettings>
#include <QMessageBox>
#include <cmath>
/* Check free space asynchronously to prevent hanging the UI thread.
Up to one request to check a path is in flight to this thread; when the check()
function runs, the current path is requested from the associated Intro object.
The reply is sent back through a signal.
This ensures that no queue of checking requests is built up while the user is
still entering the path, and that always the most recently entered path is checked as
soon as the thread becomes available.
*/
class FreespaceChecker : public QObject
{
Q_OBJECT
public:
explicit FreespaceChecker(Intro *intro);
enum Status {
ST_OK,
ST_ERROR
};
public Q_SLOTS:
void check();
Q_SIGNALS:
void reply(int status, const QString &message, quint64 available);
private:
Intro *intro;
};
#include <qt/intro.moc>
FreespaceChecker::FreespaceChecker(Intro *_intro)
{
this->intro = _intro;
}
void FreespaceChecker::check()
{
QString dataDirStr = intro->getPathToCheck();
fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
uint64_t freeBytesAvailable = 0;
int replyStatus = ST_OK;
QString replyMessage = tr("A new data directory will be created.");
/* Find first parent that exists, so that fs::space does not fail */
fs::path parentDir = dataDir;
fs::path parentDirOld = fs::path();
while(parentDir.has_parent_path() && !fs::exists(parentDir))
{
parentDir = parentDir.parent_path();
/* Check if we make any progress, break if not to prevent an infinite loop here */
if (parentDirOld == parentDir)
break;
parentDirOld = parentDir;
}
try {
freeBytesAvailable = fs::space(parentDir).available;
if(fs::exists(dataDir))
{
if(fs::is_directory(dataDir))
{
QString separator = "<code>" + QDir::toNativeSeparators("/") + tr("name") + "</code>";
replyStatus = ST_OK;
replyMessage = tr("Directory already exists. Add %1 if you intend to create a new directory here.").arg(separator);
} else {
replyStatus = ST_ERROR;
replyMessage = tr("Path already exists, and is not a directory.");
}
}
} catch (const fs::filesystem_error&)
{
/* Parent directory does not exist or is not accessible */
replyStatus = ST_ERROR;
replyMessage = tr("Cannot create data directory here.");
}
Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
}
namespace {
//! Return pruning size that will be used if automatic pruning is enabled.
int GetPruneTargetGB()
{
int64_t prune_target_mib = gArgs.GetArg("-prune", 0);
// >1 means automatic pruning is enabled by config, 1 means manual pruning, 0 means no pruning.
return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib) : DEFAULT_PRUNE_TARGET_GB;
}
} // namespace
Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_size_gb) :
QDialog(parent, GUIUtil::dialog_flags),
ui(new Ui::Intro),
thread(nullptr),
signalled(false),
m_blockchain_size_gb(blockchain_size_gb),
m_chain_state_size_gb(chain_state_size_gb),
m_prune_target_gb{GetPruneTargetGB()}
{
ui->setupUi(this);
ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME));
ui->storageLabel->setText(ui->storageLabel->text().arg(PACKAGE_NAME));
ui->lblExplanation1->setText(ui->lblExplanation1->text()
.arg(PACKAGE_NAME)
.arg(m_blockchain_size_gb)
.arg(2026)
.arg(tr("Vexta"))
);
ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
const int min_prune_target_GB = std::ceil(MIN_DISK_SPACE_FOR_BLOCK_FILES / 1e9);
ui->pruneGB->setRange(min_prune_target_GB, std::numeric_limits<int>::max());
if (gArgs.GetArg("-prune", 0) > 1) { // -prune=1 means enabled, above that it's a size in MiB
ui->prune->setChecked(true);
ui->prune->setEnabled(false);
}
ui->pruneGB->setValue(m_prune_target_gb);
ui->pruneGB->setToolTip(ui->prune->toolTip());
ui->lblPruneSuffix->setToolTip(ui->prune->toolTip());
UpdatePruneLabels(ui->prune->isChecked());
connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) {
UpdatePruneLabels(prune_checked);
UpdateFreeSpaceLabel();
});
connect(ui->pruneGB, qOverload<int>(&QSpinBox::valueChanged), [this](int prune_GB) {
m_prune_target_gb = prune_GB;
UpdatePruneLabels(ui->prune->isChecked());
UpdateFreeSpaceLabel();
});
startThread();
}
Intro::~Intro()
{
delete ui;
/* Ensure thread is finished before it is deleted */
thread->quit();
thread->wait();
}
QString Intro::getDataDirectory()
{
return ui->dataDirectory->text();
}
void Intro::setDataDirectory(const QString &dataDir)
{
ui->dataDirectory->setText(dataDir);
if(dataDir == GUIUtil::getDefaultDataDirectory())
{
ui->dataDirDefault->setChecked(true);
ui->dataDirectory->setEnabled(false);
ui->ellipsisButton->setEnabled(false);
} else {
ui->dataDirCustom->setChecked(true);
ui->dataDirectory->setEnabled(true);
ui->ellipsisButton->setEnabled(true);
}
}
int64_t Intro::getPruneMiB() const
{
switch (ui->prune->checkState()) {
case Qt::Checked:
return PruneGBtoMiB(m_prune_target_gb);
case Qt::Unchecked: default:
return 0;
}
}
bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
{
did_show_intro = false;
QSettings settings;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
if(!gArgs.GetArg("-datadir", "").empty())
return true;
/* 1) Default data directory for operating system */
QString dataDir = GUIUtil::getDefaultDataDirectory();
/* 2) Allow QSettings to override default dir */
dataDir = settings.value("strDataDir", dataDir).toString();
if(!fs::exists(GUIUtil::qstringToBoostPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
{
/* Use selectParams here to guarantee Params() can be used by node interface */
try {
SelectParams(gArgs.GetChainName());
} catch (const std::exception&) {
return false;
}
/* If current default data directory does not exist, let the user choose one */
Intro intro(0, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize());
intro.setDataDirectory(dataDir);
intro.setWindowIcon(QIcon(":icons/vexta"));
did_show_intro = true;
while(true)
{
if(!intro.exec())
{
/* Cancel clicked */
return false;
}
dataDir = intro.getDataDirectory();
try {
if (TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir))) {
// If a new data directory has been created, make wallets subdirectory too
TryCreateDirectories(GUIUtil::qstringToBoostPath(dataDir) / "wallets");
}
break;
} catch (const fs::filesystem_error&) {
QMessageBox::critical(nullptr, PACKAGE_NAME,
tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
/* fall through, back to choosing screen */
}
}
// Additional preferences:
prune_MiB = intro.getPruneMiB();
settings.setValue("strDataDir", dataDir);
settings.setValue("fReset", false);
}
/* Only override -datadir if different from the default, to make it possible to
* override -datadir in the Vexta.conf file in the default data directory
* (to be consistent with vextad behavior)
*/
if(dataDir != GUIUtil::getDefaultDataDirectory()) {
gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting
}
return true;
}
void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
{
switch(status)
{
case FreespaceChecker::ST_OK:
ui->errorMessage->setText(message);
ui->errorMessage->setStyleSheet("");
break;
case FreespaceChecker::ST_ERROR:
ui->errorMessage->setText(tr("Error") + ": " + message);
ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
break;
}
/* Indicate number of bytes available */
if(status == FreespaceChecker::ST_ERROR)
{
ui->freeSpace->setText("");
} else {
m_bytes_available = bytesAvailable;
if (ui->prune->isEnabled()) {
ui->prune->setChecked(m_bytes_available < (m_blockchain_size_gb + m_chain_state_size_gb + 10) * GB_BYTES);
}
UpdateFreeSpaceLabel();
}
/* Don't allow confirm in ERROR state */
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
}
void Intro::UpdateFreeSpaceLabel()
{
QString freeString = tr("%1 GB of free space available").arg(m_bytes_available / GB_BYTES);
if (m_bytes_available < m_required_space_gb * GB_BYTES) {
freeString += " " + tr("(of %1 GB needed)").arg(m_required_space_gb);
ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
} else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) {
freeString += " " + tr("(%1 GB needed for full chain)").arg(m_required_space_gb);
ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
} else {
ui->freeSpace->setStyleSheet("");
}
ui->freeSpace->setText(freeString + ".");
}
void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
{
/* Disable OK button until check result comes in */
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
checkPath(dataDirStr);
}
void Intro::on_ellipsisButton_clicked()
{
QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(nullptr, "Choose data directory", ui->dataDirectory->text()));
if(!dir.isEmpty())
ui->dataDirectory->setText(dir);
}
void Intro::on_dataDirDefault_clicked()
{
setDataDirectory(GUIUtil::getDefaultDataDirectory());
}
void Intro::on_dataDirCustom_clicked()
{
ui->dataDirectory->setEnabled(true);
ui->ellipsisButton->setEnabled(true);
}
void Intro::startThread()
{
thread = new QThread(this);
FreespaceChecker *executor = new FreespaceChecker(this);
executor->moveToThread(thread);
connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
/* make sure executor object is deleted in its own thread */
connect(thread, &QThread::finished, executor, &QObject::deleteLater);
thread->start();
}
void Intro::checkPath(const QString &dataDir)
{
mutex.lock();
pathToCheck = dataDir;
if(!signalled)
{
signalled = true;
Q_EMIT requestCheck();
}
mutex.unlock();
}
QString Intro::getPathToCheck()
{
QString retval;
mutex.lock();
retval = pathToCheck;
signalled = false; /* new request can be queued now */
mutex.unlock();
return retval;
}
void Intro::UpdatePruneLabels(bool prune_checked)
{
m_required_space_gb = m_blockchain_size_gb + m_chain_state_size_gb;
QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
if (prune_checked && m_prune_target_gb <= m_blockchain_size_gb) {
m_required_space_gb = m_prune_target_gb + m_chain_state_size_gb;
storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory.");
}
ui->lblExplanation3->setVisible(prune_checked);
ui->pruneGB->setEnabled(prune_checked);
static constexpr uint64_t nPowTargetSpacing = 10 * 60; // from chainparams, which we don't have at this stage
static constexpr uint32_t expected_block_data_size = 2250000; // includes undo data
const uint64_t expected_backup_days = m_prune_target_gb * 1e9 / (uint64_t(expected_block_data_size) * 86400 / nPowTargetSpacing);
ui->lblPruneSuffix->setText(
//: Explanatory text on the capability of the current prune target.
tr("(sufficient to restore backups %n day(s) old)", "", expected_backup_days));
ui->sizeWarningLabel->setText(
tr("%1 will download and store a copy of the Vexta blockchain.").arg(PACKAGE_NAME) + " " +
storageRequiresMsg.arg(m_required_space_gb) + " " +
tr("The wallet will also be stored in this directory.")
);
this->adjustSize();
}

88
src/qt/intro.h Normal file
View file

@ -0,0 +1,88 @@
// Copyright (c) 2009-2020 The Bitcoin Core developers
// Copyright (c) 2014-2020 The DigiByte Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef DIGIBYTE_QT_INTRO_H
#define DIGIBYTE_QT_INTRO_H
#include <QDialog>
#include <QMutex>
#include <QThread>
static const bool DEFAULT_CHOOSE_DATADIR = false;
class FreespaceChecker;
namespace interfaces {
class Node;
}
namespace Ui {
class Intro;
}
/** Introduction screen (pre-GUI startup).
Allows the user to choose a data directory,
in which the wallet and block chain will be stored.
*/
class Intro : public QDialog
{
Q_OBJECT
public:
explicit Intro(QWidget *parent = nullptr,
int64_t blockchain_size_gb = 0, int64_t chain_state_size_gb = 0);
~Intro();
QString getDataDirectory();
void setDataDirectory(const QString &dataDir);
int64_t getPruneMiB() const;
/**
* Determine data directory. Let the user choose if the current one doesn't exist.
* Let the user configure additional preferences such as pruning.
*
* @returns true if a data directory was selected, false if the user cancelled the selection
* dialog.
*
* @note do NOT call global gArgs.GetDataDirNet() before calling this function, this
* will cause the wrong path to be cached.
*/
static bool showIfNeeded(bool& did_show_intro, int64_t& prune_MiB);
Q_SIGNALS:
void requestCheck();
public Q_SLOTS:
void setStatus(int status, const QString &message, quint64 bytesAvailable);
private Q_SLOTS:
void on_dataDirectory_textChanged(const QString &arg1);
void on_ellipsisButton_clicked();
void on_dataDirDefault_clicked();
void on_dataDirCustom_clicked();
private:
Ui::Intro *ui;
QThread *thread;
QMutex mutex;
bool signalled;
QString pathToCheck;
const int64_t m_blockchain_size_gb;
const int64_t m_chain_state_size_gb;
//! Total required space (in GB) depending on user choice (prune or not prune).
int64_t m_required_space_gb{0};
uint64_t m_bytes_available{0};
int64_t m_prune_target_gb;
void startThread();
void checkPath(const QString &dataDir);
QString getPathToCheck();
void UpdatePruneLabels(bool prune_checked);
void UpdateFreeSpaceLabel();
friend class FreespaceChecker;
};
#endif // DIGIBYTE_QT_INTRO_H

3743
src/qt/locale/digibyte_af.ts Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,429 @@
<TS language="am" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>አድራሻ ወይም መለያ ስም ለመቀየር ቀኙን ጠቅ ያድርጉ</translation>
</message>
<message>
<source>Create a new address</source>
<translation>አዲስ አድራሻ ፍጠር</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;አዲስ</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>አሁን የተመረጠውን አድራሻ ወደ ሲስተሙ ቅንጥብ ሰሌዳ ቅዳ</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;ቅዳ</translation>
</message>
<message>
<source>C&amp;lose</source>
<translation>ዝጋ</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>አሁን የተመረጠውን አድራሻ ከዝርዝሩ ውስጥ ሰርዝ</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>በአሁኑ ማውጫ ውስጥ ያለውን መረጃ ወደ አንድ ፋይል ላክ</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>&amp;ላክ</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>&amp;ሰርዝ</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>ገንዘብ/ኮይኖች የሚልኩለትን አድራሻ ይምረጡ</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>ገንዘብ/ኮይኖች የሚቀበሉበትን አድራሻ ይምረጡ</translation>
</message>
<message>
<source>C&amp;hoose</source>
<translation>ምረጥ</translation>
</message>
<message>
<source>Sending addresses</source>
<translation>የመላኪያ አድራሻዎች</translation>
</message>
<message>
<source>Receiving addresses</source>
<translation>የመቀበያ አድራሻዎች</translation>
</message>
<message>
<source>These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
<translation>እነኚ የቢትኮይን ክፍያ የመላኪያ አድራሻዎችዎ ናቸው:: ገንዘብ/ኮይኖች ከመላክዎ በፊት መጠኑን እና የመቀበያ አድራሻውን ሁልጊዜ ያረጋግጡ::</translation>
</message>
<message>
<source>&amp;Copy Address</source>
<translation>&amp;አድራሻ ቅዳ</translation>
</message>
<message>
<source>Copy &amp;Label</source>
<translation>ቅዳ &amp;መለያ ስም</translation>
</message>
<message>
<source>&amp;Edit</source>
<translation> &amp;ቀይር</translation>
</message>
<message>
<source>Export Address List</source>
<translation>የአድራሻ ዝርዝር ላክ</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>ኮማ ሴፓሬትድ ፋይል (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>ወደ ውጪ መላክ አልተሳካም</translation>
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
<translation>የአድራሻ ዝርዝሩን ወደ %1 ለማስቀመጥ ሲሞከር ስህተት አጋጥሟል:: እባክዎ መልሰው ይሞክሩ::</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
<translation>መለያ ስም</translation>
</message>
<message>
<source>Address</source>
<translation>አድራሻ</translation>
</message>
<message>
<source>(no label)</source>
<translation>(መለያ ስም የለም)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
<translation>የይለፍ-ሐረግ ንግግር</translation>
</message>
<message>
<source>Enter passphrase</source>
<translation>የይለፍ-ሐረግዎን ያስገቡ</translation>
</message>
<message>
<source>New passphrase</source>
<translation>አዲስ የይለፍ-ሐረግ</translation>
</message>
<message>
<source>Repeat new passphrase</source>
<translation>አዲስ የይለፍ-ሐረጉን ይድገሙት</translation>
</message>
<message>
<source>Encrypt wallet</source>
<translation>የቢትኮይን ቦርሳውን አመስጥር</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
<translation>ይህ ክንዋኔ የቢትኮይን ቦርሳዎን ለመክፈት የቦርሳዎ ይለፍ-ሐረግ ያስፈልገዋል::</translation>
</message>
<message>
<source>Unlock wallet</source>
<translation>የቢትኮይን ቦርሳውን ክፈት</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
<translation>ይህ ክንዋኔ የቢትኮይን ቦርሳዎን ለመፍታት የቦርሳዎ ይለፍ-ሐረግ ያስፈልገዋል::</translation>
</message>
<message>
<source>Decrypt wallet</source>
<translation>የቢትኮይን ቦርሳውን ፍታ</translation>
</message>
<message>
<source>Change passphrase</source>
<translation>ይለፍ-ሐረግ ለውጥ</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
<translation>የቢትኮይን ቦርሳዎን ማመስጠር ያረጋግጡ</translation>
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR VTX&lt;/b&gt;!</source>
<translation>ማስጠንቀቂያ: የቢትኮይን ቦርሳዎን አመስጥረው የይለፍ-ሐረግዎን ካጡት&lt;b&gt;ቢትኮይኖቾን በሙሉ ያጣሉ&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
<translation>እርግጠኛ ነዎት ቦርሳዎን ማመስጠር ይፈልጋሉ?</translation>
</message>
<message>
<source>Wallet encrypted</source>
<translation>ቦርሳዎ ምስጢር ተደርጓል</translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>አስፈላጊ: ከ ቦርሳ ፋይልዎ ያከናወኗቸው ቀደም ያሉ ምትኬዎች በአዲስ በተፈጠረ የማመስጠሪያ ፋይል ውስጥ መተካት አለባቸው. ለደህንነት ሲባል, አዲሱን የተመሰጠ የቦርሳ ፋይል መጠቀም ሲጀመሩ ወዲያውኑ ቀደም ሲል ያልተመሰጠሩ የቦርሳ ፋይል ቅጂዎች ዋጋ ቢስ ይሆናሉ::</translation>
</message>
<message>
<source>Wallet encryption failed</source>
<translation>የቦርሳ ማመስጠር አልተሳካም</translation>
</message>
<message>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<translation>የቦርሳ ማመስጠር በውስጣዊ ስህተት ምክንያት አልተሳካም:: ቦርሳዎ አልተመሰጠረም::</translation>
</message>
<message>
<source>The supplied passphrases do not match.</source>
<translation>የተሰጡት የይለፍ-ሐረግዎች አይዛመዱም::</translation>
</message>
<message>
<source>Wallet unlock failed</source>
<translation>ቦርሳ መክፈት አልተሳካም</translation>
</message>
<message>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
<translation>ቦርሳ ለመፍታት ያስገቡት የይለፍ-ሐረግ ትክክል አልነበረም::</translation>
</message>
<message>
<source>Wallet decryption failed</source>
<translation>ቦርሳ መፍታት አልተሳካም </translation>
</message>
<message>
<source>Wallet passphrase was successfully changed.</source>
<translation>የቦርሳ የይለፍ-ሐረግ በተሳካ ሁኔታ ተቀይሯል.</translation>
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
<translation>ማስጠንቀቂያ: የ "Caps Lock" ቁልፍ በርቷል!</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
<message>
<source>IP/Netmask</source>
<translation>አይፒ/ኔትማስክ IP/Netmask</translation>
</message>
<message>
<source>Banned Until</source>
<translation>ታግደዋል እስከ</translation>
</message>
</context>
<context>
<name>DigiByteGUI</name>
<message>
<source>Sign &amp;message...</source>
<translation>ምልክትና መልእክት...</translation>
</message>
<message>
<source>Synchronizing with network...</source>
<translation>ከኔትወርክ ጋራ በማመሳሰል ላይ ነው...</translation>
</message>
<message>
<source>&amp;Overview</source>
<translation>&amp;አጠቃላይ እይታ</translation>
</message>
<message>
<source>Show general overview of wallet</source>
<translation>የቦርሳ አጠቃላይ እይታ ኣሳይ</translation>
</message>
<message>
<source>&amp;Transactions</source>
<translation>&amp;ግብይቶች</translation>
</message>
<message>
<source>Browse transaction history</source>
<translation>የግብይት ታሪክ ያስሱ</translation>
</message>
<message>
<source>E&amp;xit</source>
<translation>ውጣ</translation>
</message>
<message>
<source>Quit application</source>
<translation>አፕሊኬሽኑን አቁም</translation>
</message>
<message>
<source>&amp;About %1</source>
<translation>&amp;ስለ %1</translation>
</message>
<message>
<source>Show information about %1</source>
<translation>ስለ %1 መረጃ አሳይ</translation>
</message>
<message>
<source>About &amp;Qt</source>
<translation>ስለ &amp;Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
<translation>ስለ Qt መረጃ አሳይ</translation>
</message>
<message>
<source>&amp;Options...</source>
<translation>&amp;አማራጮች...</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>(no label)</source>
<translation>(መለያ ስም የለም)</translation>
</message>
</context>
<context>
<name>CreateWalletActivity</name>
</context>
<context>
<name>CreateWalletDialog</name>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
</context>
<context>
<name>Intro</name>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OpenWalletActivity</name>
</context>
<context>
<name>OptionsDialog</name>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PSBTOperationsDialog</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
</context>
<context>
<name>ReceiveCoinsDialog</name>
</context>
<context>
<name>ReceiveRequestDialog</name>
</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
<source>Label</source>
<translation>መለያ ስም</translation>
</message>
<message>
<source>(no label)</source>
<translation>(መለያ ስም የለም)</translation>
</message>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>(no label)</source>
<translation>(መለያ ስም የለም)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Label</source>
<translation>መለያ ስም</translation>
</message>
<message>
<source>(no label)</source>
<translation>(መለያ ስም የለም)</translation>
</message>
</context>
<context>
<name>TransactionView</name>
<message>
<source>Comma separated file (*.csv)</source>
<translation>ኮማ ሴፓሬትድ ፋይል (*.csv)</translation>
</message>
<message>
<source>Label</source>
<translation>መለያ ስም</translation>
</message>
<message>
<source>Address</source>
<translation>አድራሻ</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>ወደ ውጪ መላክ አልተሳካም</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletController</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
<translation>&amp;ላክ</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>በአሁኑ ማውጫ ውስጥ ያለውን መረጃ ወደ አንድ ፋይል ላክ</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
</context>
</TS>

3404
src/qt/locale/digibyte_ar.ts Normal file

File diff suppressed because it is too large Load diff

1235
src/qt/locale/digibyte_be.ts Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2367
src/qt/locale/digibyte_bg.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,913 @@
<TS language="bg_BG" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Клик с десен бутон на мишката за промяна на адрес или етикет</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Създай нов адрес</translation>
</message>
<message>
<source>&amp;New</source>
<translation>Нов</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Копирай текущо избрания адрес към клипборда</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Copy</translation>
</message>
<message>
<source>C&amp;lose</source>
<translation>Затвори</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Изтрий текущо избрания адрес от листа</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Изнеси данните в избрания раздел към файл</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>Изнеси</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>&amp;Изтрий</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>Избери адреса на който да пратиш монети</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>Избери адреса на който да получиш монети</translation>
</message>
<message>
<source>C&amp;hoose</source>
<translation>Избери</translation>
</message>
<message>
<source>Sending addresses</source>
<translation>Адрес за пращане</translation>
</message>
<message>
<source>Receiving addresses</source>
<translation>Адрес за получаване</translation>
</message>
<message>
<source>These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
<translation>Тези са вашите Биткойн адреси за изпращане на монети. Винаги проверявайте количеството и получаващия адрес преди изпращане. </translation>
</message>
<message>
<source>These are your Vexta addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
<translation>Това са вашите Биткойн адреси за получаване на монети. Препоръчително е да ползвате нов адрес на всяка транзакция.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
<translation>Копирай адрес</translation>
</message>
<message>
<source>Copy &amp;Label</source>
<translation>Копирай етикет</translation>
</message>
<message>
<source>&amp;Edit</source>
<translation>Редактирай</translation>
</message>
<message>
<source>Export Address List</source>
<translation>Изнеси лист с адреси</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Comma separated file (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Изнасянето се провали</translation>
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
<translation>Получи се грешка при запазването на листа с адреси към %1. Моля опитайте пак.</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
<translation>Етикет</translation>
</message>
<message>
<source>Address</source>
<translation>Адрес</translation>
</message>
<message>
<source>(no label)</source>
<translation>(без етикет)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
<translation>Диалог за пропуск</translation>
</message>
<message>
<source>Enter passphrase</source>
<translation>Въведи парола</translation>
</message>
<message>
<source>New passphrase</source>
<translation>Нова парола</translation>
</message>
<message>
<source>Repeat new passphrase</source>
<translation>Повтори парола</translation>
</message>
<message>
<source>Encrypt wallet</source>
<translation>Криптирай портфейл</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
<translation>Тази операция изисква вашата парола на портфейла за отключването на портфейла.</translation>
</message>
<message>
<source>Unlock wallet</source>
<translation>Отключи портфейла</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
<translation>Тази операция изисква вашата парола на портфейла за декриптирането на портфейла.</translation>
</message>
<message>
<source>Decrypt wallet</source>
<translation>Декриптирай портфейл</translation>
</message>
<message>
<source>Change passphrase</source>
<translation>Промени парола</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
<translation>Потвърди криптирането на порфейла</translation>
</message>
<message>
<source>Wallet encrypted</source>
<translation>портфейлa е шифрован</translation>
</message>
<message>
<source>Wallet unlock failed</source>
<translation>Отключването не бе успешно</translation>
</message>
<message>
<source>Wallet decryption failed</source>
<translation>Отключването не бе успешно</translation>
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
<translation>Внимание:Бутонът Caps Lock е включен.</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
<message>
<source>IP/Netmask</source>
<translation>IP/Мрежова маска</translation>
</message>
<message>
<source>Banned Until</source>
<translation>Блокиран до</translation>
</message>
</context>
<context>
<name>DigiByteGUI</name>
<message>
<source>Sign &amp;message...</source>
<translation>Подпиши съобщение...</translation>
</message>
<message>
<source>Synchronizing with network...</source>
<translation>Синхронизиране с мрежата...</translation>
</message>
<message>
<source>&amp;Overview</source>
<translation>Преглед</translation>
</message>
<message>
<source>Node</source>
<translation>Възел</translation>
</message>
<message>
<source>Show general overview of wallet</source>
<translation>Покажи общ преглед на портфейла</translation>
</message>
<message>
<source>&amp;Transactions</source>
<translation>Транзакции</translation>
</message>
<message>
<source>Browse transaction history</source>
<translation>Разгледай история на транзакциите</translation>
</message>
<message>
<source>E&amp;xit</source>
<translation>Изход</translation>
</message>
<message>
<source>Quit application</source>
<translation>Излез от приложението</translation>
</message>
<message>
<source>&amp;About %1</source>
<translation>За %1</translation>
</message>
<message>
<source>Show information about %1</source>
<translation>Покажи информация за %1</translation>
</message>
<message>
<source>About &amp;Qt</source>
<translation>Относно Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
<translation>Покажи информация отностно Qt</translation>
</message>
<message>
<source>&amp;Options...</source>
<translation>Настройки...</translation>
</message>
<message>
<source>Modify configuration options for %1</source>
<translation>Промени конфигурации за %1</translation>
</message>
<message>
<source>&amp;Encrypt Wallet...</source>
<translation>Криптирай портфейл</translation>
</message>
<message>
<source>&amp;Backup Wallet...</source>
<translation>Направи резервно копие на портфейла...</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
<translation>Промени паролата...</translation>
</message>
<message>
<source>&amp;Sending addresses...</source>
<translation>Адреси за пращане...</translation>
</message>
<message>
<source>&amp;Receiving addresses...</source>
<translation>Адреси за получаване...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
<translation>Отвори URI</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
<translation>Повторно индексиране на блоковете на диска...</translation>
</message>
<message>
<source>Wallet</source>
<translation>портфейл</translation>
</message>
<message>
<source>&amp;Send</source>
<translation>&amp;изпращам</translation>
</message>
<message>
<source>&amp;Receive</source>
<translation>&amp;получавам</translation>
</message>
<message>
<source>Tabs toolbar</source>
<translation>Лентата с инструменти</translation>
</message>
<message>
<source>Error</source>
<translation>грешка</translation>
</message>
<message>
<source>Warning</source>
<translation>Внимание</translation>
</message>
<message>
<source>Information</source>
<translation>Информация</translation>
</message>
<message>
<source>Up to date</source>
<translation>Актуално</translation>
</message>
<message>
<source>%1 client</source>
<translation>%1 клиент</translation>
</message>
<message>
<source>Catching up...</source>
<translation>Наваксвам</translation>
</message>
<message>
<source>Date: %1
</source>
<translation>Дата: %1
</translation>
</message>
<message>
<source>Sent transaction</source>
<translation>Изпратена транзакция</translation>
</message>
<message>
<source>Incoming transaction</source>
<translation>Входяща транзакция</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>Coin Selection</source>
<translation>Избор на монети</translation>
</message>
<message>
<source>Quantity:</source>
<translation>Количество:</translation>
</message>
<message>
<source>Amount:</source>
<translation>Количество:</translation>
</message>
<message>
<source>Fee:</source>
<translation>Такса:</translation>
</message>
<message>
<source>Dust:</source>
<translation>прах:</translation>
</message>
<message>
<source>After Fee:</source>
<translation>След такса:</translation>
</message>
<message>
<source>Change:</source>
<translation>Промяна:</translation>
</message>
<message>
<source>List mode</source>
<translation>списък Режим</translation>
</message>
<message>
<source>Amount</source>
<translation>Количество</translation>
</message>
<message>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<source>Confirmations</source>
<translation>потвърждения</translation>
</message>
<message>
<source>Confirmed</source>
<translation>Потвърдено</translation>
</message>
<message>
<source>Copy address</source>
<translation>Копирайте адреса</translation>
</message>
<message>
<source>Copy label</source>
<translation>Копиране на етикета</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Копиране на сумата</translation>
</message>
<message>
<source>Copy transaction ID</source>
<translation>Копиране на ID на транзакцията</translation>
</message>
<message>
<source>Lock unspent</source>
<translation>Заключи неусвоени</translation>
</message>
<message>
<source>Unlock unspent</source>
<translation>Отключете неизползваните</translation>
</message>
<message>
<source>Copy quantity</source>
<translation>Копиране на количеството</translation>
</message>
<message>
<source>Copy fee</source>
<translation>Копирай такса</translation>
</message>
<message>
<source>Copy after fee</source>
<translation>Копирайте след такса</translation>
</message>
<message>
<source>Copy change</source>
<translation>Промяна на копирането</translation>
</message>
<message>
<source>yes</source>
<translation>да</translation>
</message>
<message>
<source>no</source>
<translation>не</translation>
</message>
<message>
<source>(no label)</source>
<translation>(без етикет)</translation>
</message>
</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>Edit Address</source>
<translation>Редактирайте адреса</translation>
</message>
<message>
<source>New sending address</source>
<translation>Нов адрес за изпращане</translation>
</message>
<message>
<source>Edit receiving address</source>
<translation>Редактиране на получаващия адрес</translation>
</message>
<message>
<source>Edit sending address</source>
<translation>Редактиране на адрес за изпращане</translation>
</message>
<message>
<source>Could not unlock wallet.</source>
<translation>Не може да отключите портфейла.</translation>
</message>
</context>
<context>
<name>FreespaceChecker</name>
<message>
<source>name</source>
<translation>име</translation>
</message>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>version</source>
<translation>версия</translation>
</message>
</context>
<context>
<name>Intro</name>
<message>
<source>Welcome</source>
<translation>Добре дошли</translation>
</message>
<message>
<source>Error</source>
<translation>грешка</translation>
</message>
</context>
<context>
<name>ModalOverlay</name>
<message>
<source>Form</source>
<translation>форма</translation>
</message>
<message>
<source>Progress</source>
<translation>прогрес</translation>
</message>
<message>
<source>calculating...</source>
<translation>изчисляване</translation>
</message>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
<message>
<source>Options</source>
<translation>Настроики</translation>
</message>
<message>
<source>Open Configuration File</source>
<translation>Отворете конфигурационния файл</translation>
</message>
<message>
<source>Expert</source>
<translation>Експерт</translation>
</message>
<message>
<source>none</source>
<translation>нищо</translation>
</message>
<message>
<source>Confirm options reset</source>
<translation>Потвърдете опциите за нулиране</translation>
</message>
<message>
<source>Configuration options</source>
<translation>Опции за конфигуриране</translation>
</message>
<message>
<source>Error</source>
<translation>грешка</translation>
</message>
</context>
<context>
<name>OverviewPage</name>
<message>
<source>Form</source>
<translation>форма</translation>
</message>
<message>
<source>Watch-only:</source>
<translation>Гледайте само:</translation>
</message>
<message>
<source>Available:</source>
<translation>На разположение:</translation>
</message>
<message>
<source>Your current spendable balance</source>
<translation>Текущото Ви разходоносно салдо</translation>
</message>
<message>
<source>Pending:</source>
<translation>В очакване на:</translation>
</message>
<message>
<source>Immature:</source>
<translation>Незрялото:</translation>
</message>
<message>
<source>Balances</source>
<translation>баланс</translation>
</message>
<message>
<source>Total:</source>
<translation>Обща сума:</translation>
</message>
<message>
<source>Your current total balance</source>
<translation>Текущото Ви общо салдо</translation>
</message>
<message>
<source>Recent transactions</source>
<translation>Последни транзакции</translation>
</message>
</context>
<context>
<name>PaymentServer</name>
<message>
<source>Payment request expired.</source>
<translation>Заявката за плащане е изтекла.</translation>
</message>
<message>
<source>Invalid payment request.</source>
<translation>Невалидна заявка за плащане.</translation>
</message>
</context>
<context>
<name>PeerTableModel</name>
<message>
<source>User Agent</source>
<translation>Потребителски агент</translation>
</message>
<message>
<source>Ping</source>
<translation>пинг</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<source>Amount</source>
<translation>Количество</translation>
</message>
<message>
<source>None</source>
<translation>нито един</translation>
</message>
<message>
<source>unknown</source>
<translation>неизвестен</translation>
</message>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
<message>
<source>Client version</source>
<translation>Клиентска версия</translation>
</message>
<message>
<source>Debug window</source>
<translation>Прозорец за отстраняване на грешки</translation>
</message>
<message>
<source>General</source>
<translation>Общ</translation>
</message>
<message>
<source>Startup time</source>
<translation>Време за стартиране</translation>
</message>
<message>
<source>Network</source>
<translation>мрежа</translation>
</message>
<message>
<source>Name</source>
<translation>име</translation>
</message>
<message>
<source>User Agent</source>
<translation>Потребителски агент</translation>
</message>
</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>Copy label</source>
<translation>Копиране на етикета</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Копиране на сумата</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>Address</source>
<translation>Адрес</translation>
</message>
<message>
<source>Amount</source>
<translation>Количество</translation>
</message>
<message>
<source>Label</source>
<translation>Етикет</translation>
</message>
<message>
<source>Wallet</source>
<translation>портфейл</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<source>Label</source>
<translation>Етикет</translation>
</message>
<message>
<source>(no label)</source>
<translation>(без етикет)</translation>
</message>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Quantity:</source>
<translation>Количество:</translation>
</message>
<message>
<source>Amount:</source>
<translation>Количество:</translation>
</message>
<message>
<source>Fee:</source>
<translation>Такса:</translation>
</message>
<message>
<source>After Fee:</source>
<translation>След такса:</translation>
</message>
<message>
<source>Change:</source>
<translation>Промяна:</translation>
</message>
<message>
<source>Dust:</source>
<translation>прах:</translation>
</message>
<message>
<source>Copy quantity</source>
<translation>Копиране на количеството</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Копиране на сумата</translation>
</message>
<message>
<source>Copy fee</source>
<translation>Копирай такса</translation>
</message>
<message>
<source>Copy after fee</source>
<translation>Копирайте след такса</translation>
</message>
<message>
<source>Copy change</source>
<translation>Промяна на копирането</translation>
</message>
<message>
<source>Payment request expired.</source>
<translation>Заявката за плащане е изтекла.</translation>
</message>
<message>
<source>(no label)</source>
<translation>(без етикет)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
<message>
<source>Status</source>
<translation>Статус</translation>
</message>
<message>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<source>Source</source>
<translation>източник</translation>
</message>
<message>
<source>Generated</source>
<translation>Генериран</translation>
</message>
<message>
<source>From</source>
<translation>от</translation>
</message>
<message>
<source>unknown</source>
<translation>неизвестен</translation>
</message>
<message>
<source>To</source>
<translation>към</translation>
</message>
<message>
<source>own address</source>
<translation>собствен адрес</translation>
</message>
<message>
<source>Credit</source>
<translation>кредит</translation>
</message>
<message>
<source>Amount</source>
<translation>Количество</translation>
</message>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<source>Label</source>
<translation>Етикет</translation>
</message>
<message>
<source>(no label)</source>
<translation>(без етикет)</translation>
</message>
</context>
<context>
<name>TransactionView</name>
<message>
<source>Copy address</source>
<translation>Копирайте адреса</translation>
</message>
<message>
<source>Copy label</source>
<translation>Копиране на етикета</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Копиране на сумата</translation>
</message>
<message>
<source>Copy transaction ID</source>
<translation>Копиране на ID на транзакцията</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Comma separated file (*.csv)</translation>
</message>
<message>
<source>Confirmed</source>
<translation>Потвърдено</translation>
</message>
<message>
<source>Date</source>
<translation>Дата</translation>
</message>
<message>
<source>Label</source>
<translation>Етикет</translation>
</message>
<message>
<source>Address</source>
<translation>Адрес</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Изнасянето се провали</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
<translation>Изнеси</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Изнеси данните в избрания раздел към файл</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
<message>
<source>Vexta Core</source>
<translation>Биткойн ядро</translation>
</message>
<message>
<source>Information</source>
<translation>Информация</translation>
</message>
<message>
<source>Warning</source>
<translation>Внимание</translation>
</message>
<message>
<source>Error</source>
<translation>грешка</translation>
</message>
</context>
</TS>

3743
src/qt/locale/digibyte_bn.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,277 @@
<TS language="bs" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Desni klik za uređivanje adrese ili oznake</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Napravite novu adresu</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;Nova</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Kopirajte trenutno odabranu adresu u sistemski meduspremnik</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Kopirajte</translation>
</message>
<message>
<source>C&amp;lose</source>
<translation>&amp;Zatvorite</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Izbrišite trenutno odabranu odresu sa liste</translation>
</message>
<message>
<source>Enter address or label to search</source>
<translation>Unesite adresu ili oznaku za pretragu</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Izvezite podatke trenutne kartice u datoteku</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>&amp;Izvezite</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>Iz&amp;brišite</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>Izaberite adresu na koju ćete poslati novac</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>Izaberite adresu na koju ćete primiti novac</translation>
</message>
<message>
<source>C&amp;hoose</source>
<translation>&amp;Izaberite</translation>
</message>
<message>
<source>Sending addresses</source>
<translation>Adrese pošiljalaca</translation>
</message>
<message>
<source>Receiving addresses</source>
<translation>Adrese primalaca</translation>
</message>
<message>
<source>These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
<translation>Ovo su vaše Vexta adrese za slanje novca. Uvijek provjerite iznos i adresu primaoca prije slanja novca.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopirajte adresu</translation>
</message>
<message>
<source>Copy &amp;Label</source>
<translation>Kopirajte &amp;ozanku</translation>
</message>
<message>
<source>&amp;Edit</source>
<translation>&amp;Uredite</translation>
</message>
<message>
<source>Export Address List</source>
<translation>Izvezite listu adresa</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Datoteka podataka odvojenih zarezima (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Izvoz neuspješan</translation>
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
<translation>Došlo je do greške kod spašavanja liste adresa na %1. Molimo pokušajte ponovo.</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
</context>
<context>
<name>AskPassphraseDialog</name>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
</context>
<context>
<name>CoinControlDialog</name>
</context>
<context>
<name>CreateWalletActivity</name>
</context>
<context>
<name>CreateWalletDialog</name>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
</context>
<context>
<name>Intro</name>
<message>
<source>Vexta</source>
<translation>Vexta</translation>
</message>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OpenWalletActivity</name>
</context>
<context>
<name>OptionsDialog</name>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PSBTOperationsDialog</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
</context>
<context>
<name>ReceiveCoinsDialog</name>
</context>
<context>
<name>ReceiveRequestDialog</name>
</context>
<context>
<name>RecentRequestsTableModel</name>
</context>
<context>
<name>SendCoinsDialog</name>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
<source>Alt+P</source>
<translation>Alt+P</translation>
</message>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
<source>Alt+P</source>
<translation>Alt+P</translation>
</message>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
</context>
<context>
<name>TransactionView</name>
<message>
<source>All</source>
<translation>Sve</translation>
</message>
<message>
<source>Today</source>
<translation>Danas</translation>
</message>
<message>
<source>This month</source>
<translation>Ovaj mjesec</translation>
</message>
<message>
<source>Last month</source>
<translation>Prošli mjesec</translation>
</message>
<message>
<source>This year</source>
<translation>Ove godine</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Datoteka podataka odvojenih zarezima (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Izvoz neuspješan</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletController</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
<translation>&amp;Izvezite</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Izvezite podatke trenutne kartice u datoteku</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
</context>
</TS>

3834
src/qt/locale/digibyte_ca.ts Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

3756
src/qt/locale/digibyte_cs.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,679 @@
<TS language="cs_CZ" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Pravým klikem editujte adresu nebo popisek</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Vytvořit novou adresu</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;Nový</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Kopírovat aktuálně vybrané adresy do schránky</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Kopírovat</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Odstraní aktuálně vybrané adresy ze seznamu</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Exportovat aktuální pohled do souboru</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>&amp;Exportovat</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>&amp;Odstranit</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Textový soubor oddělený středníkem (*.csv)</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
<translation>Popis</translation>
</message>
<message>
<source>Address</source>
<translation>Adresa</translation>
</message>
<message>
<source>(no label)</source>
<translation>(bez popisu)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Enter passphrase</source>
<translation>Zadej heslo</translation>
</message>
<message>
<source>New passphrase</source>
<translation>Nové heslo</translation>
</message>
<message>
<source>Repeat new passphrase</source>
<translation>Zopakujte nové heslo</translation>
</message>
<message>
<source>Encrypt wallet</source>
<translation>Zašifrovat peněženku</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
<translation>Tato operace vyžaduje heslo k odemknutí peněženky.</translation>
</message>
<message>
<source>Unlock wallet</source>
<translation>Odemknout peněženku</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
<translation>Tato operace vyžaduje heslo k dešifrování peněženky.</translation>
</message>
<message>
<source>Decrypt wallet</source>
<translation>Dešifrovat peněženku</translation>
</message>
<message>
<source>Change passphrase</source>
<translation>Změnit heslo</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
<translation>Potvrďte zašifrování peněženky</translation>
</message>
<message>
<source>Wallet encrypted</source>
<translation>Peněženka zašifrována</translation>
</message>
<message>
<source>Wallet encryption failed</source>
<translation>Zašifrování peněženky selhalo</translation>
</message>
<message>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<translation>Šifrování peněženky selhalo vinou vnitřní chyby. Vaše peněženka nebyla zašifrována.</translation>
</message>
<message>
<source>The supplied passphrases do not match.</source>
<translation>Zadaná hesla nejsou shodná.</translation>
</message>
<message>
<source>Wallet unlock failed</source>
<translation>Odemčení peněženky selhalo</translation>
</message>
<message>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
<translation>Heslo zadané k dešifrování peněženky nebylo správné</translation>
</message>
<message>
<source>Wallet decryption failed</source>
<translation>Deěifrování peněženky selhalo</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
<message>
<source>Synchronizing with network...</source>
<translation>Synchronizuji se sítí...</translation>
</message>
<message>
<source>&amp;Overview</source>
<translation>&amp;Přehled</translation>
</message>
<message>
<source>Show general overview of wallet</source>
<translation>Zobrazit základní přehled o peněžence</translation>
</message>
<message>
<source>&amp;Transactions</source>
<translation>&amp;Transakce</translation>
</message>
<message>
<source>Browse transaction history</source>
<translation>Procházení historií transakcí</translation>
</message>
<message>
<source>Quit application</source>
<translation>Ukončit aplikaci</translation>
</message>
<message>
<source>&amp;Options...</source>
<translation>&amp;Možnosti...</translation>
</message>
<message>
<source>Change the passphrase used for wallet encryption</source>
<translation>Změnit heslo k šifrování peněženky</translation>
</message>
<message>
<source>Vexta</source>
<translation>Vexta</translation>
</message>
<message>
<source>&amp;File</source>
<translation>&amp;Soubor</translation>
</message>
<message>
<source>&amp;Settings</source>
<translation>&amp;Nastavení</translation>
</message>
<message>
<source>&amp;Help</source>
<translation>Nápo&amp;věda</translation>
</message>
<message>
<source>Tabs toolbar</source>
<translation>Panely</translation>
</message>
<message>
<source>Up to date</source>
<translation>Aktuální</translation>
</message>
<message>
<source>Catching up...</source>
<translation>Zachytávám...</translation>
</message>
<message>
<source>Sent transaction</source>
<translation>Odeslané transakce</translation>
</message>
<message>
<source>Incoming transaction</source>
<translation>Příchozí transakce</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;unlocked&lt;/b&gt;</source>
<translation>Peněženka je &lt;b&gt;zašifrována&lt;/b&gt; a momentálně &lt;b&gt;odemčená&lt;/b&gt;</translation>
</message>
<message>
<source>Wallet is &lt;b&gt;encrypted&lt;/b&gt; and currently &lt;b&gt;locked&lt;/b&gt;</source>
<translation>Peněženka je &lt;b&gt;zašifrována&lt;/b&gt; a momentálně &lt;b&gt;uzamčená&lt;/b&gt;</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>Amount:</source>
<translation>Množství:</translation>
</message>
<message>
<source>Amount</source>
<translation>Množství</translation>
</message>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Confirmed</source>
<translation>Potvrzeno</translation>
</message>
<message>
<source>Copy address</source>
<translation>Kopírovat sdresu</translation>
</message>
<message>
<source>Copy label</source>
<translation>Kopírovat popis</translation>
</message>
<message>
<source>(no label)</source>
<translation>(bez popisu)</translation>
</message>
</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>Edit Address</source>
<translation>Upravit adresu</translation>
</message>
<message>
<source>&amp;Label</source>
<translation>&amp;Popisek</translation>
</message>
<message>
<source>&amp;Address</source>
<translation>&amp;Adresa</translation>
</message>
<message>
<source>New receiving address</source>
<translation>Nová adresa pro příjem</translation>
</message>
<message>
<source>New sending address</source>
<translation>Nová adresa k odeslání</translation>
</message>
<message>
<source>Edit receiving address</source>
<translation>Upravit adresu pro příjem</translation>
</message>
<message>
<source>Edit sending address</source>
<translation>Upravit adresu k odeslání</translation>
</message>
<message>
<source>The entered address "%1" is already in the address book.</source>
<translation>Zadaná adresa "%1" se již v seznamu adres nachází.</translation>
</message>
<message>
<source>Could not unlock wallet.</source>
<translation>Nemohu odemknout peněženku</translation>
</message>
<message>
<source>New key generation failed.</source>
<translation>Generování nového klíče selhalo.</translation>
</message>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>Usage:</source>
<translation>Použití:</translation>
</message>
</context>
<context>
<name>Intro</name>
<message>
<source>Vexta</source>
<translation>Vexta</translation>
</message>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
<message>
<source>Options</source>
<translation>Možnosti</translation>
</message>
<message>
<source>Map port using &amp;UPnP</source>
<translation>Mapovat port pomocí &amp;UPnP</translation>
</message>
<message>
<source>&amp;Minimize to the tray instead of the taskbar</source>
<translation>&amp;Minimalizovat do systémové lišty (tray) namísto do hlavního panelu</translation>
</message>
<message>
<source>M&amp;inimize on close</source>
<translation>M&amp;inimalizovat při zavření</translation>
</message>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
<message>
<source>Amount</source>
<translation>Množství</translation>
</message>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
<message>
<source>Name</source>
<translation>Jméno</translation>
</message>
</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>&amp;Label:</source>
<translation>&amp;Popisek:</translation>
</message>
<message>
<source>&amp;Message:</source>
<translation>Zpráva:</translation>
</message>
<message>
<source>Copy label</source>
<translation>Kopírovat popis</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>Address</source>
<translation>Adresa</translation>
</message>
<message>
<source>Amount</source>
<translation>Množství</translation>
</message>
<message>
<source>Label</source>
<translation>Popis</translation>
</message>
<message>
<source>Message</source>
<translation>Zpráva</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Label</source>
<translation>Popis</translation>
</message>
<message>
<source>Message</source>
<translation>Zpráva</translation>
</message>
<message>
<source>(no label)</source>
<translation>(bez popisu)</translation>
</message>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Amount:</source>
<translation>Množství:</translation>
</message>
<message>
<source>Balance:</source>
<translation>Zůstatek:</translation>
</message>
<message>
<source>The amount to pay must be larger than 0.</source>
<translation>Částka k zaplacení musí být větší než 0.</translation>
</message>
<message>
<source>(no label)</source>
<translation>(bez popisu)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>&amp;Label:</source>
<translation>&amp;Popisek:</translation>
</message>
<message>
<source>Message:</source>
<translation>Zpráva:</translation>
</message>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
<message>
<source>[testnet]</source>
<translation>[testnet]</translation>
</message>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
<message>
<source>%1/unconfirmed</source>
<translation>%1 potvrzeno</translation>
</message>
<message>
<source>%1 confirmations</source>
<translation>%1 potvrzení</translation>
</message>
<message>
<source>Status</source>
<translation>Stav</translation>
</message>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Message</source>
<translation>Zpráva</translation>
</message>
<message>
<source>Transaction</source>
<translation>Transakce</translation>
</message>
<message>
<source>Amount</source>
<translation>Množství</translation>
</message>
</context>
<context>
<name>TransactionDescDialog</name>
<message>
<source>This pane shows a detailed description of the transaction</source>
<translation>Toto podokno zobrazuje detailní popis transakce</translation>
</message>
</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Type</source>
<translation>Typ</translation>
</message>
<message>
<source>Label</source>
<translation>Popis</translation>
</message>
<message>
<source>Confirmed (%1 confirmations)</source>
<translation>Potvrzeno (%1 potvrzení)</translation>
</message>
<message>
<source>This block was not received by any other nodes and will probably not be accepted!</source>
<translation>Tento blok nebyl přijat žádným dalším uzlem a pravděpodobně nebude akceptován!</translation>
</message>
<message>
<source>Received with</source>
<translation>Přijato s</translation>
</message>
<message>
<source>Sent to</source>
<translation>Odesláno na</translation>
</message>
<message>
<source>Payment to yourself</source>
<translation>Platba sobě samému</translation>
</message>
<message>
<source>Mined</source>
<translation>Vytěženo</translation>
</message>
<message>
<source>(no label)</source>
<translation>(bez popisu)</translation>
</message>
<message>
<source>Type of transaction.</source>
<translation>Typ transakce.</translation>
</message>
</context>
<context>
<name>TransactionView</name>
<message>
<source>All</source>
<translation>Vše</translation>
</message>
<message>
<source>Today</source>
<translation>Dnes</translation>
</message>
<message>
<source>This week</source>
<translation>Tento týden</translation>
</message>
<message>
<source>This month</source>
<translation>Tento měsíc</translation>
</message>
<message>
<source>Last month</source>
<translation>Minulý měsíc</translation>
</message>
<message>
<source>This year</source>
<translation>Tento rok</translation>
</message>
<message>
<source>Range...</source>
<translation>Rozsah...</translation>
</message>
<message>
<source>Received with</source>
<translation>Přijato s</translation>
</message>
<message>
<source>Sent to</source>
<translation>Odesláno na</translation>
</message>
<message>
<source>To yourself</source>
<translation>Sobě samému</translation>
</message>
<message>
<source>Mined</source>
<translation>Vytěženo</translation>
</message>
<message>
<source>Other</source>
<translation>Ostatní</translation>
</message>
<message>
<source>Min amount</source>
<translation>Min. množství</translation>
</message>
<message>
<source>Copy address</source>
<translation>Kopírovat sdresu</translation>
</message>
<message>
<source>Copy label</source>
<translation>Kopírovat popis</translation>
</message>
<message>
<source>Edit label</source>
<translation>Upravit popis</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Textový soubor oddělený středníkem (*.csv)</translation>
</message>
<message>
<source>Confirmed</source>
<translation>Potvrzeno</translation>
</message>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Type</source>
<translation>Typ</translation>
</message>
<message>
<source>Label</source>
<translation>Popis</translation>
</message>
<message>
<source>Address</source>
<translation>Adresa</translation>
</message>
<message>
<source>ID</source>
<translation>ID</translation>
</message>
<message>
<source>Range:</source>
<translation>Rozsah:</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
<translation>&amp;Exportovat</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Exportovat aktuální pohled do souboru</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
<message>
<source>Options:</source>
<translation>Možnosti:</translation>
</message>
<message>
<source>Loading wallet...</source>
<translation>Načítání peněženky...</translation>
</message>
<message>
<source>Done loading</source>
<translation>Načítání dokončeno</translation>
</message>
</context>
</TS>

1005
src/qt/locale/digibyte_cy.ts Normal file

File diff suppressed because it is too large Load diff

3755
src/qt/locale/digibyte_da.ts Normal file

File diff suppressed because it is too large Load diff

4059
src/qt/locale/digibyte_de.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,929 @@
<TS language="de_DE" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Rechtsklick um Adresse oder Etikett zu bearbeiten</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Eine neue Adresse erstellen</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;Neu</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Aktive Adresse in die Zwischenablage kopieren</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Kopieren</translation>
</message>
<message>
<source>C&amp;lose</source>
<translation>Schliessen</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Ausgewählte Adresse aus der Liste löschen</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Daten des aktiven Fensters exportieren</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>&amp;Exportieren</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>&amp;Löschen</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>Wähle die Empfängeradresse</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>Wähle die Empfangsadresse</translation>
</message>
<message>
<source>C&amp;hoose</source>
<translation>Auswählen</translation>
</message>
<message>
<source>Sending addresses</source>
<translation>Sendeadressen</translation>
</message>
<message>
<source>Receiving addresses</source>
<translation>Empfangsadressen</translation>
</message>
<message>
<source>These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
<translation>Dies sind Ihre Vexta-Adressen zum Senden von Zahlungen. Überprüfen Sie immer die Menge und die Empfangsadresse, bevor Sie VTX senden.</translation>
</message>
<message>
<source>These are your Vexta addresses for receiving payments. It is recommended to use a new receiving address for each transaction.</source>
<translation>Dies sind Ihre Vexta-Adressen für den Empfang von Zahlungen. Es wird empfohlen, für jede Transaktion eine neue Empfangsadresse zu verwenden.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopiere Adresse</translation>
</message>
<message>
<source>Copy &amp;Label</source>
<translation>Kopiere &amp;Etikett</translation>
</message>
<message>
<source>&amp;Edit</source>
<translation>&amp;Bearbeiten</translation>
</message>
<message>
<source>Export Address List</source>
<translation>Export Adressliste</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Komma getrenntes Dokument (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Export fehlgeschlagen</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
<message>
<source>Label</source>
<translation>Etikett</translation>
</message>
<message>
<source>Address</source>
<translation>Adresse</translation>
</message>
<message>
<source>(no label)</source>
<translation>(kein Etikett)</translation>
</message>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
<translation>Passwort Dialog</translation>
</message>
<message>
<source>Enter passphrase</source>
<translation>Passwort eingeben</translation>
</message>
<message>
<source>New passphrase</source>
<translation>Neues Passwort</translation>
</message>
<message>
<source>Repeat new passphrase</source>
<translation>Neues Passwort wiederholen</translation>
</message>
<message>
<source>Enter the new passphrase to the wallet.&lt;br/&gt;Please use a passphrase of &lt;b&gt;ten or more random characters&lt;/b&gt;, or &lt;b&gt;eight or more words&lt;/b&gt;.</source>
<translation>Geben Sie das neue Passwort Ihrer digitalen Geldbörse ein.&lt;br/&gt;Verwenden Sie bitte ein Passwort bestehend aus &lt;b&gt;zehn oder mehr zufälligen Zeichen&lt;/b&gt;, oder aus &lt;b&gt;acht oder mehr Wörtern&lt;/b&gt;.</translation>
</message>
<message>
<source>Encrypt wallet</source>
<translation>Brieftasche verschlüsseln</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to unlock the wallet.</source>
<translation>Diese Aktion benötigt ihr Passwort zum entsperren der Wallet.</translation>
</message>
<message>
<source>Unlock wallet</source>
<translation>Brieftasche entschlüsseln</translation>
</message>
<message>
<source>This operation needs your wallet passphrase to decrypt the wallet.</source>
<translation>Diese Operation benötigt Ihr Wallet-Passwort zum entschlüsseln der Wallet.</translation>
</message>
<message>
<source>Decrypt wallet</source>
<translation>Brieftasche entschlüsseln</translation>
</message>
<message>
<source>Change passphrase</source>
<translation>Passwort ändern</translation>
</message>
<message>
<source>Enter the old passphrase and new passphrase to the wallet.</source>
<translation>Geben Sie das alte und neue Passwort Ihrer Wallet ein.</translation>
</message>
<message>
<source>Confirm wallet encryption</source>
<translation>Brieftaschenverschlüsselung bestätigen</translation>
</message>
<message>
<source>Warning: If you encrypt your wallet and lose your passphrase, you will &lt;b&gt;LOSE ALL OF YOUR VTX&lt;/b&gt;!</source>
<translation>Achtung: Wenn Sie Ihre Wallet verschlüsseln und das Passwort verlieren, gehen &lt;b&gt;alle Ihre VTX verloren&lt;/b&gt;!</translation>
</message>
<message>
<source>Are you sure you wish to encrypt your wallet?</source>
<translation>Möchten Sie Ihre Wallet wirklich verschlüsseln?</translation>
</message>
<message>
<source>Wallet encrypted</source>
<translation>Brieftasche verschlüsselt</translation>
</message>
<message>
<source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source>
<translation>WICHTIG: Alle früheren Sicherungen, die Sie von Ihrer Wallet-Datei gemacht haben, sollten durch die neu erzeugte, verschlüsselte Wallet-Datei ersetzt werden. Sobald Sie die neue, verschlüsselte Wallet verwenden, werden frühere Sicherungen der unverschlüsselten Wallet-Datei aus Sicherheitsgründen nutzlos.</translation>
</message>
<message>
<source>Wallet encryption failed</source>
<translation>Brieftaschenverschlüsselung fehlgeschlagen</translation>
</message>
<message>
<source>Wallet encryption failed due to an internal error. Your wallet was not encrypted.</source>
<translation>Die Wallet-Verschlüsselung ist aufgrund eines internen Fehlers fehlgeschlagen. Ihre Wallet wurde nicht verschlüsselt.</translation>
</message>
<message>
<source>The supplied passphrases do not match.</source>
<translation>Die eingegebenen Passwörter stimmen nicht überein.</translation>
</message>
<message>
<source>Wallet unlock failed</source>
<translation>Brieftasche öffnen fehlgeschlagen</translation>
</message>
<message>
<source>The passphrase entered for the wallet decryption was incorrect.</source>
<translation>Inkorrektes Passwort um die Brieftasche zu entschlüsseln</translation>
</message>
<message>
<source>Wallet decryption failed</source>
<translation>Brieftasche entschlüsseln fehlgeschlagen</translation>
</message>
<message>
<source>Wallet passphrase was successfully changed.</source>
<translation>Brieftaschen Passwort erfolgreich geändert</translation>
</message>
<message>
<source>Warning: The Caps Lock key is on!</source>
<translation>Achtung: Die Umschalttaste ist eingeschaltet!</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
<message>
<source>IP/Netmask</source>
<translation>IP/Netzmaske</translation>
</message>
<message>
<source>Banned Until</source>
<translation>Gesperrt bis</translation>
</message>
</context>
<context>
<name>DigiByteGUI</name>
<message>
<source>Synchronizing with network...</source>
<translation>Über das Netzwerk abgleichen</translation>
</message>
<message>
<source>&amp;Overview</source>
<translation>&amp;Übersicht</translation>
</message>
<message>
<source>Node</source>
<translation>Knoten</translation>
</message>
<message>
<source>Show general overview of wallet</source>
<translation>Generelle Übersicht der Brieftasche </translation>
</message>
<message>
<source>&amp;Transactions</source>
<translation>&amp;Transaktionen</translation>
</message>
<message>
<source>Browse transaction history</source>
<translation>Zeige Transaktionsverlauf</translation>
</message>
<message>
<source>E&amp;xit</source>
<translation>Beenden</translation>
</message>
<message>
<source>Quit application</source>
<translation>Programm beenden</translation>
</message>
<message>
<source>&amp;About %1</source>
<translation>&amp;Über %1</translation>
</message>
<message>
<source>Show information about %1</source>
<translation>Zeige Informationen zu %1</translation>
</message>
<message>
<source>About &amp;Qt</source>
<translation>Über &amp;Qt</translation>
</message>
<message>
<source>Show information about Qt</source>
<translation>Informationen über Qt anzeigen</translation>
</message>
<message>
<source>&amp;Options...</source>
<translation>&amp;Einstellungen...</translation>
</message>
<message>
<source>Modify configuration options for %1</source>
<translation>Ändern Sie die Konfigurationsoptionen für %1</translation>
</message>
<message>
<source>&amp;Encrypt Wallet...</source>
<translation>&amp;Brieftasche verschlüsseln...</translation>
</message>
<message>
<source>&amp;Backup Wallet...</source>
<translation>Sicherungskopie der Brieftasche...</translation>
</message>
<message>
<source>&amp;Change Passphrase...</source>
<translation>&amp;Passwort ändern...</translation>
</message>
<message>
<source>&amp;Sending addresses...</source>
<translation>&amp;Adressen senden...</translation>
</message>
<message>
<source>&amp;Receiving addresses...</source>
<translation>&amp;Adressen empfangen...</translation>
</message>
<message>
<source>Open &amp;URI...</source>
<translation>Öffne &amp;URI...</translation>
</message>
<message>
<source>Click to disable network activity.</source>
<translation>Klicken Sie, um die Netzwerkaktivität zu deaktivieren.</translation>
</message>
<message>
<source>Network activity disabled.</source>
<translation>Netzwerkaktivität wurde deaktiviert.</translation>
</message>
<message>
<source>Click to enable network activity again.</source>
<translation>Klicken Sie, um die Netzwerkaktivität wieder zu aktivieren.</translation>
</message>
<message>
<source>Syncing Headers (%1%)...</source>
<translation>Synchronisiere Header (%1%)...</translation>
</message>
<message>
<source>Reindexing blocks on disk...</source>
<translation>Reindexiere Blöcke auf dem Datenträger...</translation>
</message>
<message>
<source>Send coins to a Vexta address</source>
<translation>Coins an eine Vexta-Adresse senden</translation>
</message>
<message>
<source>Backup wallet to another location</source>
<translation>Wallet an einen anderen Ort sichern</translation>
</message>
<message>
<source>Change the passphrase used for wallet encryption</source>
<translation>Ändern Sie das Passwort, das für die Wallet-Verschlüsselung verwendet wird</translation>
</message>
<message>
<source>&amp;Debug window</source>
<translation>&amp;Debug Fenster</translation>
</message>
<message>
<source>Open debugging and diagnostic console</source>
<translation>Die Debugging- und Diagnosekonsole öffnen</translation>
</message>
<message>
<source>&amp;Verify message...</source>
<translation>Nachricht &amp;bestätigen</translation>
</message>
<message>
<source>Vexta</source>
<translation>Vexta</translation>
</message>
<message>
<source>Wallet</source>
<translation>Brieftasche</translation>
</message>
<message>
<source>&amp;Send</source>
<translation>&amp;Senden</translation>
</message>
<message>
<source>&amp;Receive</source>
<translation>&amp;Empfangen</translation>
</message>
<message>
<source>&amp;Show / Hide</source>
<translation>&amp;anzeigen / verstecken</translation>
</message>
<message>
<source>Show or hide the main Window</source>
<translation>Das Hauptfenster ein- oder ausblenden</translation>
</message>
<message>
<source>Encrypt the private keys that belong to your wallet</source>
<translation>Verschlüssele die Private Keys, die zu deiner Brieftasche gehören</translation>
</message>
<message>
<source>Sign messages with your Vexta addresses to prove you own them</source>
<translation>Nachrichten mit Ihrer Vexta-Adresse signieren, um zu beweisen, dass sie Ihnen gehören</translation>
</message>
<message>
<source>Verify messages to ensure they were signed with specified Vexta addresses</source>
<translation>Überprüfen Sie Nachrichten, um sicherzustellen, dass sie mit den angegebenen Vexta-Adressen signiert wurden</translation>
</message>
<message>
<source>&amp;File</source>
<translation>&amp;Datei</translation>
</message>
<message>
<source>&amp;Settings</source>
<translation>&amp;Einstellungen</translation>
</message>
<message>
<source>&amp;Help</source>
<translation>&amp;Hilfe</translation>
</message>
<message>
<source>Tabs toolbar</source>
<translation>Registerkarten-Symbolleiste</translation>
</message>
<message>
<source>Show the list of used sending addresses and labels</source>
<translation>Liste der benutzten Sendeadressen und -etiketten</translation>
</message>
<message>
<source>Show the list of used receiving addresses and labels</source>
<translation>Zeige Liste der benutzten Empfangsadressen und -etiketten</translation>
</message>
<message>
<source>Indexing blocks on disk...</source>
<translation>Indexiere Blöcke auf dem Datenträger...</translation>
</message>
<message>
<source>Processing blocks on disk...</source>
<translation>Verarbeite Blöcke auf dem Datenträger...</translation>
</message>
<message>
<source>%1 behind</source>
<translation>%1 im Rückstand</translation>
</message>
<message>
<source>Transactions after this will not yet be visible.</source>
<translation>Transaktionen danach sind noch nicht sichtbar.</translation>
</message>
<message>
<source>Error</source>
<translation>Fehler</translation>
</message>
<message>
<source>Warning</source>
<translation>Achtung</translation>
</message>
<message>
<source>Information</source>
<translation>Information</translation>
</message>
<message>
<source>Up to date</source>
<translation>Auf dem neuesten Stand</translation>
</message>
<message>
<source>Catching up...</source>
<translation>Aufholen...</translation>
</message>
<message>
<source>Date: %1
</source>
<translation>Datum: %1</translation>
</message>
<message>
<source>Amount: %1
</source>
<translation>Menge: %1</translation>
</message>
<message>
<source>Sent transaction</source>
<translation>Überweisung gesendet</translation>
</message>
<message>
<source>Incoming transaction</source>
<translation>Überweisung empfangen</translation>
</message>
<message>
<source>A fatal error occurred. Vexta can no longer continue safely and will quit.</source>
<translation>Ein schwerwiegender Fehler ist aufgetreten. Vexta kann nicht mehr sicher fortfahren und beendet sich.</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>Quantity:</source>
<translation>Anzahl:</translation>
</message>
<message>
<source>Bytes:</source>
<translation>Byte:</translation>
</message>
<message>
<source>Amount:</source>
<translation>Betrag:</translation>
</message>
<message>
<source>Fee:</source>
<translation>Gebühr:</translation>
</message>
<message>
<source>Dust:</source>
<translation>Dust:</translation>
</message>
<message>
<source>After Fee:</source>
<translation>Nach Gebühr:</translation>
</message>
<message>
<source>Amount</source>
<translation>Betrag</translation>
</message>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Confirmations</source>
<translation>Bestätigungen</translation>
</message>
<message>
<source>Confirmed</source>
<translation>Bestätigt</translation>
</message>
<message>
<source>Copy address</source>
<translation>Adresse kopieren</translation>
</message>
<message>
<source>Copy label</source>
<translation>Etikett kopieren</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Betrag kopieren</translation>
</message>
<message>
<source>Copy transaction ID</source>
<translation>Transaktions ID kopieren</translation>
</message>
<message>
<source>Copy quantity</source>
<translation>Menge kopieren</translation>
</message>
<message>
<source>yes</source>
<translation>Ja</translation>
</message>
<message>
<source>no</source>
<translation>Nein</translation>
</message>
<message>
<source>(no label)</source>
<translation>(kein Etikett)</translation>
</message>
</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>Edit Address</source>
<translation>Adresse bearbeiten</translation>
</message>
<message>
<source>&amp;Label</source>
<translation>&amp;Etikett</translation>
</message>
<message>
<source>&amp;Address</source>
<translation>&amp;Adresse</translation>
</message>
<message>
<source>New receiving address</source>
<translation>Neue Empfangsadresse</translation>
</message>
<message>
<source>New sending address</source>
<translation>Neue Sendeadresse</translation>
</message>
<message>
<source>Edit receiving address</source>
<translation>Empfangsadresse bearbeiten</translation>
</message>
<message>
<source>Edit sending address</source>
<translation>Sendeadresse bearbeiten</translation>
</message>
</context>
<context>
<name>FreespaceChecker</name>
<message>
<source>name</source>
<translation>Name</translation>
</message>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>version</source>
<translation>Version</translation>
</message>
</context>
<context>
<name>Intro</name>
<message>
<source>Welcome</source>
<translation>Willkommen</translation>
</message>
<message>
<source>Vexta</source>
<translation>Vexta</translation>
</message>
<message>
<source>Error</source>
<translation>Fehler</translation>
</message>
</context>
<context>
<name>ModalOverlay</name>
<message>
<source>Form</source>
<translation>Formular</translation>
</message>
<message>
<source>Unknown...</source>
<translation>Unbekannt...</translation>
</message>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
<message>
<source>Error</source>
<translation>Fehler</translation>
</message>
</context>
<context>
<name>OverviewPage</name>
<message>
<source>Form</source>
<translation>Formular</translation>
</message>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
<message>
<source>Ping</source>
<translation>Ping</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<source>Amount</source>
<translation>Betrag</translation>
</message>
<message>
<source>Enter a Vexta address (e.g. %1)</source>
<translation>Vexta Adresse eingeben (z.B. %1)</translation>
</message>
<message>
<source>N/A</source>
<translation>N/A</translation>
</message>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
<message>
<source>N/A</source>
<translation>N/A</translation>
</message>
</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>Copy label</source>
<translation>Etikett kopieren</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Betrag kopieren</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>Address</source>
<translation>Adresse</translation>
</message>
<message>
<source>Amount</source>
<translation>Betrag</translation>
</message>
<message>
<source>Label</source>
<translation>Etikett</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Label</source>
<translation>Etikett</translation>
</message>
<message>
<source>(no label)</source>
<translation>(kein Etikett)</translation>
</message>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Quantity:</source>
<translation>Anzahl:</translation>
</message>
<message>
<source>Bytes:</source>
<translation>Byte:</translation>
</message>
<message>
<source>Amount:</source>
<translation>Betrag:</translation>
</message>
<message>
<source>Fee:</source>
<translation>Gebühr:</translation>
</message>
<message>
<source>After Fee:</source>
<translation>Nach Gebühr:</translation>
</message>
<message>
<source>Dust:</source>
<translation>Dust:</translation>
</message>
<message>
<source>Copy quantity</source>
<translation>Menge kopieren</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Betrag kopieren</translation>
</message>
<message>
<source>(no label)</source>
<translation>(kein Etikett)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Amount</source>
<translation>Betrag</translation>
</message>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Label</source>
<translation>Etikett</translation>
</message>
<message>
<source>(no label)</source>
<translation>(kein Etikett)</translation>
</message>
</context>
<context>
<name>TransactionView</name>
<message>
<source>Copy address</source>
<translation>Adresse kopieren</translation>
</message>
<message>
<source>Copy label</source>
<translation>Etikett kopieren</translation>
</message>
<message>
<source>Copy amount</source>
<translation>Betrag kopieren</translation>
</message>
<message>
<source>Copy transaction ID</source>
<translation>Transaktions ID kopieren</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Komma getrenntes Dokument (*.csv)</translation>
</message>
<message>
<source>Confirmed</source>
<translation>Bestätigt</translation>
</message>
<message>
<source>Date</source>
<translation>Datum</translation>
</message>
<message>
<source>Label</source>
<translation>Etikett</translation>
</message>
<message>
<source>Address</source>
<translation>Adresse</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Export fehlgeschlagen</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
<translation>&amp;Exportieren</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Daten des aktiven Fensters exportieren</translation>
</message>
<message>
<source>Backup Failed</source>
<translation>Sicherung fehlgeschlagen</translation>
</message>
<message>
<source>Backup Successful</source>
<translation>Sicherung erfolgreich</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
<message>
<source>Options:</source>
<translation>Optionen:</translation>
</message>
<message>
<source>Information</source>
<translation>Information</translation>
</message>
<message>
<source>Warning</source>
<translation>Achtung</translation>
</message>
<message>
<source>Starting network threads...</source>
<translation>Starte Netzwerk-Threads...</translation>
</message>
<message>
<source>This is the minimum transaction fee you pay on every transaction.</source>
<translation>Das ist die Mindestgebühr für jede Transaktion</translation>
</message>
<message>
<source>This is the transaction fee you will pay if you send a transaction.</source>
<translation>Das ist die Gebühr für die Überweisung</translation>
</message>
<message>
<source>Transaction amounts must not be negative</source>
<translation>Überweisungsbetrag kann nicht negativ sein</translation>
</message>
<message>
<source>Transaction must have at least one recipient</source>
<translation>Überweisung muss mindestens einen Empfänger haben </translation>
</message>
<message>
<source>Insufficient funds</source>
<translation>Ungenügendes Guthaben</translation>
</message>
<message>
<source>Loading block index...</source>
<translation>Block Verzeichnis laden...</translation>
</message>
<message>
<source>Loading wallet...</source>
<translation>Brieftasche laden...</translation>
</message>
<message>
<source>Rescanning...</source>
<translation>Neuscannen...</translation>
</message>
<message>
<source>Done loading</source>
<translation>Laden abgeschlossen</translation>
</message>
<message>
<source>Error</source>
<translation>Fehler</translation>
</message>
</context>
</TS>

View file

@ -0,0 +1,227 @@
<TS language="el" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Create a new address</source>
<translation>Δημιουργία νέου λογαριασμού</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Αντιγραφή</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Enter passphrase</source>
<translation>Εισάγετε συνθηματικό</translation>
</message>
<message>
<source>New passphrase</source>
<translation>Νέο συνθηματικό</translation>
</message>
<message>
<source>Repeat new passphrase</source>
<translation>Επαναλάβετε νέο συνθηματικό</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
<message>
<source>Quit application</source>
<translation>Κλείσιμο εφαρμογής</translation>
</message>
<message>
<source>Wallet</source>
<translation>Πορτοφόλι</translation>
</message>
<message>
<source>Error</source>
<translation>Σφάλμα</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
<message>
<source>Date</source>
<translation>Ημερομηνία</translation>
</message>
</context>
<context>
<name>EditAddressDialog</name>
<message>
<source>&amp;Label</source>
<translation>Ετικέτα</translation>
</message>
<message>
<source>&amp;Address</source>
<translation>Διεύθυνση</translation>
</message>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>version</source>
<translation>έκδοση</translation>
</message>
</context>
<context>
<name>Intro</name>
<message>
<source>Welcome</source>
<translation>Καλώς Ήλθατε</translation>
</message>
<message>
<source>Error</source>
<translation>Σφάλμα</translation>
</message>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
<message>
<source>W&amp;allet</source>
<translation>Πορτοφόλι</translation>
</message>
<message>
<source>Error</source>
<translation>Σφάλμα</translation>
</message>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
<message>
<source>Services</source>
<translation>Υπηρεσίες</translation>
</message>
</context>
<context>
<name>ReceiveCoinsDialog</name>
<message>
<source>Remove</source>
<translation>Αφαίρεση</translation>
</message>
</context>
<context>
<name>ReceiveRequestDialog</name>
<message>
<source>Wallet</source>
<translation>Πορτοφόλι</translation>
</message>
</context>
<context>
<name>RecentRequestsTableModel</name>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>Insufficient funds!</source>
<translation>Κεφάλαια μη επαρκή</translation>
</message>
<message>
<source>Recommended:</source>
<translation>Συνίσταται:</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>Message:</source>
<translation>Μήνυμα:</translation>
</message>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
</context>
<context>
<name>TransactionView</name>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
</context>
<context>
<name>digibyte-core</name>
<message>
<source>Insufficient funds</source>
<translation>Κεφάλαια μη επαρκή</translation>
</message>
<message>
<source>Loading wallet...</source>
<translation>Φόρτωση πορτοφολιού...</translation>
</message>
<message>
<source>Rescanning...</source>
<translation>Επανάληψη σάρωσης</translation>
</message>
<message>
<source>Done loading</source>
<translation>Η φόρτωση ολοκληρώθηκε</translation>
</message>
<message>
<source>Error</source>
<translation>Σφάλμα</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load diff

5648
src/qt/locale/digibyte_en.ts Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,187 @@
<TS language="en_AU" version="2.1">
<context>
<name>AddressBookPage</name>
</context>
<context>
<name>AddressTableModel</name>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
<translation>Passphrase Dialogue</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
<message>
<source>Synchronizing with network...</source>
<translation>Synchronising with network...</translation>
</message>
</context>
<context>
<name>CoinControlDialog</name>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
<message>
<source>Start minimized</source>
<translation>Start minimised</translation>
</message>
</context>
<context>
<name>Intro</name>
</context>
<context>
<name>ModalOverlay</name>
<message>
<source>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 vexta network, as detailed below.</source>
<translation>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 synchronising with the vexta network, as detailed below.</translation>
</message>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
<message>
<source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source>
<translation>Minimise instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</translation>
</message>
<message>
<source>Show only a tray icon after minimizing the window.</source>
<translation>Show only a tray icon after minimising the window.</translation>
</message>
<message>
<source>&amp;Minimize to the tray instead of the taskbar</source>
<translation>&amp;Minimise to the tray instead of the taskbar</translation>
</message>
<message>
<source>M&amp;inimize on close</source>
<translation>M&amp;inimise on close</translation>
</message>
</context>
<context>
<name>OverviewPage</name>
<message>
<source>The displayed information may be out of date. Your wallet automatically synchronizes with the Vexta network after a connection is established, but this process has not completed yet.</source>
<translation>The displayed information may be out of date. Your wallet automatically synchronises with the Vexta network after a connection is established, but this process has not completed yet.</translation>
</message>
</context>
<context>
<name>PaymentServer</name>
<message>
<source>Payment request is not initialized.</source>
<translation>Payment request is not initialised.</translation>
</message>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
</context>
<context>
<name>ReceiveCoinsDialog</name>
</context>
<context>
<name>ReceiveRequestDialog</name>
</context>
<context>
<name>RecentRequestsTableModel</name>
</context>
<context>
<name>SendCoinsDialog</name>
<message>
<source>(Smart fee not initialized yet. This usually takes a few blocks...)</source>
<translation>(Smart fee not initialised yet. This usually takes a few blocks...)</translation>
</message>
</context>
<context>
<name>SendCoinsEntry</name>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
</context>
<context>
<name>TransactionView</name>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
</context>
<context>
<name>digibyte-core</name>
<message>
<source>Error initializing block database</source>
<translation>Error initialising block database</translation>
</message>
<message>
<source>Error initializing wallet database environment %s!</source>
<translation>Error initialising wallet database environment %s!</translation>
</message>
<message>
<source>Failed to rescan the wallet during initialization</source>
<translation>Failed to rescan the wallet during initialisation</translation>
</message>
<message>
<source>Initialization sanity check failed. %s is shutting down.</source>
<translation>Initialisation sanity check failed. %s is shutting down.</translation>
</message>
<message>
<source>Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)</source>
<translation>Randomise credentials for every proxy connection. This enables Tor stream isolation (default: %u)</translation>
</message>
<message>
<source>Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)</source>
<translation>Sets the serialisation of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)</translation>
</message>
</context>
</TS>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,183 @@
<TS language="en_US" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Right-click to edit address or label</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Create a new address</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;New</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Copy the currently selected address to the system clipboard</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Copy</translation>
</message>
<message>
<source>C&amp;lose</source>
<translation>C&amp;lose</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Delete the currently selected address from the list</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Export the data in the current tab to a file</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>&amp;Export</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>&amp;Delete</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>Choose the address to send coins to</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>Choose the address to receive coins with</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
</context>
<context>
<name>AskPassphraseDialog</name>
<message>
<source>Passphrase Dialog</source>
<translation>Passphrase Dialog</translation>
</message>
<message>
<source>Enter passphrase</source>
<translation>Enter passphrase</translation>
</message>
<message>
<source>New passphrase</source>
<translation>New passphrase</translation>
</message>
<message>
<source>Repeat new passphrase</source>
<translation>Repeat new passphrase</translation>
</message>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
</context>
<context>
<name>CoinControlDialog</name>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
</context>
<context>
<name>Intro</name>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
</context>
<context>
<name>ReceiveCoinsDialog</name>
</context>
<context>
<name>ReceiveRequestDialog</name>
</context>
<context>
<name>RecentRequestsTableModel</name>
</context>
<context>
<name>SendCoinsDialog</name>
</context>
<context>
<name>SendCoinsEntry</name>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
</context>
<context>
<name>TransactionView</name>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
</context>
<context>
<name>digibyte-core</name>
</context>
</TS>

2019
src/qt/locale/digibyte_eo.ts Normal file

File diff suppressed because it is too large Load diff

4005
src/qt/locale/digibyte_es.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,167 @@
<TS language="es_419" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Haga clic para editar la dirección o etiqueta</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Crear una nueva dirección</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;New</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Copia la dirección seleccionada al portapapeles del sistema</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Borrar la dirección que esta seleccionada en la lista</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Exportar los datos de la actual tabla hacia un archivo</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>Seleccione la dirección a la que enviará las monedas</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>Seleccione la dirección con la que recibirá las monedas</translation>
</message>
<message>
<source>Sending addresses</source>
<translation>Enviando direcciones</translation>
</message>
<message>
<source>Receiving addresses</source>
<translation>Recibiendo direcciones</translation>
</message>
<message>
<source>These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
<translation>Estas son sus direcciones de Vexta para enviar sus pagos. Siempre revise el monto y la dirección recibida antes de enviar monedas.</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
</context>
<context>
<name>AskPassphraseDialog</name>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
</context>
<context>
<name>CoinControlDialog</name>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
</context>
<context>
<name>Intro</name>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OptionsDialog</name>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QObject::QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
</context>
<context>
<name>ReceiveCoinsDialog</name>
</context>
<context>
<name>ReceiveRequestDialog</name>
</context>
<context>
<name>RecentRequestsTableModel</name>
</context>
<context>
<name>SendCoinsDialog</name>
</context>
<context>
<name>SendCoinsEntry</name>
</context>
<context>
<name>SendConfirmationDialog</name>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
</context>
<context>
<name>SplashScreen</name>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
</context>
<context>
<name>TransactionView</name>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Exportar los datos de la actual tabla hacia un archivo</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
</context>
</TS>

View file

@ -0,0 +1,277 @@
<TS language="bs" version="2.1">
<context>
<name>AddressBookPage</name>
<message>
<source>Right-click to edit address or label</source>
<translation>Desni klik za uređivanje adrese ili oznake</translation>
</message>
<message>
<source>Create a new address</source>
<translation>Napravite novu adresu</translation>
</message>
<message>
<source>&amp;New</source>
<translation>&amp;Nova</translation>
</message>
<message>
<source>Copy the currently selected address to the system clipboard</source>
<translation>Kopirajte trenutno odabranu adresu u sistemski meduspremnik</translation>
</message>
<message>
<source>&amp;Copy</source>
<translation>&amp;Kopirajte</translation>
</message>
<message>
<source>C&amp;lose</source>
<translation>&amp;Zatvorite</translation>
</message>
<message>
<source>Delete the currently selected address from the list</source>
<translation>Izbrišite trenutno odabranu odresu sa liste</translation>
</message>
<message>
<source>Enter address or label to search</source>
<translation>Unesite adresu ili oznaku za pretragu</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Izvezite podatke trenutne kartice u datoteku</translation>
</message>
<message>
<source>&amp;Export</source>
<translation>&amp;Izvezite</translation>
</message>
<message>
<source>&amp;Delete</source>
<translation>Iz&amp;brišite</translation>
</message>
<message>
<source>Choose the address to send coins to</source>
<translation>Izaberite adresu na koju ćete poslati novac</translation>
</message>
<message>
<source>Choose the address to receive coins with</source>
<translation>Izaberite adresu na koju ćete primiti novac</translation>
</message>
<message>
<source>C&amp;hoose</source>
<translation>&amp;Izaberite</translation>
</message>
<message>
<source>Sending addresses</source>
<translation>Adrese pošiljalaca</translation>
</message>
<message>
<source>Receiving addresses</source>
<translation>Adrese primalaca</translation>
</message>
<message>
<source>These are your Vexta addresses for sending payments. Always check the amount and the receiving address before sending coins.</source>
<translation>Ovo su vaše Vexta adrese za slanje novca. Uvijek provjerite iznos i adresu primaoca prije slanja novca.</translation>
</message>
<message>
<source>&amp;Copy Address</source>
<translation>&amp;Kopirajte adresu</translation>
</message>
<message>
<source>Copy &amp;Label</source>
<translation>Kopirajte &amp;ozanku</translation>
</message>
<message>
<source>&amp;Edit</source>
<translation>&amp;Uredite</translation>
</message>
<message>
<source>Export Address List</source>
<translation>Izvezite listu adresa</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Datoteka podataka odvojenih zarezima (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Izvoz neuspješan</translation>
</message>
<message>
<source>There was an error trying to save the address list to %1. Please try again.</source>
<translation>Došlo je do greške kod spašavanja liste adresa na %1. Molimo pokušajte ponovo.</translation>
</message>
</context>
<context>
<name>AddressTableModel</name>
</context>
<context>
<name>AskPassphraseDialog</name>
</context>
<context>
<name>BanTableModel</name>
</context>
<context>
<name>DigiByteGUI</name>
</context>
<context>
<name>CoinControlDialog</name>
</context>
<context>
<name>CreateWalletActivity</name>
</context>
<context>
<name>CreateWalletDialog</name>
</context>
<context>
<name>EditAddressDialog</name>
</context>
<context>
<name>FreespaceChecker</name>
</context>
<context>
<name>HelpMessageDialog</name>
</context>
<context>
<name>Intro</name>
<message>
<source>Vexta</source>
<translation>Vexta</translation>
</message>
</context>
<context>
<name>ModalOverlay</name>
</context>
<context>
<name>OpenURIDialog</name>
</context>
<context>
<name>OpenWalletActivity</name>
</context>
<context>
<name>OptionsDialog</name>
</context>
<context>
<name>OverviewPage</name>
</context>
<context>
<name>PSBTOperationsDialog</name>
</context>
<context>
<name>PaymentServer</name>
</context>
<context>
<name>PeerTableModel</name>
</context>
<context>
<name>QObject</name>
</context>
<context>
<name>QRImageWidget</name>
</context>
<context>
<name>RPCConsole</name>
</context>
<context>
<name>ReceiveCoinsDialog</name>
</context>
<context>
<name>ReceiveRequestDialog</name>
</context>
<context>
<name>RecentRequestsTableModel</name>
</context>
<context>
<name>SendCoinsDialog</name>
</context>
<context>
<name>SendCoinsEntry</name>
<message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
<source>Alt+P</source>
<translation>Alt+P</translation>
</message>
</context>
<context>
<name>ShutdownWindow</name>
</context>
<context>
<name>SignVerifyMessageDialog</name>
<message>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
<source>Alt+P</source>
<translation>Alt+P</translation>
</message>
</context>
<context>
<name>TrafficGraphWidget</name>
</context>
<context>
<name>TransactionDesc</name>
</context>
<context>
<name>TransactionDescDialog</name>
</context>
<context>
<name>TransactionTableModel</name>
</context>
<context>
<name>TransactionView</name>
<message>
<source>All</source>
<translation>Sve</translation>
</message>
<message>
<source>Today</source>
<translation>Danas</translation>
</message>
<message>
<source>This month</source>
<translation>Ovaj mjesec</translation>
</message>
<message>
<source>Last month</source>
<translation>Prošli mjesec</translation>
</message>
<message>
<source>This year</source>
<translation>Ove godine</translation>
</message>
<message>
<source>Comma separated file (*.csv)</source>
<translation>Datoteka podataka odvojenih zarezima (*.csv)</translation>
</message>
<message>
<source>Exporting Failed</source>
<translation>Izvoz neuspješan</translation>
</message>
</context>
<context>
<name>UnitDisplayStatusBarControl</name>
</context>
<context>
<name>WalletController</name>
</context>
<context>
<name>WalletFrame</name>
</context>
<context>
<name>WalletModel</name>
</context>
<context>
<name>WalletView</name>
<message>
<source>&amp;Export</source>
<translation>&amp;Izvezite</translation>
</message>
<message>
<source>Export the data in the current tab to a file</source>
<translation>Izvezite podatke trenutne kartice u datoteku</translation>
</message>
</context>
<context>
<name>digibyte-core</name>
</context>
</TS>

Some files were not shown because too many files have changed in this diff Show more