commit 04bc8b699fe39b9432efae8f6d66ccf6efab9fad Author: cryptonotefoundation Date: Wed Apr 29 20:03:08 2015 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20a3894 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +/build +/tags diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..15636b1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,201 @@ +cmake_minimum_required(VERSION 2.8) + +include(CryptoNoteWallet.cmake) + +project(${CN_PROJECT_NAME}) + +execute_process(COMMAND git log -1 --pretty=format:%h +WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} +OUTPUT_VARIABLE GIT_REVISION) + +set(CRYPTONOTE_LIB cryptonote) + +include_directories(${CMAKE_BINARY_DIR} + src + cryptonote/external + cryptonote/include + cryptonote/src + cryptonote/contrib/epee/include) + +find_package(Qt5Gui REQUIRED) +find_package(Qt5Widgets REQUIRED) + +set(Boost_USE_STATIC_LIBS ON) +if(WIN32) + set(Boost_USE_STATIC_RUNTIME OFF) +else(WIN32) + set(Boost_USE_STATIC_RUNTIME ON) +endif(WIN32) + +find_package(Boost 1.55 REQUIRED COMPONENTS date_time filesystem program_options regex serialization system thread chrono) +if ((${Boost_MAJOR_VERSION} EQUAL 1) AND (${Boost_MINOR_VERSION} EQUAL 54)) + message(SEND_ERROR "Boost version 1.54 is unsupported, more details are available here http://goo.gl/RrCFmA") +endif () + +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +set(VERSION "") +configure_file("cryptonote/src/version.h.in" "version.h") +configure_file("src/CryptoNoteWalletConfig.h.in" "CryptoNoteWalletConfig.h") + +add_definitions(-DGIT_REVISION=\"${GIT_REVISION}\") + +set(CMAKE_AUTOMOC ON) + +set(CRYPTONOTE_SOURCES + cryptonote/contrib/epee/include/misc_log_ex.cpp + cryptonote/contrib/epee/include/misc_os_dependent.cpp + cryptonote/contrib/epee/include/string_tools.cpp + cryptonote/external/miniupnpc/connecthostport.c + cryptonote/external/miniupnpc/igd_desc_parse.c + cryptonote/external/miniupnpc/minisoap.c + cryptonote/external/miniupnpc/miniupnpc.c + cryptonote/external/miniupnpc/miniwget.c + cryptonote/external/miniupnpc/minixml.c + cryptonote/external/miniupnpc/portlistingparse.c + cryptonote/external/miniupnpc/receivedata.c + cryptonote/external/miniupnpc/upnpcommands.c + cryptonote/external/miniupnpc/upnpreplyparse.c + cryptonote/src/common/base58.cpp + cryptonote/src/common/command_line.cpp + cryptonote/src/common/util.cpp + cryptonote/src/crypto/blake256.c + cryptonote/src/crypto/chacha8.c + cryptonote/src/crypto/crypto-ops-data.c + cryptonote/src/crypto/crypto-ops.c + cryptonote/src/crypto/crypto.cpp + cryptonote/src/crypto/groestl.c + cryptonote/src/crypto/hash-extra-blake.c + cryptonote/src/crypto/hash-extra-groestl.c + cryptonote/src/crypto/hash-extra-jh.c + cryptonote/src/crypto/hash-extra-skein.c + cryptonote/src/crypto/hash.c + cryptonote/src/crypto/jh.c + cryptonote/src/crypto/keccak.c + cryptonote/src/crypto/oaes_lib.c + cryptonote/src/crypto/random.c + cryptonote/src/crypto/skein.c + cryptonote/src/crypto/slow-hash.c + cryptonote/src/crypto/slow-hash.cpp + cryptonote/src/crypto/tree-hash.c + cryptonote/src/cryptonote_core/BlockIndex.cpp + cryptonote/src/cryptonote_core/CoreConfig.cpp + cryptonote/src/cryptonote_core/Currency.cpp + cryptonote/src/cryptonote_core/MinerConfig.cpp + cryptonote/src/cryptonote_core/Transaction.cpp + cryptonote/src/cryptonote_core/account.cpp + cryptonote/src/cryptonote_core/blockchain_storage.cpp + cryptonote/src/cryptonote_core/checkpoints.cpp + cryptonote/src/cryptonote_core/cryptonote_basic_impl.cpp + cryptonote/src/cryptonote_core/cryptonote_core.cpp + cryptonote/src/cryptonote_core/cryptonote_format_utils.cpp + cryptonote/src/cryptonote_core/cryptonote_serialization.cpp + cryptonote/src/cryptonote_core/difficulty.cpp + cryptonote/src/cryptonote_core/miner.cpp + cryptonote/src/cryptonote_core/tx_pool.cpp + cryptonote/src/inprocess_node/InProcessNode.cpp + cryptonote/src/inprocess_node/InProcessNodeErrors.cpp + cryptonote/src/node_rpc_proxy/NodeErrors.cpp + cryptonote/src/node_rpc_proxy/NodeRpcProxy.cpp + cryptonote/src/p2p/NetNodeConfig.cpp + cryptonote/src/serialization/BinaryInputStreamSerializer.cpp + cryptonote/src/serialization/BinaryOutputStreamSerializer.cpp + cryptonote/src/transfers/BlockchainSynchronizer.cpp + cryptonote/src/transfers/SynchronizationState.cpp + cryptonote/src/transfers/TransfersConsumer.cpp + cryptonote/src/transfers/TransfersContainer.cpp + cryptonote/src/transfers/TransfersSubscription.cpp + cryptonote/src/transfers/TransfersSynchronizer.cpp + cryptonote/src/wallet/KeysStorage.cpp + cryptonote/src/wallet/Wallet.cpp + cryptonote/src/wallet/WalletAsyncContextCounter.cpp + cryptonote/src/wallet/WalletErrors.cpp + cryptonote/src/wallet/WalletSerializer.cpp + cryptonote/src/wallet/WalletSerialization.cpp + cryptonote/src/wallet/WalletTransactionSender.cpp + cryptonote/src/wallet/WalletUnconfirmedTransactions.cpp + cryptonote/src/wallet/WalletUserTransactionsCache.cpp + cryptonote/src/wallet/LegacyKeysImporter.cpp +) + +file(GLOB_RECURSE SOURCES src/*.cpp) +file(GLOB_RECURSE HEADERS src/*.h) +file(GLOB_RECURSE FORMS src/gui/ui/*.ui) + +set(QRC src/resources.qrc) + +qt5_wrap_ui(UIS ${FORMS}) +qt5_add_resources(RCC ${QRC}) + + +if (WIN32) + if (NOT MSVC) + message(FATAL_ERROR "Only MSVC is supported on this platform") + endif () + add_definitions(/D_CRT_SECURE_NO_WARNINGS /D_WIN32_WINNT=0x0600 /DSTATICLIB) + include_directories(cryptonote/src/platform/msc) + + set(PLATFORM_DIR Windows) + set(BUILD_PLATFORM WIN32) + set(BUILD_RESOURCES src/cryptonotewallet.rc) + set(QTMAIN Qt5::WinMain) + +elseif (UNIX) + set(CRYPTONOTE_SOURCES ${CRYPTONOTE_SOURCES} cryptonote/external/miniupnpc/minissdpc.c) + if (APPLE) + enable_language(ASM) + file(GLOB_RECURSE OBJC_SOURCES src/*.mm) + set(SOURCES ${SOURCES} ${OBJC_SOURCES}) + set(PLATFORM_DIR OSX) + set(MACOSX_BUNDLE_INFO_STRING "Cryptonote GUI wallet") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${VERSION_VERSION}.${VERSION_MINOR}.${VERSION_PATCH}") + set(MACOSX_BUNDLE_BUNDLE_NAME CryptonoteWallet) + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VERSION_VERSION}.${VERSION_MINOR}.${VERSION_PATCH}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "$${VERSION_VERSION}.${VERSION_MINOR}.${VERSION_PATCH}") + + find_package(Qt5PrintSupport REQUIRED) + + include_directories(/usr/include/malloc) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes -std=c++11 -stdlib=libc++") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -maes -D_DARWIN_C_SOURCE") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Cocoa -framework OpenGL -framework CoreFoundation -framework Carbon -framework IOKit -L/usr/lib") + + set(MACOSX_BUNDLE_ICON_FILE cryptonote.icns) + set(APPLICATION_ICON src/images/cryptonote.icns) + set_source_files_properties(${APPLICATION_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + + set(BUILD_PLATFORM MACOSX_BUNDLE) + set(BUILD_RESOURCES ${APPLICATION_ICON}) + + GET_TARGET_PROPERTY(QT_LIB_DIR "${Qt5Widgets_LIBRARIES}" LOCATION) + GET_FILENAME_COMPONENT(QT_LIB_DIR "${QT_LIB_DIR}" PATH) + else(APPLE) + set(PLATFORM_DIR Linux) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes -std=c++11") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes -std=c11") + endif (APPLE) +endif () + +include_directories(cryptonote/src/Platform/${PLATFORM_DIR}) +file(GLOB PLATFORM_SOURCES cryptonote/src/Platform/${PLATFORM_DIR}/System/*) +set(CRYPTONOTE_SOURCES ${CRYPTONOTE_SOURCES} ${PLATFORM_SOURCES}) + +add_library(${CRYPTONOTE_LIB} STATIC ${CRYPTONOTE_SOURCES}) +set_target_properties(${CRYPTONOTE_LIB} PROPERTIES COMPILE_DEFINITIONS _GNU_SOURCE) +target_link_libraries(${CRYPTONOTE_LIB} ${Boost_LIBRARIES}) + +add_executable(${PROJECT_NAME} ${BUILD_PLATFORM} ${BUILD_RESOURCES} ${SOURCES} ${HEADERS} ${UIS} ${RCC}) +set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_DEFINITIONS _GNU_SOURCE) +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${QTMAIN} ${CRYPTONOTE_LIB}) + +if (APPLE) + qt5_use_modules(${PROJECT_NAME} PrintSupport) +elseif (UNIX) + target_link_libraries(${PROJECT_NAME} -lpthread) +elseif (WIN32) + target_link_libraries(${PROJECT_NAME} Imm32 Iphlpapi Winmm) +endif (APPLE) + +qt5_use_modules(${PROJECT_NAME} Widgets Gui) diff --git a/CryptoNoteWallet.cmake b/CryptoNoteWallet.cmake new file mode 100644 index 0000000..4af8821 --- /dev/null +++ b/CryptoNoteWallet.cmake @@ -0,0 +1,4 @@ + +set(CN_PROJECT_NAME "") +set(CN_CURRENCY_DISPLAY_NAME "") +set(CN_CURRENCY_TICKER "") diff --git a/src/CommandLineParser.cpp b/src/CommandLineParser.cpp new file mode 100644 index 0000000..817e314 --- /dev/null +++ b/src/CommandLineParser.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include + +#include "CommandLineParser.h" +#include "CurrencyAdapter.h" + +namespace WalletGui { + +CommandLineParser::CommandLineParser() : QObject(), m_parser(), m_help_option(m_parser.addHelpOption()), + m_version_option(m_parser.addVersionOption()), + m_data_dir_option("data-dir", tr("Specify data directory"), tr("directory"), QString::fromStdString(tools::get_default_data_dir())) { + m_parser.setApplicationDescription(QString(tr("%1 wallet")).arg(CurrencyAdapter::instance().getCurrencyDisplayName())); + m_parser.addHelpOption(); + m_parser.addVersionOption(); + m_parser.addOption(m_data_dir_option); +} + +CommandLineParser::~CommandLineParser() { +} + +bool CommandLineParser::process() { +#ifdef Q_OS_WIN + return m_parser.parse(QCoreApplication::arguments()); +#else + m_parser.process(*QCoreApplication::instance()); + return true; +#endif +} + +bool CommandLineParser::hasHelpOption() const { + return m_parser.isSet(m_help_option); +} + +bool CommandLineParser::hasVersionOption() const { + return m_parser.isSet(m_version_option); +} + +QString CommandLineParser::errorText() const { + return m_parser.errorText(); +} + +QString CommandLineParser::helpText() const { + return m_parser.helpText(); +} + +QString CommandLineParser::getDataDir() const { + return m_parser.value(m_data_dir_option); +} + +} diff --git a/src/CommandLineParser.h b/src/CommandLineParser.h new file mode 100644 index 0000000..e6930e2 --- /dev/null +++ b/src/CommandLineParser.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +namespace WalletGui { + +class CommandLineParser : public QObject { + Q_OBJECT + Q_DISABLE_COPY(CommandLineParser) + +public: + CommandLineParser(); + ~CommandLineParser(); + + bool process(); + + bool hasHelpOption() const; + bool hasVersionOption() const; + QString errorText() const; + QString helpText() const; + QString getDataDir() const; +private: + QCommandLineParser m_parser; + QCommandLineOption m_help_option; + QCommandLineOption m_version_option; + QCommandLineOption m_data_dir_option; +}; + +} diff --git a/src/CryptoNote.cpp b/src/CryptoNote.cpp new file mode 100644 index 0000000..440fb7a --- /dev/null +++ b/src/CryptoNote.cpp @@ -0,0 +1,265 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "CryptoNote.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" +#include "node_rpc_proxy/NodeRpcProxy.h" +#include "cryptonote_core/CoreConfig.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "inprocess_node/InProcessNode.h" +#include "p2p/net_node.h" +#include "wallet/Wallet.h" + +namespace WalletGui { + +namespace { + +bool parsePaymentId(const std::string& payment_id_str, crypto::hash& payment_id) { + cryptonote::blobdata payment_id_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) { + return false; + } + + if (sizeof(crypto::hash) != payment_id_data.size()) { + return false; + } + + payment_id = *reinterpret_cast(payment_id_data.data()); + return true; +} + +std::string convertPaymentId(const std::string& paymentIdString) { + if (paymentIdString.empty()) { + return ""; + } + + crypto::hash paymentId; + if (!parsePaymentId(paymentIdString, paymentId)) { + std::stringstream errorStr; + errorStr << "Payment id has invalid format: \"" + paymentIdString + "\", expected 64-character string"; + throw std::runtime_error(errorStr.str()); + } + + std::vector extra; + std::string extra_nonce; + cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, paymentId); + if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + std::stringstream errorStr; + errorStr << "Something went wrong with payment_id. Please check its format: \"" + paymentIdString + "\", expected 64-character string"; + throw std::runtime_error(errorStr.str()); + } + + return std::string(extra.begin(), extra.end()); +} + +std::string extractPaymentId(const std::string& extra) { + std::vector extraFields; + std::vector extraVector; + std::copy(extra.begin(), extra.end(), std::back_inserter(extraVector)); + + if (!cryptonote::parse_tx_extra(extraVector, extraFields)) { + throw std::runtime_error("Can't parse extra"); + } + + std::string result; + cryptonote::tx_extra_nonce extra_nonce; + if (cryptonote::find_tx_extra_field_by_type(extraFields, extra_nonce)) { + crypto::hash paymentIdHash; + if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentIdHash)) { + unsigned char* buff = reinterpret_cast(&paymentIdHash); + for (size_t i = 0; i < sizeof(paymentIdHash); ++i) { + result.push_back("0123456789ABCDEF"[buff[i] >> 4]); + result.push_back("0123456789ABCDEF"[buff[i] & 15]); + } + } + } + + return result; +} + + +} + +Node::~Node() { +} + +class RpcNode : CryptoNote::INodeObserver, public Node { +public: + RpcNode(const cryptonote::Currency& currency, INodeCallback& callback, const std::string& nodeHost, unsigned short nodePort) : + m_callback(callback), + m_currency(currency), + m_node(nodeHost, nodePort) { + m_node.addObserver(this); + } + + ~RpcNode() override { + } + + void init(const std::function& callback) override { + m_node.init(callback); + } + + void deinit() override { + } + + std::string convertPaymentId(const std::string& paymentIdString) override { + return WalletGui::convertPaymentId(paymentIdString); + } + + std::string extractPaymentId(const std::string& extra) override { + return WalletGui::extractPaymentId(extra); + } + + uint64_t getLastKnownBlockHeight() const override { + return m_node.getLastKnownBlockHeight(); + } + + uint64_t getLastLocalBlockHeight() const override { + return m_node.getLastLocalBlockHeight(); + } + + uint64_t getLastLocalBlockTimestamp() const override { + return m_node.getLastLocalBlockTimestamp(); + } + + uint64_t getPeerCount() const override { + return m_node.getPeerCount(); + } + + CryptoNote::IWallet* createWallet() override { + return new CryptoNote::Wallet(m_currency, m_node); + } + +private: + INodeCallback& m_callback; + const cryptonote::Currency& m_currency; + cryptonote::NodeRpcProxy m_node; + + void peerCountUpdated(size_t count) { + m_callback.peerCountUpdated(*this, count); + } + + void localBlockchainUpdated(uint64_t height) { + m_callback.localBlockchainUpdated(*this, height); + } + + void lastKnownBlockHeightUpdated(uint64_t height) { + m_callback.lastKnownBlockHeightUpdated(*this, height); + } +}; + +class InprocessNode : CryptoNote::INodeObserver, public Node { +public: + InprocessNode(const cryptonote::Currency& currency, INodeCallback& callback, const cryptonote::CoreConfig& coreConfig) : + m_callback(callback), + m_currency(currency), + m_coreConfig(coreConfig), + m_core(m_currency, nullptr), + m_protocolHandler(m_core, nullptr), + m_nodeServer(m_protocolHandler), + m_node(m_core, m_protocolHandler) { + + m_core.set_cryptonote_protocol(&m_protocolHandler); + m_protocolHandler.set_p2p_endpoint(&m_nodeServer); + cryptonote::checkpoints checkpoints; + for (const cryptonote::CheckpointData& checkpoint : cryptonote::CHECKPOINTS) { + checkpoints.add_checkpoint(checkpoint.height, checkpoint.blockId); + } + + m_core.set_checkpoints(std::move(checkpoints)); + } + + ~InprocessNode() override { + + } + + void init(const std::function& callback) override { + if (!m_core.init(m_coreConfig, cryptonote::MinerConfig(), true)) { + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + if (!m_nodeServer.init(nodetool::NetNodeConfig(), false)) { + m_core.deinit(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + m_node.init([this, callback](std::error_code ec) { + m_node.addObserver(this); + callback(ec); + }); + m_nodeServer.run(); + + m_nodeServer.deinit(); + m_core.deinit(); + m_node.shutdown(); + } + + void deinit() override { + m_nodeServer.send_stop_signal(); + } + + std::string convertPaymentId(const std::string& paymentIdString) override { + return WalletGui::convertPaymentId(paymentIdString); + } + + std::string extractPaymentId(const std::string& extra) override { + return WalletGui::extractPaymentId(extra); + } + + uint64_t getLastKnownBlockHeight() const override { + return m_node.getLastKnownBlockHeight(); + } + + uint64_t getLastLocalBlockHeight() const override { + return m_node.getLastLocalBlockHeight(); + } + + uint64_t getLastLocalBlockTimestamp() const override { + return m_node.getLastLocalBlockTimestamp(); + } + + uint64_t getPeerCount() const override { + return m_node.getPeerCount(); + } + + CryptoNote::IWallet* createWallet() override { + return new CryptoNote::Wallet(m_currency, m_node); + } + +private: + INodeCallback& m_callback; + const cryptonote::Currency& m_currency; + cryptonote::CoreConfig m_coreConfig; + cryptonote::core m_core; + cryptonote::t_cryptonote_protocol_handler m_protocolHandler; + nodetool::node_server> m_nodeServer; + CryptoNote::InProcessNode m_node; + std::future m_nodeServerFuture; + + void peerCountUpdated(size_t count) { + m_callback.peerCountUpdated(*this, count); + } + + void localBlockchainUpdated(uint64_t height) { + m_callback.localBlockchainUpdated(*this, height); + } + + void lastKnownBlockHeightUpdated(uint64_t height) { + m_callback.lastKnownBlockHeightUpdated(*this, height); + } +}; + +Node* createRpcNode(const cryptonote::Currency& currency, INodeCallback& callback, const std::string& nodeHost, unsigned short nodePort) { + return new RpcNode(currency, callback, nodeHost, nodePort); +} + +Node* createInprocessNode(const cryptonote::Currency& currency, INodeCallback& callback, const cryptonote::CoreConfig& coreConfig) { + return new InprocessNode(currency, callback, coreConfig); +} + +} diff --git a/src/CryptoNote.h b/src/CryptoNote.h new file mode 100644 index 0000000..ee3acdc --- /dev/null +++ b/src/CryptoNote.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include +#include +#include + +#include + +namespace cryptonote { + +class Currency; + +} + +namespace CryptoNote { + +class INode; +class IWallet; + +} + +namespace WalletGui { + +class Node { +public: + virtual ~Node() = 0; + virtual void init(const std::function& callback) = 0; + virtual void deinit() = 0; + + virtual std::string convertPaymentId(const std::string& paymentIdString) = 0; + virtual std::string extractPaymentId(const std::string& extra) = 0; + virtual uint64_t getLastKnownBlockHeight() const = 0; + virtual uint64_t getLastLocalBlockHeight() const = 0; + virtual uint64_t getLastLocalBlockTimestamp() const = 0; + virtual uint64_t getPeerCount() const = 0; + + virtual CryptoNote::IWallet* createWallet() = 0; +}; + +class INodeCallback { +public: + virtual void peerCountUpdated(Node& node, size_t count) = 0; + virtual void localBlockchainUpdated(Node& node, uint64_t height) = 0; + virtual void lastKnownBlockHeightUpdated(Node& node, uint64_t height) = 0; +}; + +Node* createRpcNode(const cryptonote::Currency& currency, INodeCallback& callback, const std::string& nodeHost, unsigned short nodePort); +Node* createInprocessNode(const cryptonote::Currency& currency, INodeCallback& callback, const cryptonote::CoreConfig& coreConfig); + +} diff --git a/src/CryptoNoteWalletConfig.h.in b/src/CryptoNoteWalletConfig.h.in new file mode 100644 index 0000000..6e63995 --- /dev/null +++ b/src/CryptoNoteWalletConfig.h.in @@ -0,0 +1,9 @@ + +#pragma once + +namespace WalletGui { + +const char WALLET_CURRENCY_DISPLAY_NAME[] = "@CN_CURRENCY_DISPLAY_NAME@"; +const char WALLET_CURRENCY_TICKER[] = "@CN_CURRENCY_TICKER@"; + +} diff --git a/src/CurrencyAdapter.cpp b/src/CurrencyAdapter.cpp new file mode 100644 index 0000000..26d0514 --- /dev/null +++ b/src/CurrencyAdapter.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "CurrencyAdapter.h" +#include "CryptoNoteWalletConfig.h" + +namespace WalletGui { + +CurrencyAdapter& CurrencyAdapter::instance() { + static CurrencyAdapter inst; + return inst; +} + +CurrencyAdapter::CurrencyAdapter() : m_currency(cryptonote::CurrencyBuilder().currency()) { +} + +CurrencyAdapter::~CurrencyAdapter() { +} + +const cryptonote::Currency& CurrencyAdapter::getCurrency() { + return m_currency; +} + +quintptr CurrencyAdapter::getNumberOfDecimalPlaces() const { + return m_currency.numberOfDecimalPlaces(); +} + +QString CurrencyAdapter::getCurrencyDisplayName() const { + return WALLET_CURRENCY_DISPLAY_NAME; +} + +QString CurrencyAdapter::getCurrencyName() const { + return cryptonote::CRYPTONOTE_NAME; +} + +QString CurrencyAdapter::getCurrencyTicker() const { + return WALLET_CURRENCY_TICKER; +} + +QString CurrencyAdapter::formatAmount(quint64 _amount) const { + QString result = QString::number(_amount); + if (result.length() < getNumberOfDecimalPlaces() + 1) { + result = result.rightJustified(getNumberOfDecimalPlaces() + 1, '0'); + } + + quint32 dot_pos = result.length() - getNumberOfDecimalPlaces(); + for (quint32 pos = result.length() - 1; pos > dot_pos + 1; --pos) { + if (result[pos] == '0') { + result.remove(pos, 1); + } else { + break; + } + } + + result.insert(dot_pos, "."); + for (qint32 pos = dot_pos - 3; pos > 0; pos -= 3) { + if (result[pos - 1].isDigit()) { + result.insert(pos, ','); + } + } + + return result; +} + +quint64 CurrencyAdapter::parseAmount(const QString& _amountString) const { + QString amountString = _amountString.trimmed(); + amountString.remove(','); + + int pointIndex = amountString.indexOf('.'); + int fractionSize; + if (pointIndex != -1) { + fractionSize = amountString.length() - pointIndex - 1; + while (getNumberOfDecimalPlaces() < fractionSize && amountString.right(1) == "0") { + amountString.remove(amountString.length() - 1, 1); + --fractionSize; + } + + if (getNumberOfDecimalPlaces() < fractionSize) { + return 0; + } + + amountString.remove(pointIndex, 1); + } else { + fractionSize = 0; + } + + if (amountString.isEmpty()) { + return 0; + } + + for (qint32 i = 0; i < getNumberOfDecimalPlaces() - fractionSize; ++i) { + amountString.append('0'); + } + + return amountString.toULongLong(); +} + +bool CurrencyAdapter::validateAddress(const QString& _address) const { + cryptonote::AccountPublicAddress internalAddress; + return m_currency.parseAccountAddressString(_address.toStdString(), internalAddress); +} + +} diff --git a/src/CurrencyAdapter.h b/src/CurrencyAdapter.h new file mode 100644 index 0000000..3f9a720 --- /dev/null +++ b/src/CurrencyAdapter.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +#include "cryptonote_core/Currency.h" + +namespace WalletGui { + +class CurrencyAdapter { + +public: + static CurrencyAdapter& instance(); + + const cryptonote::Currency& getCurrency(); + QString getCurrencyDisplayName() const; + QString getCurrencyName() const; + QString getCurrencyTicker() const; + quintptr getNumberOfDecimalPlaces() const; + QString formatAmount(quint64 _amount) const; + quint64 parseAmount(const QString& _amountString) const; + bool validateAddress(const QString& _address) const; + +private: + cryptonote::Currency m_currency; + + CurrencyAdapter(); + ~CurrencyAdapter(); +}; + +} diff --git a/src/NodeAdapter.cpp b/src/NodeAdapter.cpp new file mode 100644 index 0000000..1f14ab8 --- /dev/null +++ b/src/NodeAdapter.cpp @@ -0,0 +1,216 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include +#include + +#include + +#include "CurrencyAdapter.h" +#include "NodeAdapter.h" +#include "Settings.h" + +namespace WalletGui { + +class InProcessNodeInitializer : public QObject { + Q_OBJECT + Q_DISABLE_COPY(InProcessNodeInitializer) + +Q_SIGNALS: + void nodeInitCompletedSignal(); + void nodeInitFailedSignal(int _errorCode); + void nodeDeinitCompletedSignal(); + +public: + InProcessNodeInitializer(QObject* _parent = nullptr) { + } + + ~InProcessNodeInitializer() { + } + + void start(Node** _node, const cryptonote::Currency* currency, INodeCallback* _callback, const cryptonote::CoreConfig& _coreConfig) { + (*_node) = createInprocessNode(*currency, *_callback, _coreConfig); + try { + (*_node)->init([this](std::error_code _err) { + if (_err) { + Q_EMIT nodeInitFailedSignal(_err.value()); + QCoreApplication::processEvents(); + return; + } + + Q_EMIT nodeInitCompletedSignal(); + QCoreApplication::processEvents(); + }); + } catch (std::runtime_error& err) { + Q_EMIT nodeInitFailedSignal(cryptonote::error::INTERNAL_WALLET_ERROR); + QCoreApplication::processEvents(); + return; + } + + delete *_node; + *_node = nullptr; + Q_EMIT nodeDeinitCompletedSignal(); + } + + void stop(Node** _node) { + Q_CHECK_PTR(*_node); + (*_node)->deinit(); + } +}; + +NodeAdapter& NodeAdapter::instance() { + static NodeAdapter inst; + return inst; +} + +NodeAdapter::NodeAdapter() : QObject(), m_node(nullptr), m_nodeInitializerThread(), m_nodeInitializer(new InProcessNodeInitializer) { + m_nodeInitializer->moveToThread(&m_nodeInitializerThread); + + qRegisterMetaType("cryptonote::CoreConfig"); + + connect(m_nodeInitializer, &InProcessNodeInitializer::nodeInitCompletedSignal, this, &NodeAdapter::nodeInitCompletedSignal, Qt::QueuedConnection); + connect(this, &NodeAdapter::initNodeSignal, m_nodeInitializer, &InProcessNodeInitializer::start, Qt::QueuedConnection); + connect(this, &NodeAdapter::deinitNodeSignal, m_nodeInitializer, &InProcessNodeInitializer::stop, Qt::QueuedConnection); + + QString logFileName = QCoreApplication::applicationName() + ".log"; + epee::log_space::log_singletone::add_logger(LOGGER_FILE, logFileName.toLocal8Bit(), + Settings::instance().getDataDir().absolutePath().toLocal8Bit()); +} + +NodeAdapter::~NodeAdapter() { +} + +quintptr NodeAdapter::getPeerCount() const { + Q_ASSERT(m_node != nullptr); + return m_node->getPeerCount(); +} + +std::string NodeAdapter::convertPaymentId(const QString& _paymentIdString) const { + Q_CHECK_PTR(m_node); + try { + return m_node->convertPaymentId(_paymentIdString.toStdString()); + } catch (std::runtime_error& err) { + } + return std::string(); +} + +QString NodeAdapter::extractPaymentId(const std::string& _extra) const { + Q_CHECK_PTR(m_node); + return QString::fromStdString(m_node->extractPaymentId(_extra)); +} + +CryptoNote::IWallet* NodeAdapter::createWallet() const { + Q_CHECK_PTR(m_node); + return m_node->createWallet(); +} + +bool NodeAdapter::init() { + Q_ASSERT(m_node == nullptr); + // TODO Insert the right URL for the local daemon + QUrl localNodeUrl = QUrl::fromUserInput(""); + + m_node = createRpcNode(CurrencyAdapter::instance().getCurrency(), *this, localNodeUrl.host().toStdString(), localNodeUrl.port()); + + QTimer initTimer; + initTimer.setInterval(3000); + initTimer.setSingleShot(true); + initTimer.start(); + m_node->init([this](std::error_code _err) { + Q_UNUSED(_err); + }); + QEventLoop waitLoop; + connect(&initTimer, &QTimer::timeout, &waitLoop, &QEventLoop::quit); + connect(this, &NodeAdapter::peerCountUpdatedSignal, &waitLoop, &QEventLoop::quit); + connect(this, &NodeAdapter::localBlockchainUpdatedSignal, &waitLoop, &QEventLoop::quit); + waitLoop.exec(); + if (initTimer.isActive()) { + initTimer.stop(); + Q_EMIT nodeInitCompletedSignal(); + return true; + } + + delete m_node; + m_node = nullptr; + return initInProcessNode(); +} + +quint64 NodeAdapter::getLastKnownBlockHeight() const { + Q_CHECK_PTR(m_node); + return m_node->getLastKnownBlockHeight(); +} + +quint64 NodeAdapter::getLastLocalBlockHeight() const { + Q_CHECK_PTR(m_node); + return m_node->getLastLocalBlockHeight(); +} + +QDateTime NodeAdapter::getLastLocalBlockTimestamp() const { + Q_CHECK_PTR(m_node); + return QDateTime::fromTime_t(m_node->getLastLocalBlockTimestamp(), Qt::UTC); +} + +void NodeAdapter::peerCountUpdated(Node& _node, size_t _count) { + Q_UNUSED(_node); + Q_EMIT peerCountUpdatedSignal(_count); +} + +void NodeAdapter::localBlockchainUpdated(Node& _node, uint64_t _height) { + Q_UNUSED(_node); + Q_EMIT localBlockchainUpdatedSignal(_height); +} + +void NodeAdapter::lastKnownBlockHeightUpdated(Node& _node, uint64_t _height) { + Q_UNUSED(_node); + Q_EMIT lastKnownBlockHeightUpdatedSignal(_height); +} + +bool NodeAdapter::initInProcessNode() { + Q_ASSERT(m_node == nullptr); + m_nodeInitializerThread.start(); + cryptonote::CoreConfig coreConfig = makeCoreConfig(); + Q_EMIT initNodeSignal(&m_node, &CurrencyAdapter::instance().getCurrency(), this, coreConfig); + QEventLoop waitLoop; + connect(m_nodeInitializer, &InProcessNodeInitializer::nodeInitCompletedSignal, &waitLoop, &QEventLoop::quit); + connect(m_nodeInitializer, &InProcessNodeInitializer::nodeInitFailedSignal, &waitLoop, &QEventLoop::exit); + if (waitLoop.exec() != 0) { + return false; + } + + Q_EMIT localBlockchainUpdatedSignal(getLastLocalBlockHeight()); + Q_EMIT lastKnownBlockHeightUpdatedSignal(getLastKnownBlockHeight()); + return true; +} + +void NodeAdapter::deinit() { + if (m_node != nullptr) { + if (m_nodeInitializerThread.isRunning()) { + m_nodeInitializer->stop(&m_node); + QEventLoop waitLoop; + connect(m_nodeInitializer, &InProcessNodeInitializer::nodeDeinitCompletedSignal, &waitLoop, &QEventLoop::quit, Qt::QueuedConnection); + waitLoop.exec(); + m_nodeInitializerThread.quit(); + m_nodeInitializerThread.wait(); + } else { + delete m_node; + m_node = nullptr; + } + } +} + +cryptonote::CoreConfig NodeAdapter::makeCoreConfig() const { + cryptonote::CoreConfig config; + boost::program_options::variables_map options; + boost::any dataDir = Settings::instance().getDataDir().absolutePath().toStdString(); + options.insert(std::make_pair("data-dir", boost::program_options::variable_value(dataDir, false))); + config.init(options); + return config; +} + +} + +#include "NodeAdapter.moc" diff --git a/src/NodeAdapter.h b/src/NodeAdapter.h new file mode 100644 index 0000000..18467a4 --- /dev/null +++ b/src/NodeAdapter.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +#include +#include + +#include "CryptoNote.h" + +namespace cryptonote { + +class Currency; + +} + +namespace WalletGui { + +class InProcessNodeInitializer; + +class NodeAdapter : public QObject, public INodeCallback { + Q_OBJECT + Q_DISABLE_COPY(NodeAdapter) + +public: + static NodeAdapter& instance(); + + quintptr getPeerCount() const; + std::string convertPaymentId(const QString& _payment_id_string) const; + QString extractPaymentId(const std::string& _extra) const; + CryptoNote::IWallet* createWallet() const; + + bool init(); + void deinit(); + quint64 getLastKnownBlockHeight() const; + quint64 getLastLocalBlockHeight() const; + QDateTime getLastLocalBlockTimestamp() const; + void peerCountUpdated(Node& _node, size_t _count) Q_DECL_OVERRIDE; + void localBlockchainUpdated(Node& _node, uint64_t _height) Q_DECL_OVERRIDE; + void lastKnownBlockHeightUpdated(Node& _node, uint64_t _height) Q_DECL_OVERRIDE; + +private: + Node* m_node; + QThread m_nodeInitializerThread; + InProcessNodeInitializer* m_nodeInitializer; + + NodeAdapter(); + ~NodeAdapter(); + + bool initInProcessNode(); + cryptonote::CoreConfig makeCoreConfig() const; + +Q_SIGNALS: + void localBlockchainUpdatedSignal(quint64 _height); + void lastKnownBlockHeightUpdatedSignal(quint64 _height); + void nodeInitCompletedSignal(); + void peerCountUpdatedSignal(quintptr _count); + void initNodeSignal(Node** _node, const cryptonote::Currency* currency, INodeCallback* _callback, const cryptonote::CoreConfig& _core_config); + void deinitNodeSignal(Node** _node); +}; + +} diff --git a/src/Settings.cpp b/src/Settings.cpp new file mode 100644 index 0000000..547047b --- /dev/null +++ b/src/Settings.cpp @@ -0,0 +1,225 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include +#include +#include + +#include + +#include "CommandLineParser.h" +#include "CurrencyAdapter.h" +#include "Settings.h" + +namespace WalletGui { + +Settings& Settings::instance() { + static Settings inst; + return inst; +} + +Settings::Settings() : QObject(), m_cmdLineParser(nullptr) { +} + +Settings::~Settings() { +} + +void Settings::setCommandLineParser(CommandLineParser* _cmdLineParser) { + Q_CHECK_PTR(_cmdLineParser); + m_cmdLineParser = _cmdLineParser; +} + +void Settings::load() { + QFile cfgFile(getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".cfg")); + if (cfgFile.open(QIODevice::ReadOnly)) { + m_settings = QJsonDocument::fromJson(cfgFile.readAll()).object(); + cfgFile.close(); + if (!m_settings.contains("walletFile")) { + m_addressBookFile = getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".addressbook"); + } else { + m_addressBookFile = m_settings.value("walletFile").toString(); + m_addressBookFile.replace(m_addressBookFile.lastIndexOf(".wallet"), 7, ".addressbook"); + } + } else { + m_addressBookFile = getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".addressbook"); + } +} + +QDir Settings::getDataDir() const { + Q_CHECK_PTR(m_cmdLineParser); + return QDir(m_cmdLineParser->getDataDir()); +} + +QString Settings::getWalletFile() const { + return m_settings.contains("walletFile") ? m_settings.value("walletFile").toString() : + getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".wallet"); +} + +QString Settings::getAddressBookFile() const { + return m_addressBookFile; +} + +bool Settings::isEncrypted() const { + return m_settings.contains("encrypted") ? m_settings.value("encrypted").toBool() : false; +} + +QString Settings::getVersion() const { + return GIT_REVISION; +} + +bool Settings::isStartOnLoginEnabled() const { + bool res = false; +#ifdef Q_OS_MAC + QDir autorunDir = QDir::home(); + if (!autorunDir.cd("Library") || !autorunDir.cd("LaunchAgents")) { + return false; + } + + QString autorunFilePath = autorunDir.absoluteFilePath(QCoreApplication::applicationName() + ".plist"); + if (!QFile::exists(autorunFilePath)) { + return false; + } + + QSettings autorunSettings(autorunFilePath, QSettings::NativeFormat); + res = autorunSettings.value("RunAtLoad", false).toBool(); +#elif defined(Q_OS_LINUX) + QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + if (configPath.isEmpty()) { + return false; + } + + QDir autorunDir(configPath); + if (!autorunDir.cd("autostart")) { + return false; + } + + QString autorunFilePath = autorunDir.absoluteFilePath(QCoreApplication::applicationName() + ".desktop"); + res = QFile::exists(autorunFilePath); +#elif defined(Q_OS_WIN) + QSettings autorunSettings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); + QString keyName = QString("%1Wallet").arg(CurrencyAdapter::instance().getCurrencyDisplayName()); + res = autorunSettings.contains(keyName) && + !QDir::fromNativeSeparators(autorunSettings.value(keyName).toString()).compare(QApplication::applicationFilePath()); +#endif + return res; +} + +#ifdef Q_OS_WIN +bool Settings::isMinimizeToTrayEnabled() const { + return m_settings.contains("minimizeToTray") ? m_settings.value("minimizeToTray").toBool() : false; +} + +bool Settings::isCloseToTrayEnabled() const { + return m_settings.contains("closeToTray") ? m_settings.value("closeToTray").toBool() : false; +} +#endif + +void Settings::setWalletFile(const QString& _file) { + if (_file.endsWith(".wallet") || _file.endsWith(".keys")) { + m_settings.insert("walletFile", _file); + } else { + m_settings.insert("walletFile", _file + ".wallet"); + } + + saveSettings(); + m_addressBookFile = m_settings.value("walletFile").toString(); + m_addressBookFile.replace(m_addressBookFile.lastIndexOf(".wallet"), 7, ".addressbook"); +} + +void Settings::setEncrypted(bool _encrypted) { + if (isEncrypted() != _encrypted) { + m_settings.insert("encrypted", _encrypted); + saveSettings(); + } +} + +void Settings::setCurrentTheme(const QString& _theme) { +} + +void Settings::setStartOnLoginEnabled(bool _enable) { +#ifdef Q_OS_MAC + QDir autorunDir = QDir::home(); + if (!autorunDir.cd("Library") || !autorunDir.cd("LaunchAgents")) { + return; + } + + QString autorunFilePath = autorunDir.absoluteFilePath(QCoreApplication::applicationName() + ".plist"); + QSettings autorunSettings(autorunFilePath, QSettings::NativeFormat); + autorunSettings.setValue("Label", "org." + QCoreApplication::applicationName()); + autorunSettings.setValue("Program", QApplication::applicationFilePath()); + autorunSettings.setValue("RunAtLoad", _enable); + autorunSettings.setValue("ProcessType", "InterActive"); +#elif defined(Q_OS_LINUX) + QString configPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); + if (configPath.isEmpty()) { + return; + } + + QDir autorunDir(configPath); + if(!autorunDir.exists("autostart")) { + autorunDir.mkdir("autostart"); + } + + if (!autorunDir.cd("autostart")) { + return; + } + + QString autorunFilePath = autorunDir.absoluteFilePath(QCoreApplication::applicationName() + ".desktop"); + QFile autorunFile(autorunFilePath); + if (!autorunFile.open(QFile::WriteOnly | QFile::Truncate)) { + return; + } + + if (_enable) { + autorunFile.write("[Desktop Entry]\n"); + autorunFile.write("Type=Application\n"); + autorunFile.write(QString("Name=%1 Wallet\n").arg(CurrencyAdapter::instance().getCurrencyDisplayName()).toLocal8Bit()); + autorunFile.write(QString("Exec=%1\n").arg(QApplication::applicationFilePath()).toLocal8Bit()); + autorunFile.write("Terminal=false\n"); + autorunFile.write("Hidden=false\n"); + autorunFile.close(); + } else { + QFile::remove(autorunFilePath); + } +#elif defined(Q_OS_WIN) + QSettings autorunSettings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); + QString keyName = QString("%1Wallet").arg(CurrencyAdapter::instance().getCurrencyDisplayName()); + if (_enable) { + autorunSettings.setValue(keyName, QDir::toNativeSeparators(QApplication::applicationFilePath())); + } else { + autorunSettings.remove(keyName); + } +#endif +} + +#ifdef Q_OS_WIN +void Settings::setMinimizeToTrayEnabled(bool _enable) { + if (isMinimizeToTrayEnabled() != _enable) { + m_settings.insert("minimizeToTray", _enable); + saveSettings(); + } +} + +void Settings::setCloseToTrayEnabled(bool _enable) { + if (isCloseToTrayEnabled() != _enable) { + m_settings.insert("closeToTray", _enable); + saveSettings(); + } +} +#endif + +void Settings::saveSettings() const { + QFile cfgFile(getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".cfg")); + if (cfgFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QJsonDocument cfg_doc(m_settings); + cfgFile.write(cfg_doc.toJson()); + cfgFile.close(); + } +} + +} diff --git a/src/Settings.h b/src/Settings.h new file mode 100644 index 0000000..20ffe4d --- /dev/null +++ b/src/Settings.h @@ -0,0 +1,57 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include +#include + +namespace WalletGui { + +class CommandLineParser; + +class Settings : public QObject { + Q_OBJECT + Q_DISABLE_COPY(Settings) + +public: + static Settings& instance(); + + void setCommandLineParser(CommandLineParser* _cmd_line_parser); + void load(); + + QDir getDataDir() const; + QString getWalletFile() const; + QString getAddressBookFile() const; + bool isEncrypted() const; + QString getVersion() const; + bool isStartOnLoginEnabled() const; +#ifdef Q_OS_WIN + bool isMinimizeToTrayEnabled() const; + bool isCloseToTrayEnabled() const; +#endif + + void setWalletFile(const QString& _file); + void setEncrypted(bool _encrypted); + void setCurrentTheme(const QString& _theme); + void setStartOnLoginEnabled(bool _enable); +#ifdef Q_OS_WIN + void setMinimizeToTrayEnabled(bool _enable); + void setCloseToTrayEnabled(bool _enable); +#endif + +private: + QJsonObject m_settings; + QString m_addressBookFile; + CommandLineParser* m_cmdLineParser; + + Settings(); + ~Settings(); + + void saveSettings() const; +}; + +} diff --git a/src/SignalHandler.cpp b/src/SignalHandler.cpp new file mode 100644 index 0000000..445250f --- /dev/null +++ b/src/SignalHandler.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include "SignalHandler.h" + +namespace WalletGui { + +SignalHandler& SignalHandler::instance() { + static SignalHandler inst; + return inst; +} + +SignalHandler::SignalHandler() : QObject() { +} + +SignalHandler::~SignalHandler() { +} + +void SignalHandler::init() { + std::signal(SIGINT, SignalHandler::sigHandler); + std::signal(SIGTERM, SignalHandler::sigHandler); +} + +void SignalHandler::sigHandler(int _signal) { + Q_EMIT SignalHandler::instance().quitSignal(); +} + +} diff --git a/src/SignalHandler.h b/src/SignalHandler.h new file mode 100644 index 0000000..fac3966 --- /dev/null +++ b/src/SignalHandler.h @@ -0,0 +1,31 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace WalletGui { + +class SignalHandler : public QObject { + Q_OBJECT + Q_DISABLE_COPY(SignalHandler) + +public: + static SignalHandler& instance(); + + void init(); + +private: + SignalHandler(); + ~SignalHandler(); + + static void sigHandler(int _signal); + +Q_SIGNALS: + void quitSignal(); +}; + +} diff --git a/src/WalletAdapter.cpp b/src/WalletAdapter.cpp new file mode 100644 index 0000000..436e2cd --- /dev/null +++ b/src/WalletAdapter.cpp @@ -0,0 +1,432 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include + +#include +#include +#include + +#include "NodeAdapter.h" +#include "Settings.h" +#include "WalletAdapter.h" + +namespace WalletGui { + +const quint32 MSECS_IN_HOUR = 60 * 60 * 1000; +const quint32 MSECS_IN_MINUTE = 60 * 1000; + +const quint32 LAST_BLOCK_INFO_UPDATING_INTERVAL = 1 * MSECS_IN_MINUTE; +const quint32 LAST_BLOCK_INFO_WARNING_INTERVAL = 1 * MSECS_IN_HOUR; + +WalletAdapter& WalletAdapter::instance() { + static WalletAdapter inst; + return inst; +} + +WalletAdapter::WalletAdapter() : QObject(), m_wallet(nullptr), m_mutex(), m_isBackupInProgress(false), + m_isSynchronized(false), m_newTransactionsNotificationTimer(), + m_lastWalletTransactionId(std::numeric_limits::max()) { + connect(this, &WalletAdapter::walletInitCompletedSignal, this, &WalletAdapter::onWalletInitCompleted, Qt::QueuedConnection); + connect(this, &WalletAdapter::walletSendTransactionCompletedSignal, this, &WalletAdapter::onWalletSendTransactionCompleted, Qt::QueuedConnection); + connect(this, &WalletAdapter::updateBlockStatusTextSignal, this, &WalletAdapter::updateBlockStatusText, Qt::QueuedConnection); + connect(this, &WalletAdapter::updateBlockStatusTextWithDelaySignal, this, &WalletAdapter::updateBlockStatusTextWithDelay, Qt::QueuedConnection); + connect(&m_newTransactionsNotificationTimer, &QTimer::timeout, this, &WalletAdapter::notifyAboutLastTransaction); + connect(this, &WalletAdapter::walletSynchronizationProgressUpdatedSignal, this, [&]() { + if (!m_newTransactionsNotificationTimer.isActive()) { + m_newTransactionsNotificationTimer.start(); + } + }, Qt::QueuedConnection); + + connect(this, &WalletAdapter::walletSynchronizationCompletedSignal, this, [&]() { + m_newTransactionsNotificationTimer.stop(); + notifyAboutLastTransaction(); + }, Qt::QueuedConnection); + + m_newTransactionsNotificationTimer.setInterval(500); +} + +WalletAdapter::~WalletAdapter() { +} + +QString WalletAdapter::getAddress() const { + try { + return m_wallet == nullptr ? QString() : QString::fromStdString(m_wallet->getAddress()); + } catch (std::system_error&) { + return QString(); + } +} + +quint64 WalletAdapter::getActualBalance() const { + try { + return m_wallet == nullptr ? 0 : m_wallet->actualBalance(); + } catch (std::system_error&) { + return 0; + } +} + +quint64 WalletAdapter::getPendingBalance() const { + try { + return m_wallet == nullptr ? 0 : m_wallet->pendingBalance(); + } catch (std::system_error&) { + return 0; + } +} + +void WalletAdapter::open(const QString& _password) { + Q_ASSERT(m_wallet == nullptr); + Settings::instance().setEncrypted(!_password.isEmpty()); + Q_EMIT walletStateChangedSignal(tr("Opening wallet")); + + m_wallet = NodeAdapter::instance().createWallet(); + m_wallet->addObserver(this); + + if (QFile::exists(Settings::instance().getWalletFile())) { + if (Settings::instance().getWalletFile().endsWith(".keys")) { + if(!importLegacyWallet(_password)) { + return; + } + } + + if (openFile(Settings::instance().getWalletFile(), true)) { + try { + m_wallet->initAndLoad(m_file, _password.toStdString()); + } catch (std::system_error&) { + closeFile(); + delete m_wallet; + m_wallet = nullptr; + } + } + } else { + Settings::instance().setEncrypted(false); + try { + m_wallet->initAndGenerate(""); + } catch (std::system_error&) { + delete m_wallet; + m_wallet = nullptr; + } + } +} + +bool WalletAdapter::isOpen() const { + return m_wallet != nullptr; +} + +bool WalletAdapter::importLegacyWallet(const QString &_password) { + QString fileName = Settings::instance().getWalletFile(); + Settings::instance().setEncrypted(!_password.isEmpty()); + try { + fileName.replace(fileName.lastIndexOf(".keys"), 5, ".wallet"); + if (!openFile(fileName, false)) { + delete m_wallet; + m_wallet = nullptr; + return false; + } + + cryptonote::importLegacyKeys(Settings::instance().getWalletFile().toStdString(), _password.toStdString(), m_file); + closeFile(); + Settings::instance().setWalletFile(fileName); + return true; + } catch (std::system_error& _err) { + closeFile(); + if (_err.code().value() == cryptonote::error::WRONG_PASSWORD) { + Settings::instance().setEncrypted(true); + Q_EMIT openWalletWithPasswordSignal(!_password.isEmpty()); + } + } catch (std::runtime_error& _err) { + closeFile(); + } + + delete m_wallet; + m_wallet = nullptr; + return false; +} + +void WalletAdapter::close() { + Q_CHECK_PTR(m_wallet); + save(true, true); + lock(); + m_wallet->removeObserver(this); + m_isSynchronized = false; + m_newTransactionsNotificationTimer.stop(); + m_lastWalletTransactionId = std::numeric_limits::max(); + Q_EMIT walletCloseCompletedSignal(); + QCoreApplication::processEvents(); + delete m_wallet; + m_wallet = nullptr; + unlock(); +} + +bool WalletAdapter::save(bool _details, bool _cache) { + return save(Settings::instance().getWalletFile() + ".temp", _details, _cache); +} + +bool WalletAdapter::save(const QString& _file, bool _details, bool _cache) { + Q_CHECK_PTR(m_wallet); + if (openFile(_file, false)) { + try { + m_wallet->save(m_file, _details, _cache); + } catch (std::system_error&) { + closeFile(); + return false; + } + Q_EMIT walletStateChangedSignal(tr("Saving data")); + } else { + return false; + } + + return true; +} + +void WalletAdapter::backup(const QString& _file) { + if (save(_file.endsWith(".wallet") ? _file : _file + ".wallet", true, false)) { + m_isBackupInProgress = true; + } +} + +quint64 WalletAdapter::getTransactionCount() const { + Q_CHECK_PTR(m_wallet); + try { + return m_wallet->getTransactionCount(); + } catch (std::system_error&) { + } + + return 0; +} + +quint64 WalletAdapter::getTransferCount() const { + Q_CHECK_PTR(m_wallet); + try { + return m_wallet->getTransferCount(); + } catch (std::system_error&) { + } + + return 0; +} + +bool WalletAdapter::getTransaction(CryptoNote::TransactionId& _id, CryptoNote::TransactionInfo& _transaction) { + Q_CHECK_PTR(m_wallet); + try { + return m_wallet->getTransaction(_id, _transaction); + } catch (std::system_error&) { + } + + return false; +} + +bool WalletAdapter::getTransfer(CryptoNote::TransferId& _id, CryptoNote::Transfer& _transfer) { + Q_CHECK_PTR(m_wallet); + try { + return m_wallet->getTransfer(_id, _transfer); + } catch (std::system_error&) { + } + + return false; +} + +void WalletAdapter::sendTransaction(const QVector& _transfers, quint64 _fee, const QString& _paymentId, quint64 _mixin) { + Q_CHECK_PTR(m_wallet); + try { + lock(); + m_wallet->sendTransaction(_transfers.toStdVector(), _fee, NodeAdapter::instance().convertPaymentId(_paymentId), _mixin, 0); + Q_EMIT walletStateChangedSignal(tr("Sending transaction")); + } catch (std::system_error&) { + unlock(); + } +} + +bool WalletAdapter::changePassword(const QString& _oldPassword, const QString& _newPassword) { + Q_CHECK_PTR(m_wallet); + try { + if (m_wallet->changePassword(_oldPassword.toStdString(), _newPassword.toStdString()).value() == cryptonote::error::WRONG_PASSWORD) { + return false; + } + } catch (std::system_error&) { + return false; + } + + Settings::instance().setEncrypted(!_newPassword.isEmpty()); + return save(true, true); +} + +void WalletAdapter::setWalletFile(const QString& _path) { + Q_ASSERT(m_wallet == nullptr); + Settings::instance().setWalletFile(_path); +} + +void WalletAdapter::initCompleted(std::error_code _error) { + if (m_file.is_open()) { + closeFile(); + } + + Q_EMIT walletInitCompletedSignal(_error.value(), QString::fromStdString(_error.message())); +} + +void WalletAdapter::onWalletInitCompleted(int _error, const QString& _errorText) { + switch(_error) { + case 0: { + Q_EMIT walletActualBalanceUpdatedSignal(m_wallet->actualBalance()); + Q_EMIT walletPendingBalanceUpdatedSignal(m_wallet->pendingBalance()); + Q_EMIT updateWalletAddressSignal(QString::fromStdString(m_wallet->getAddress())); + Q_EMIT reloadWalletTransactionsSignal(); + Q_EMIT walletStateChangedSignal(tr("Ready")); + QTimer::singleShot(5000, this, SLOT(updateBlockStatusText())); + if (!QFile::exists(Settings::instance().getWalletFile())) { + save(true, true); + } + + break; + } + case cryptonote::error::WRONG_PASSWORD: + Q_EMIT openWalletWithPasswordSignal(Settings::instance().isEncrypted()); + Settings::instance().setEncrypted(true); + delete m_wallet; + m_wallet = nullptr; + break; + default: { + delete m_wallet; + m_wallet = nullptr; + break; + } + } +} + +void WalletAdapter::saveCompleted(std::error_code _error) { + if (!_error && !m_isBackupInProgress) { + closeFile(); + renameFile(Settings::instance().getWalletFile() + ".temp", Settings::instance().getWalletFile()); + Q_EMIT walletStateChangedSignal(tr("Ready")); + Q_EMIT updateBlockStatusTextWithDelaySignal(); + } else if (m_isBackupInProgress) { + m_isBackupInProgress = false; + closeFile(); + } else { + closeFile(); + } + + Q_EMIT walletSaveCompletedSignal(_error.value(), QString::fromStdString(_error.message())); +} + +void WalletAdapter::synchronizationProgressUpdated(uint64_t _current, uint64_t _total) { + m_isSynchronized = false; + Q_EMIT walletStateChangedSignal(QString("%1 %2/%3").arg(tr("Synchronizing")).arg(_current).arg(_total)); + Q_EMIT walletSynchronizationProgressUpdatedSignal(_current, _total); +} + +void WalletAdapter::synchronizationCompleted(std::error_code _error) { + if (!_error) { + m_isSynchronized = true; + Q_EMIT updateBlockStatusTextSignal(); + Q_EMIT walletSynchronizationCompletedSignal(_error.value(), QString::fromStdString(_error.message())); + } +} + +void WalletAdapter::actualBalanceUpdated(uint64_t _actual_balance) { + Q_EMIT walletActualBalanceUpdatedSignal(_actual_balance); +} + +void WalletAdapter::pendingBalanceUpdated(uint64_t _pending_balance) { + Q_EMIT walletPendingBalanceUpdatedSignal(_pending_balance); +} + +void WalletAdapter::externalTransactionCreated(CryptoNote::TransactionId _transactionId) { + if (!m_isSynchronized) { + m_lastWalletTransactionId = _transactionId; + } else { + Q_EMIT walletTransactionCreatedSignal(_transactionId); + } +} + +void WalletAdapter::sendTransactionCompleted(CryptoNote::TransactionId _transaction_id, std::error_code _error) { + unlock(); + Q_EMIT walletSendTransactionCompletedSignal(_transaction_id, _error.value(), QString::fromStdString(_error.message())); + Q_EMIT updateBlockStatusTextWithDelaySignal(); +} + +void WalletAdapter::onWalletSendTransactionCompleted(CryptoNote::TransactionId _transactionId, int _error, const QString& _errorText) { + if (_error) { + return; + } + + CryptoNote::TransactionInfo transaction; + if (!this->getTransaction(_transactionId, transaction)) { + return; + } + + if (transaction.transferCount == 0) { + return; + } + + Q_EMIT walletTransactionCreatedSignal(_transactionId); + + save(true, true); +} + +void WalletAdapter::transactionUpdated(CryptoNote::TransactionId _transactionId) { + Q_EMIT walletTransactionUpdatedSignal(_transactionId); +} + +void WalletAdapter::lock() { + m_mutex.lock(); +} + +void WalletAdapter::unlock() { + m_mutex.unlock(); +} + +bool WalletAdapter::openFile(const QString& _file, bool _readOnly) { + lock(); + m_file.open(_file.toStdString(), std::ios::binary | (_readOnly ? std::ios::in : (std::ios::out | std::ios::trunc))); + if (!m_file.is_open()) { + unlock(); + } + + return m_file.is_open(); +} + +void WalletAdapter::closeFile() { + m_file.close(); + unlock(); +} + +void WalletAdapter::notifyAboutLastTransaction() { + if (m_lastWalletTransactionId != std::numeric_limits::max()) { + Q_EMIT walletTransactionCreatedSignal(m_lastWalletTransactionId); + m_lastWalletTransactionId = std::numeric_limits::max(); + } +} + +void WalletAdapter::renameFile(const QString& _oldName, const QString& _newName) { + Q_ASSERT(QFile::exists(_oldName)); + QFile::remove(_newName); + QFile::rename(_oldName, _newName); +} + +void WalletAdapter::updateBlockStatusText() { + if (m_wallet == nullptr) { + return; + } + + const QDateTime currentTime = QDateTime::currentDateTimeUtc(); + const QDateTime blockTime = NodeAdapter::instance().getLastLocalBlockTimestamp(); + quint64 blockAge = blockTime.msecsTo(currentTime); + const QString warningString = blockTime.msecsTo(currentTime) < LAST_BLOCK_INFO_WARNING_INTERVAL ? "" : + QString(" Warning: last block was received %1 hours %2 minutes ago").arg(blockAge / MSECS_IN_HOUR).arg(blockAge % MSECS_IN_HOUR / MSECS_IN_MINUTE); + Q_EMIT walletStateChangedSignal(QString(tr("Wallet synchronized. Height: %1 | Time (UTC): %2%3")). + arg(NodeAdapter::instance().getLastLocalBlockHeight()). + arg(QLocale(QLocale::English).toString(blockTime, "dd MMM yyyy, HH:mm:ss")). + arg(warningString)); + + QTimer::singleShot(LAST_BLOCK_INFO_UPDATING_INTERVAL, this, SLOT(updateBlockStatusText())); +} + +void WalletAdapter::updateBlockStatusTextWithDelay() { + QTimer::singleShot(5000, this, SLOT(updateBlockStatusText())); +} + +} diff --git a/src/WalletAdapter.h b/src/WalletAdapter.h new file mode 100644 index 0000000..615f819 --- /dev/null +++ b/src/WalletAdapter.h @@ -0,0 +1,103 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include +#include + +#include +#include + +#include + +namespace WalletGui { + +class WalletAdapter : public QObject, public CryptoNote::IWalletObserver { + Q_OBJECT + Q_DISABLE_COPY(WalletAdapter) + +public: + static WalletAdapter& instance(); + + void open(const QString& _password); + void close(); + bool save(bool _details, bool _cache); + void backup(const QString& _file); + + QString getAddress() const; + quint64 getActualBalance() const; + quint64 getPendingBalance() const; + quint64 getTransactionCount() const; + quint64 getTransferCount() const; + bool getTransaction(CryptoNote::TransactionId& _id, CryptoNote::TransactionInfo& _transaction); + bool getTransfer(CryptoNote::TransferId& _id, CryptoNote::Transfer& _transfer); + bool isOpen() const; + void sendTransaction(const QVector& _transfers, quint64 _fee, const QString& _payment_id, quint64 _mixin); + bool changePassword(const QString& _old_pass, const QString& _new_pass); + void setWalletFile(const QString& _path); + + void initCompleted(std::error_code _result) Q_DECL_OVERRIDE; + void saveCompleted(std::error_code _result) Q_DECL_OVERRIDE; + void synchronizationProgressUpdated(uint64_t _current, uint64_t _total) Q_DECL_OVERRIDE; + void synchronizationCompleted(std::error_code _error) Q_DECL_OVERRIDE; + void actualBalanceUpdated(uint64_t _actual_balance) Q_DECL_OVERRIDE; + void pendingBalanceUpdated(uint64_t _pending_balance) Q_DECL_OVERRIDE; + void externalTransactionCreated(CryptoNote::TransactionId _transaction_id) Q_DECL_OVERRIDE; + void sendTransactionCompleted(CryptoNote::TransactionId _transaction_id, std::error_code _result) Q_DECL_OVERRIDE; + void transactionUpdated(CryptoNote::TransactionId _transaction_id) Q_DECL_OVERRIDE; + +private: + std::fstream m_file; + CryptoNote::IWallet* m_wallet; + QMutex m_mutex; + std::atomic m_isBackupInProgress; + std::atomic m_isSynchronized; + std::atomic m_lastWalletTransactionId; + QTimer m_newTransactionsNotificationTimer; + + WalletAdapter(); + ~WalletAdapter(); + + void onWalletInitCompleted(int _error, const QString& _error_text); + void onWalletSendTransactionCompleted(CryptoNote::TransactionId _transaction_id, int _error, const QString& _error_text); + + bool importLegacyWallet(const QString &_password); + bool save(const QString& _file, bool _details, bool _cache); + void lock(); + void unlock(); + bool openFile(const QString& _file, bool _read_only); + void closeFile(); + void notifyAboutLastTransaction(); + + static void renameFile(const QString& _old_name, const QString& _new_name); + Q_SLOT void updateBlockStatusText(); + Q_SLOT void updateBlockStatusTextWithDelay(); + +Q_SIGNALS: + void walletInitCompletedSignal(int _error, const QString& _error_text); + void walletCloseCompletedSignal(); + void walletSaveCompletedSignal(int _error, const QString& _error_text); + void walletSynchronizationProgressUpdatedSignal(quint64 _current, quint64 _total); + void walletSynchronizationCompletedSignal(int _error, const QString& _error_text); + void walletActualBalanceUpdatedSignal(quint64 _actual_balance); + void walletPendingBalanceUpdatedSignal(quint64 _pending_balance); + void walletTransactionCreatedSignal(CryptoNote::TransactionId _transaction_id); + void walletSendTransactionCompletedSignal(CryptoNote::TransactionId _transaction_id, int _error, const QString& _error_text); + void walletTransactionUpdatedSignal(CryptoNote::TransactionId _transaction_id); + void walletStateChangedSignal(const QString &_state_text); + + void openWalletWithPasswordSignal(bool _error); + void changeWalletPasswordSignal(); + void updateWalletAddressSignal(const QString& _address); + void reloadWalletTransactionsSignal(); + void updateBlockStatusTextSignal(); + void updateBlockStatusTextWithDelaySignal(); +}; + + + +} diff --git a/src/cryptonotewallet.rc b/src/cryptonotewallet.rc new file mode 100644 index 0000000..a790366 --- /dev/null +++ b/src/cryptonotewallet.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON "images\cryptonote.ico" diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp new file mode 100644 index 0000000..1e3418e --- /dev/null +++ b/src/gui/AboutDialog.cpp @@ -0,0 +1,24 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "AboutDialog.h" +#include "CurrencyAdapter.h" +#include "Settings.h" + +#include "ui_aboutdialog.h" + +namespace WalletGui { + +AboutDialog::AboutDialog(QWidget* _parent) : QDialog(_parent), m_ui(new Ui::AboutDialog) { + m_ui->setupUi(this); + setWindowTitle(QString(tr("About %1 Wallet")).arg(CurrencyAdapter::instance().getCurrencyDisplayName())); + QString aboutText = m_ui->m_aboutLabel->text(); + m_ui->m_aboutLabel->setText(aboutText.arg(GIT_REVISION)); +} + +AboutDialog::~AboutDialog() { +} + +} diff --git a/src/gui/AboutDialog.h b/src/gui/AboutDialog.h new file mode 100644 index 0000000..fdb9775 --- /dev/null +++ b/src/gui/AboutDialog.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class AboutDialog; +} + +namespace WalletGui { + +class AboutDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(AboutDialog) + +public: + AboutDialog(QWidget* _parent); + ~AboutDialog(); + +private: + QScopedPointer m_ui; +}; + +} diff --git a/src/gui/AddressBookDialog.cpp b/src/gui/AddressBookDialog.cpp new file mode 100644 index 0000000..71c66b3 --- /dev/null +++ b/src/gui/AddressBookDialog.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "AddressBookDialog.h" +#include "AddressBookModel.h" + +#include "ui_addressbookdialog.h" + +namespace WalletGui { + +AddressBookDialog::AddressBookDialog(QWidget* _parent) : QDialog(_parent), m_ui(new Ui::AddressBookDialog) { + m_ui->setupUi(this); + m_ui->m_addressBookView->setModel(&AddressBookModel::instance()); + if (AddressBookModel::instance().rowCount() > 0) { + m_ui->m_addressBookView->setCurrentIndex(AddressBookModel::instance().index(0, 0)); + } +} + +AddressBookDialog::~AddressBookDialog() { +} + +QString AddressBookDialog::getAddress() const { + return m_ui->m_addressBookView->currentIndex().data(AddressBookModel::ROLE_ADDRESS).toString(); +} + +} diff --git a/src/gui/AddressBookDialog.h b/src/gui/AddressBookDialog.h new file mode 100644 index 0000000..d9aba9f --- /dev/null +++ b/src/gui/AddressBookDialog.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class AddressBookDialog; +} + +namespace WalletGui { + +class AddressBookDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(AddressBookDialog) + +public: + AddressBookDialog(QWidget* _parent); + ~AddressBookDialog(); + + QString getAddress() const; + +private: + QScopedPointer m_ui; +}; + +} diff --git a/src/gui/AddressBookFrame.cpp b/src/gui/AddressBookFrame.cpp new file mode 100644 index 0000000..ca612aa --- /dev/null +++ b/src/gui/AddressBookFrame.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include + +#include "CurrencyAdapter.h" +#include "AddressBookModel.h" +#include "AddressBookFrame.h" +#include "MainWindow.h" +#include "NewAddressDialog.h" +#include "WalletEvents.h" + +#include "ui_addressbookframe.h" + +namespace WalletGui { + +AddressBookFrame::AddressBookFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::AddressBookFrame) { + m_ui->setupUi(this); + m_ui->m_addressBookView->setModel(&AddressBookModel::instance()); + + connect(m_ui->m_addressBookView->selectionModel(), &QItemSelectionModel::currentChanged, this, &AddressBookFrame::currentAddressChanged); +} + +AddressBookFrame::~AddressBookFrame() { +} + +void AddressBookFrame::addClicked() { + NewAddressDialog dlg(&MainWindow::instance()); + if (dlg.exec() == QDialog::Accepted) { + QString label = dlg.getLabel(); + QString address = dlg.getAddress(); + if (!CurrencyAdapter::instance().validateAddress(address)) { + QCoreApplication::postEvent(&MainWindow::instance(), new ShowMessageEvent(tr("Invalid address"), QtCriticalMsg)); + return; + } + + AddressBookModel::instance().addAddress(label, address); + } +} + +void AddressBookFrame::copyClicked() { + QApplication::clipboard()->setText(m_ui->m_addressBookView->currentIndex().data(AddressBookModel::ROLE_ADDRESS).toString()); +} + +void AddressBookFrame::deleteClicked() { + int row = m_ui->m_addressBookView->currentIndex().row(); + AddressBookModel::instance().removeAddress(row); +} + +void AddressBookFrame::currentAddressChanged(const QModelIndex& _index) { + m_ui->m_copyAddressButton->setEnabled(_index.isValid()); + m_ui->m_deleteAddressButton->setEnabled(_index.isValid()); +} + +} diff --git a/src/gui/AddressBookFrame.h b/src/gui/AddressBookFrame.h new file mode 100644 index 0000000..444d733 --- /dev/null +++ b/src/gui/AddressBookFrame.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class AddressBookFrame; +} + +namespace WalletGui { + +class AddressBookFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(AddressBookFrame) + +public: + AddressBookFrame(QWidget* _parent); + ~AddressBookFrame(); + +private: + QScopedPointer m_ui; + + Q_SLOT void addClicked(); + Q_SLOT void copyClicked(); + Q_SLOT void deleteClicked(); + Q_SLOT void currentAddressChanged(const QModelIndex& _index); +}; + +} diff --git a/src/gui/AddressBookModel.cpp b/src/gui/AddressBookModel.cpp new file mode 100644 index 0000000..7f9d457 --- /dev/null +++ b/src/gui/AddressBookModel.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include + +#include "WalletAdapter.h" +#include "AddressBookModel.h" +#include "Settings.h" + +namespace WalletGui { + +AddressBookModel& AddressBookModel::instance() { + static AddressBookModel inst; + return inst; +} + +AddressBookModel::AddressBookModel() : QAbstractItemModel() { + connect(&WalletAdapter::instance(), &WalletAdapter::walletInitCompletedSignal, this, &AddressBookModel::walletInitCompleted, Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletCloseCompletedSignal, this, &AddressBookModel::reset, Qt::QueuedConnection); +} + +AddressBookModel::~AddressBookModel() { +} + +int AddressBookModel::columnCount(const QModelIndex& _parent) const { + return 2; +} + +QVariant AddressBookModel::data(const QModelIndex& _index, int _role) const { + if (!_index.isValid()) { + return QVariant(); + } + + QJsonObject address = m_addressBook.at(_index.row()).toObject(); + + switch (_role) { + case Qt::DisplayRole: + switch (_index.column()) { + case COLUMN_LABEL: + return _index.data(ROLE_LABEL); + case COLUMN_ADDRESS: + return _index.data(ROLE_ADDRESS); + default: + return QVariant(); + } + + case ROLE_LABEL: + return address.value("label"); + case ROLE_ADDRESS: + return address.value("address"); + default: + return QVariant(); + } + + return QVariant(); +} + +Qt::ItemFlags AddressBookModel::flags(const QModelIndex& _index) const { + return (Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable); +} + +QVariant AddressBookModel::headerData(int _section, Qt::Orientation _orientation, int _role) const { + if (_orientation != Qt::Horizontal || _role != Qt::DisplayRole) { + return QVariant(); + } + + switch (_section) { + case COLUMN_LABEL: + return tr("Label"); + case COLUMN_ADDRESS: + return tr("Address"); + } + + return QVariant(); +} + +QModelIndex AddressBookModel::index(int _row, int _column, const QModelIndex& _parent) const { + if (_parent.isValid()) { + return QModelIndex(); + } + + return createIndex(_row, _column, _row); +} + +QModelIndex AddressBookModel::parent(const QModelIndex& _index) const { + return QModelIndex(); +} + +int AddressBookModel::rowCount(const QModelIndex& _parent) const { + return m_addressBook.size(); +} + +void AddressBookModel::addAddress(const QString& _label, const QString& _address) { + beginInsertRows(QModelIndex(), m_addressBook.size(), m_addressBook.size()); + QJsonObject newAddress; + newAddress.insert("label", _label); + newAddress.insert("address", _address); + m_addressBook.append(newAddress); + endInsertRows(); + saveAddressBook(); +} + +void AddressBookModel::removeAddress(quint32 _row) { + if (_row > m_addressBook.size() - 1) { + return; + } + + beginRemoveRows(QModelIndex(), _row, _row); + m_addressBook.removeAt(_row); + endRemoveRows(); + saveAddressBook(); +} + +void AddressBookModel::reset() { + beginResetModel(); + while (!m_addressBook.empty()) { + m_addressBook.removeFirst(); + } + + endResetModel(); +} + +void AddressBookModel::saveAddressBook() { + QFile addressBookFile(Settings::instance().getAddressBookFile()); + if (addressBookFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QByteArray file_content = QJsonDocument(m_addressBook).toJson(QJsonDocument::Compact); + addressBookFile.write(file_content); + addressBookFile.close(); + } +} + +void AddressBookModel::walletInitCompleted(int _error, const QString& _error_text) { + if (!_error) { + QFile addressBookFile(Settings::instance().getAddressBookFile()); + if (addressBookFile.open(QIODevice::ReadOnly)) { + QByteArray file_content = addressBookFile.readAll(); + QJsonDocument doc = QJsonDocument::fromJson(file_content); + if (!doc.isNull()) { + m_addressBook = doc.array(); + } + + addressBookFile.close(); + if (!m_addressBook.isEmpty()) { + beginInsertRows(QModelIndex(), 0, m_addressBook.size() - 1); + endInsertRows(); + } + } + } +} + +} diff --git a/src/gui/AddressBookModel.h b/src/gui/AddressBookModel.h new file mode 100644 index 0000000..a07a3ab --- /dev/null +++ b/src/gui/AddressBookModel.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +namespace WalletGui { + +class AddressBookModel : public QAbstractItemModel +{ + Q_OBJECT + Q_DISABLE_COPY(AddressBookModel) + +public: + enum Columns {COLUMN_LABEL = 0, COLUMN_ADDRESS}; + enum Roles { ROLE_LABEL = Qt::UserRole, ROLE_ADDRESS }; + + static AddressBookModel& instance(); + int columnCount(const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex& _index, int _role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + Qt::ItemFlags flags(const QModelIndex& _index) const Q_DECL_OVERRIDE; + QVariant headerData(int _section, Qt::Orientation _orientation, int _role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int _row, int _column, const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex& _index) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + + void addAddress(const QString& _label, const QString& _address); + void removeAddress(quint32 _row); + +private: + QJsonArray m_addressBook; + + AddressBookModel(); + ~AddressBookModel(); + + void reset(); + void saveAddressBook(); + void walletInitCompleted(int _error, const QString& _error_text); +}; + +} diff --git a/src/gui/AnimatedLabel.cpp b/src/gui/AnimatedLabel.cpp new file mode 100644 index 0000000..88b919f --- /dev/null +++ b/src/gui/AnimatedLabel.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "AnimatedLabel.h" + +namespace WalletGui { + +AnimatedLabel::AnimatedLabel(QWidget* _parent) : QLabel(_parent), m_spriteVerticalSpace(0) { + connect(&m_animationTimer, &QTimer::timeout, this, &AnimatedLabel::timeout); +} + +AnimatedLabel::~AnimatedLabel() { +} + +void AnimatedLabel::setSprite(const QPixmap& _spritePixmap, const QSize& _frameSize, quint32 _verticalSpace, quint32 _frequency) { + m_spritePixmap = _spritePixmap; + m_spriteFrameSize = _frameSize; + m_spriteVerticalSpace = _verticalSpace; + m_animationTimer.setInterval(1000 / _frequency); + m_frameRect.setTopLeft(QPoint(0, 0)); + m_frameRect.setBottomRight(m_frameRect.topLeft() += QPoint(_frameSize.width(), _frameSize.height())); +} + +void AnimatedLabel::startAnimation() { + if (m_animationTimer.isActive()) { + return; + } + + m_animationTimer.start(); +} + +void AnimatedLabel::stopAnimation() { + m_animationTimer.stop(); +} + +void AnimatedLabel::timeout() { + setPixmap(m_spritePixmap.copy(m_frameRect)); + m_frameRect.translate(QPoint(0, m_spriteVerticalSpace + m_spriteFrameSize.height())); + if (m_frameRect.bottom() >= m_spritePixmap.height()) { + m_frameRect.moveTop(0); + } +} + +} diff --git a/src/gui/AnimatedLabel.h b/src/gui/AnimatedLabel.h new file mode 100644 index 0000000..42bd642 --- /dev/null +++ b/src/gui/AnimatedLabel.h @@ -0,0 +1,35 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +namespace WalletGui { + +class AnimatedLabel : public QLabel { + Q_OBJECT + Q_DISABLE_COPY(AnimatedLabel) + +public: + AnimatedLabel(QWidget* _parent); + ~AnimatedLabel(); + + void setSprite(const QPixmap& _sprite_pixmap, const QSize& _frame_size, quint32 _vertical_space, quint32 _frequency); + void startAnimation(); + void stopAnimation(); + +private: + QPixmap m_spritePixmap; + QSize m_spriteFrameSize; + quint32 m_spriteVerticalSpace; + QTimer m_animationTimer; + QRect m_frameRect; + + void timeout(); +}; + +} diff --git a/src/gui/ChangePasswordDialog.cpp b/src/gui/ChangePasswordDialog.cpp new file mode 100644 index 0000000..a4267e0 --- /dev/null +++ b/src/gui/ChangePasswordDialog.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "ChangePasswordDialog.h" + +#include "ui_changepassworddialog.h" + +namespace WalletGui { + +ChangePasswordDialog::ChangePasswordDialog(QWidget* _parent) : QDialog(_parent), m_ui(new Ui::ChangePasswordDialog) { + m_ui->setupUi(this); + m_ui->m_errorLabel->setText(""); +} + +ChangePasswordDialog::~ChangePasswordDialog() { +} + +QString ChangePasswordDialog::getNewPassword() const { + return m_ui->m_newPasswordEdit->text(); +} + +QString ChangePasswordDialog::getOldPassword() const { + return m_ui->m_oldPasswordEdit->text(); +} + +void ChangePasswordDialog::checkPassword(const QString& _password) { + bool passwordIsConfirmed = !(m_ui->m_newPasswordEdit->text().trimmed().isEmpty() || + m_ui->m_newPasswordConfirmationEdit->text().trimmed().isEmpty() || + m_ui->m_newPasswordEdit->text().compare(m_ui->m_newPasswordConfirmationEdit->text())); + m_ui->m_errorLabel->setText(passwordIsConfirmed ? "" : tr("Password not confirmed")); + m_ui->m_okButton->setEnabled(passwordIsConfirmed); +} + +} diff --git a/src/gui/ChangePasswordDialog.h b/src/gui/ChangePasswordDialog.h new file mode 100644 index 0000000..8e83d41 --- /dev/null +++ b/src/gui/ChangePasswordDialog.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class ChangePasswordDialog; +} + +namespace WalletGui { + +class ChangePasswordDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(ChangePasswordDialog) + +public: + ChangePasswordDialog(QWidget* _parent); + ~ChangePasswordDialog(); + + QString getNewPassword() const; + QString getOldPassword() const; + +private: + QScopedPointer m_ui; + + Q_SLOT void checkPassword(const QString& _password); +}; + +} diff --git a/src/gui/ExitWidget.cpp b/src/gui/ExitWidget.cpp new file mode 100644 index 0000000..6395905 --- /dev/null +++ b/src/gui/ExitWidget.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include "CurrencyAdapter.h" +#include "ExitWidget.h" + +#include "ui_exitwidget.h" + +namespace WalletGui { + +ExitWidget::ExitWidget(QWidget* _parent) : QWidget(_parent, Qt::Window), m_ui(new Ui::ExitWidget), + m_clockMovie(new QMovie(this)) { + m_ui->setupUi(this); + QString text = m_ui->m_label->text(); + m_ui->m_label->setText(text.arg(CurrencyAdapter::instance().getCurrencyDisplayName())); + m_clockMovie->setFileName(":images/clock"); + m_clockMovie->setScaledSize(QSize(48, 48)); + m_ui->m_clockLabel->setMovie(m_clockMovie); + m_clockMovie->start(); +} + +ExitWidget::~ExitWidget() { +} + +} diff --git a/src/gui/ExitWidget.h b/src/gui/ExitWidget.h new file mode 100644 index 0000000..dbe75b2 --- /dev/null +++ b/src/gui/ExitWidget.h @@ -0,0 +1,31 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +class QMovie; + +namespace Ui { +class ExitWidget; +} + +namespace WalletGui { + +class ExitWidget : public QWidget { + Q_OBJECT + Q_DISABLE_COPY(ExitWidget) + +public: + ExitWidget(QWidget* _parent); + ~ExitWidget(); + +private: + QScopedPointer m_ui; + QMovie* m_clockMovie; +}; + +} diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp new file mode 100644 index 0000000..0bbb18f --- /dev/null +++ b/src/gui/MainWindow.cpp @@ -0,0 +1,413 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include +#include +#include + +#include + +#include "AboutDialog.h" +#include "AnimatedLabel.h" +#include "ChangePasswordDialog.h" +#include "CurrencyAdapter.h" +#include "ExitWidget.h" +#include "MainWindow.h" +#include "NewPasswordDialog.h" +#include "NodeAdapter.h" +#include "PasswordDialog.h" +#include "Settings.h" +#include "WalletAdapter.h" +#include "WalletEvents.h" + +#include "ui_mainwindow.h" + +namespace WalletGui { + +MainWindow* MainWindow::m_instance = nullptr; + +MainWindow& MainWindow::instance() { + if (m_instance == nullptr) { + m_instance = new MainWindow; + } + + return *m_instance; +} + +MainWindow::MainWindow() : QMainWindow(), m_ui(new Ui::MainWindow), m_trayIcon(nullptr), m_tabActionGroup(new QActionGroup(this)), + m_isAboutToQuit(false) { + m_ui->setupUi(this); + m_connectionStateIconLabel = new QLabel(this); + m_encryptionStateIconLabel = new QLabel(this); + m_synchronizationStateIconLabel = new AnimatedLabel(this); + + connectToSignals(); + initUi(); + + walletClosed(); +} + +MainWindow::~MainWindow() { +} + +void MainWindow::connectToSignals() { + connect(&WalletAdapter::instance(), &WalletAdapter::openWalletWithPasswordSignal, this, &MainWindow::askForWalletPassword, Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::changeWalletPasswordSignal, this, &MainWindow::encryptWallet, Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletSynchronizationProgressUpdatedSignal, + this, &MainWindow::walletSynchronizationInProgress, Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletSynchronizationCompletedSignal, this, &MainWindow::walletSynchronized + , Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletStateChangedSignal, this, &MainWindow::setStatusBarText); + connect(&WalletAdapter::instance(), &WalletAdapter::walletInitCompletedSignal, this, &MainWindow::walletOpened); + connect(&WalletAdapter::instance(), &WalletAdapter::walletCloseCompletedSignal, this, &MainWindow::walletClosed); + connect(&NodeAdapter::instance(), &NodeAdapter::peerCountUpdatedSignal, this, &MainWindow::peerCountUpdated, Qt::QueuedConnection); + connect(m_ui->m_exitAction, &QAction::triggered, qApp, &QApplication::quit); +} + +void MainWindow::initUi() { + setWindowTitle(QString("%1 Wallet %2").arg(CurrencyAdapter::instance().getCurrencyDisplayName()).arg(Settings::instance().getVersion())); +#ifdef Q_OS_WIN32 + if (QSystemTrayIcon::isSystemTrayAvailable()) { + m_trayIcon = new QSystemTrayIcon(QPixmap(":images/cryptonote"), this); + connect(m_trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayActivated); + } +#endif + m_ui->m_aboutCryptonoteAction->setText(QString(tr("About %1 Wallet")).arg(CurrencyAdapter::instance().getCurrencyDisplayName())); + + m_ui->m_overviewFrame->hide(); + m_ui->m_sendFrame->hide(); + m_ui->m_receiveFrame->hide(); + m_ui->m_transactionsFrame->hide(); + m_ui->m_addressBookFrame->hide(); + + m_tabActionGroup->addAction(m_ui->m_overviewAction); + m_tabActionGroup->addAction(m_ui->m_sendAction); + m_tabActionGroup->addAction(m_ui->m_receiveAction); + m_tabActionGroup->addAction(m_ui->m_transactionsAction); + m_tabActionGroup->addAction(m_ui->m_addressBookAction); + + m_ui->m_overviewAction->toggle(); + encryptedFlagChanged(false); + statusBar()->addPermanentWidget(m_connectionStateIconLabel); + statusBar()->addPermanentWidget(m_encryptionStateIconLabel); + statusBar()->addPermanentWidget(m_synchronizationStateIconLabel); + qobject_cast(m_synchronizationStateIconLabel)->setSprite(QPixmap(":icons/sync_sprite"), QSize(16, 16), 5, 24); + m_connectionStateIconLabel->setPixmap(QPixmap(":icons/disconnected").scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + +#ifdef Q_OS_MAC + installDockHandler(); +#endif + +#ifndef Q_OS_WIN + m_ui->m_minimizeToTrayAction->deleteLater(); + m_ui->m_closeToTrayAction->deleteLater(); +#endif +} + +#ifdef Q_OS_WIN +void MainWindow::minimizeToTray(bool _on) { + if (_on) { + hide(); + m_trayIcon->show(); + } else { + showNormal(); + activateWindow(); + m_trayIcon->hide(); + } +} +#endif + +void MainWindow::scrollToTransaction(const QModelIndex& _index) { + m_ui->m_transactionsAction->setChecked(true); + m_ui->m_transactionsFrame->scrollToTransaction(_index); +} + +void MainWindow::quit() { + if (!m_isAboutToQuit) { + ExitWidget* exitWidget = new ExitWidget(nullptr); + exitWidget->show(); + m_isAboutToQuit = true; + close(); + } +} + +#ifdef Q_OS_MAC +void MainWindow::restoreFromDock() { + if (m_isAboutToQuit) { + return; + } + + showNormal(); +} +#endif + +void MainWindow::closeEvent(QCloseEvent* _event) { +#ifdef Q_OS_WIN + if (m_isAboutToQuit) { + QMainWindow::closeEvent(_event); + return; + } else if (Settings::instance().isCloseToTrayEnabled()) { + minimizeToTray(true); + _event->ignore(); + } else { + QApplication::quit(); + return; + } +#elif defined(Q_OS_LINUX) + if (!m_isAboutToQuit) { + QApplication::quit(); + return; + } +#endif + QMainWindow::closeEvent(_event); + +} + +#ifdef Q_OS_WIN +void MainWindow::changeEvent(QEvent* _event) { + QMainWindow::changeEvent(_event); + if (!QSystemTrayIcon::isSystemTrayAvailable()) { + QMainWindow::changeEvent(_event); + return; + } + + switch (_event->type()) { + case QEvent::WindowStateChange: + if(Settings::instance().isMinimizeToTrayEnabled()) { + minimizeToTray(isMinimized()); + } + break; + default: + break; + } + + QMainWindow::changeEvent(_event); +} +#endif + +bool MainWindow::event(QEvent* _event) { + switch (static_cast(_event->type())) { + case WalletEventType::ShowMessage: + showMessage(static_cast(_event)->messageText(), static_cast(_event)->messageType()); + return true; + } + + return QMainWindow::event(_event); +} + +void MainWindow::createWallet() { + QString filePath = QFileDialog::getSaveFileName(this, tr("New wallet file"), + #ifdef Q_OS_WIN + QApplication::applicationDirPath(), + #else + QDir::homePath(), + #endif + tr("Wallets (*.wallet)") + ); + + if (!filePath.isEmpty() && !filePath.endsWith(".wallet")) { + filePath.append(".wallet"); + } + + if (!filePath.isEmpty() && !QFile::exists(filePath)) { + if (WalletAdapter::instance().isOpen()) { + WalletAdapter::instance().close(); + } + + WalletAdapter::instance().setWalletFile(filePath); + WalletAdapter::instance().open(""); + } +} + +void MainWindow::openWallet() { + QString filePath = QFileDialog::getOpenFileName(this, tr("Open .wallet/.keys file"), +#ifdef Q_OS_WIN + QApplication::applicationDirPath(), +#else + QDir::homePath(), +#endif + tr("Wallet (*.wallet *.keys)")); + + if (!filePath.isEmpty()) { + if (WalletAdapter::instance().isOpen()) { + WalletAdapter::instance().close(); + } + + WalletAdapter::instance().setWalletFile(filePath); + WalletAdapter::instance().open(""); + } +} + +void MainWindow::backupWallet() { + QString filePath = QFileDialog::getSaveFileName(this, tr("Backup wallet to..."), + #ifdef Q_OS_WIN + QApplication::applicationDirPath(), + #else + QDir::homePath(), + #endif + tr("Wallets (*.wallet)") + ); + if (!filePath.isEmpty() && !filePath.endsWith(".wallet")) { + filePath.append(".wallet"); + } + + if (!filePath.isEmpty() && !QFile::exists(filePath)) { + WalletAdapter::instance().backup(filePath); + } +} + +void MainWindow::encryptWallet() { + if (Settings::instance().isEncrypted()) { + bool error = false; + do { + ChangePasswordDialog dlg(this); + if (dlg.exec() == QDialog::Rejected) { + return; + } + + QString oldPassword = dlg.getOldPassword(); + QString newPassword = dlg.getNewPassword(); + error = !WalletAdapter::instance().changePassword(oldPassword, newPassword); + } while (error); + } else { + NewPasswordDialog dlg(this); + if (dlg.exec() == QDialog::Accepted) { + QString password = dlg.getPassword(); + if (password.isEmpty()) { + return; + } + + encryptedFlagChanged(WalletAdapter::instance().changePassword("", password)); + } + } +} + +void MainWindow::aboutQt() { + QMessageBox::aboutQt(this); +} + +void MainWindow::setStartOnLogin(bool _on) { + Settings::instance().setStartOnLoginEnabled(_on); + m_ui->m_startOnLoginAction->setChecked(Settings::instance().isStartOnLoginEnabled()); +} + +void MainWindow::setMinimizeToTray(bool _on) { +#ifdef Q_OS_WIN + Settings::instance().setMinimizeToTrayEnabled(_on); +#endif +} + +void MainWindow::setCloseToTray(bool _on) { +#ifdef Q_OS_WIN + Settings::instance().setCloseToTrayEnabled(_on); +#endif +} + +void MainWindow::about() { + AboutDialog dlg(this); + dlg.exec(); +} + +void MainWindow::setStatusBarText(const QString& _text) { + statusBar()->showMessage(_text); +} + +void MainWindow::showMessage(const QString& _text, QtMsgType _type) { + switch (_type) { + case QtCriticalMsg: + QMessageBox::critical(this, tr("Wallet error"), _text); + break; + case QtDebugMsg: + QMessageBox::information(this, tr("Wallet"), _text); + break; + default: + break; + } +} + +void MainWindow::askForWalletPassword(bool _error) { + PasswordDialog dlg(_error, this); + if (dlg.exec() == QDialog::Accepted) { + QString password = dlg.getPassword(); + WalletAdapter::instance().open(password); + } +} + +void MainWindow::encryptedFlagChanged(bool _encrypted) { + m_ui->m_encryptWalletAction->setEnabled(!_encrypted); + m_ui->m_changePasswordAction->setEnabled(_encrypted); + QString encryptionIconPath = _encrypted ? ":icons/encrypted" : ":icons/decrypted"; + QPixmap encryptionIcon = QPixmap(encryptionIconPath).scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_encryptionStateIconLabel->setPixmap(encryptionIcon); + QString encryptionLabelTooltip = _encrypted ? tr("Encrypted") : tr("Not encrypted"); + m_encryptionStateIconLabel->setToolTip(encryptionLabelTooltip); +} + +void MainWindow::peerCountUpdated(quint64 _peerCount) { + QString connectionIconPath = _peerCount > 0 ? ":icons/connected" : ":icons/disconnected"; + QPixmap connectionIcon = QPixmap(connectionIconPath).scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_connectionStateIconLabel->setPixmap(connectionIcon); + m_connectionStateIconLabel->setToolTip(QString(tr("%1 peers").arg(_peerCount))); +} + +void MainWindow::walletSynchronizationInProgress() { + qobject_cast(m_synchronizationStateIconLabel)->startAnimation(); + m_synchronizationStateIconLabel->setToolTip(tr("Synchronization in progress")); +} + +void MainWindow::walletSynchronized(int _error, const QString& _error_text) { + QPixmap syncIcon = QPixmap(":icons/synced").scaled(16, 16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + qobject_cast(m_synchronizationStateIconLabel)->stopAnimation(); + m_synchronizationStateIconLabel->setPixmap(syncIcon); + QString syncLabelTooltip = _error > 0 ? tr("Not synchronized") : tr("Synchronized"); + m_synchronizationStateIconLabel->setToolTip(syncLabelTooltip); +} + +void MainWindow::walletOpened(bool _error, const QString& _error_text) { + if (!_error) { + m_encryptionStateIconLabel->show(); + m_synchronizationStateIconLabel->show(); + m_ui->m_backupWalletAction->setEnabled(true); + encryptedFlagChanged(Settings::instance().isEncrypted()); + + QList tabActions = m_tabActionGroup->actions(); + Q_FOREACH(auto action, tabActions) { + action->setEnabled(true); + } + + m_ui->m_overviewAction->trigger(); + m_ui->m_overviewFrame->show(); + } else { + walletClosed(); + } +} + +void MainWindow::walletClosed() { + m_ui->m_backupWalletAction->setEnabled(false); + m_ui->m_encryptWalletAction->setEnabled(false); + m_ui->m_changePasswordAction->setEnabled(false); + m_ui->m_overviewFrame->hide(); + m_ui->m_sendFrame->hide(); + m_ui->m_transactionsFrame->hide(); + m_ui->m_addressBookFrame->hide(); + m_encryptionStateIconLabel->hide(); + m_synchronizationStateIconLabel->hide(); + QList tabActions = m_tabActionGroup->actions(); + Q_FOREACH(auto action, tabActions) { + action->setEnabled(false); + } +} + +#ifdef Q_OS_WIN +void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason _reason) { + showNormal(); + m_trayIcon->hide(); +} +#endif + +} diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h new file mode 100644 index 0000000..0ae5574 --- /dev/null +++ b/src/gui/MainWindow.h @@ -0,0 +1,87 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include +#include +#include + +class QActionGroup; + +namespace Ui { +class MainWindow; +} + +namespace WalletGui { + +class MainWindow : public QMainWindow { + Q_OBJECT + Q_DISABLE_COPY(MainWindow) + +public: + static MainWindow& instance(); + void scrollToTransaction(const QModelIndex& _index); + void quit(); + +protected: + void closeEvent(QCloseEvent* _event) Q_DECL_OVERRIDE; + bool event(QEvent* _event) Q_DECL_OVERRIDE; + +private: + QScopedPointer m_ui; + QLabel* m_connectionStateIconLabel; + QLabel* m_encryptionStateIconLabel; + QLabel* m_synchronizationStateIconLabel; + QSystemTrayIcon* m_trayIcon; + QActionGroup* m_tabActionGroup; + bool m_isAboutToQuit; + + static MainWindow* m_instance; + + MainWindow(); + ~MainWindow(); + + void connectToSignals(); + void initUi(); + + void minimizeToTray(bool _on); + void setStatusBarText(const QString& _text); + void showMessage(const QString& _text, QtMsgType _type); + void askForWalletPassword(bool _error); + void encryptedFlagChanged(bool _encrypted); + void peerCountUpdated(quint64 _peer_count); + void walletSynchronizationInProgress(); + void walletSynchronized(int _error, const QString& _error_text); + void walletOpened(bool _error, const QString& _error_text); + void walletClosed(); + + Q_SLOT void createWallet(); + Q_SLOT void openWallet(); + Q_SLOT void backupWallet(); + Q_SLOT void encryptWallet(); + Q_SLOT void aboutQt(); + Q_SLOT void about(); + Q_SLOT void setStartOnLogin(bool _on); + Q_SLOT void setMinimizeToTray(bool _on); + Q_SLOT void setCloseToTray(bool _on); + +#ifdef Q_OS_MAC +public: + void restoreFromDock(); + +private: + void installDockHandler(); +#elif defined(Q_OS_WIN) +protected: + void changeEvent(QEvent* _event) Q_DECL_OVERRIDE; + +private: + void trayActivated(QSystemTrayIcon::ActivationReason _reason); +#endif +}; + +} diff --git a/src/gui/MainWindow.mm b/src/gui/MainWindow.mm new file mode 100644 index 0000000..146ff6e --- /dev/null +++ b/src/gui/MainWindow.mm @@ -0,0 +1,30 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#import +#import + +#include "mainwindow.h" + +namespace WalletGui { + +namespace { + +void dockClickHandler(id self, SEL _cmd) { + Q_UNUSED(self) + Q_UNUSED(_cmd) + MainWindow::instance().restoreFromDock(); +} + +} + +void MainWindow::installDockHandler() { + Class cls = [[[NSApplication sharedApplication] delegate] class]; + if (!class_replaceMethod(cls, @selector(applicationShouldHandleReopen:hasVisibleWindows:), (IMP) dockClickHandler, "v@:")) { + NSLog(@"MainWindow::installDockHandler() : class_addMethod failed!"); + } +} + +} diff --git a/src/gui/NewAddressDialog.cpp b/src/gui/NewAddressDialog.cpp new file mode 100644 index 0000000..084d5cb --- /dev/null +++ b/src/gui/NewAddressDialog.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "NewAddressDialog.h" + +#include "ui_newaddressdialog.h" + +namespace WalletGui { + +NewAddressDialog::NewAddressDialog(QWidget* _parent) : QDialog(_parent), m_ui(new Ui::NewAddressDialog) { + m_ui->setupUi(this); +} + +NewAddressDialog::~NewAddressDialog() { +} + +QString NewAddressDialog::getAddress() const { + return m_ui->m_addressEdit->text(); +} + + +QString NewAddressDialog::getLabel() const { + return m_ui->m_labelEdit->text(); +} + +} diff --git a/src/gui/NewAddressDialog.h b/src/gui/NewAddressDialog.h new file mode 100644 index 0000000..152c377 --- /dev/null +++ b/src/gui/NewAddressDialog.h @@ -0,0 +1,31 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class NewAddressDialog; +} + +namespace WalletGui { + +class NewAddressDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(NewAddressDialog) + +public: + NewAddressDialog(QWidget* _parent); + ~NewAddressDialog(); + + QString getAddress() const; + QString getLabel() const; + +private: + QScopedPointer m_ui; +}; + +} diff --git a/src/gui/NewPasswordDialog.cpp b/src/gui/NewPasswordDialog.cpp new file mode 100644 index 0000000..f169301 --- /dev/null +++ b/src/gui/NewPasswordDialog.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "NewPasswordDialog.h" + +#include "ui_newpassworddialog.h" + +namespace WalletGui { + +NewPasswordDialog::NewPasswordDialog(QWidget* _parent) : QDialog(_parent), m_ui(new Ui::NewPasswordDialog) { + m_ui->setupUi(this); + m_ui->m_errorLabel->setText(""); +} + +NewPasswordDialog::~NewPasswordDialog() { +} + +QString NewPasswordDialog::getPassword() const { + return m_ui->m_passwordEdit->text(); +} + +void NewPasswordDialog::checkPassword(const QString& _password) { + bool passwordIsConfirmed = !(m_ui->m_passwordEdit->text().trimmed().isEmpty() || + m_ui->m_passwordConfirmationEdit->text().trimmed().isEmpty() || + m_ui->m_passwordEdit->text().compare(m_ui->m_passwordConfirmationEdit->text())); + m_ui->m_errorLabel->setText(passwordIsConfirmed ? "" : tr("Password not confirmed")); + m_ui->m_okButton->setEnabled(passwordIsConfirmed); +} + +} diff --git a/src/gui/NewPasswordDialog.h b/src/gui/NewPasswordDialog.h new file mode 100644 index 0000000..f84e907 --- /dev/null +++ b/src/gui/NewPasswordDialog.h @@ -0,0 +1,32 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class NewPasswordDialog; +} + +namespace WalletGui { + +class NewPasswordDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(NewPasswordDialog) + +public: + NewPasswordDialog(QWidget* _parent); + ~NewPasswordDialog(); + + QString getPassword() const; + +private: + QScopedPointer m_ui; + + Q_SLOT void checkPassword(const QString& _password); +}; + +} diff --git a/src/gui/OverviewFrame.cpp b/src/gui/OverviewFrame.cpp new file mode 100644 index 0000000..0141677 --- /dev/null +++ b/src/gui/OverviewFrame.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "CurrencyAdapter.h" +#include "OverviewFrame.h" +#include "TransactionFrame.h" +#include "RecentTransactionsModel.h" +#include "WalletAdapter.h" + +#include "ui_overviewframe.h" + +namespace WalletGui { + +class RecentTransactionsDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + RecentTransactionsDelegate(QObject* _parent) : QStyledItemDelegate(_parent) { + } + + ~RecentTransactionsDelegate() { + } + + QWidget* createEditor(QWidget* _parent, const QStyleOptionViewItem& _option, const QModelIndex& _index) const Q_DECL_OVERRIDE { + if (!_index.isValid()) { + return nullptr; + } + + return new TransactionFrame(_index, _parent); + } + + QSize sizeHint(const QStyleOptionViewItem& _option, const QModelIndex& _index) const Q_DECL_OVERRIDE { + return QSize(346, 64); + } +}; + +OverviewFrame::OverviewFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::OverviewFrame), m_transactionModel(new RecentTransactionsModel) { + m_ui->setupUi(this); + connect(&WalletAdapter::instance(), &WalletAdapter::walletActualBalanceUpdatedSignal, this, &OverviewFrame::updateActualBalance, + Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletPendingBalanceUpdatedSignal, this, &OverviewFrame::updatePendingBalance, + Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletCloseCompletedSignal, this, &OverviewFrame::reset, + Qt::QueuedConnection); + connect(m_transactionModel.data(), &QAbstractItemModel::rowsInserted, this, &OverviewFrame::transactionsInserted); + connect(m_transactionModel.data(), &QAbstractItemModel::layoutChanged, this, &OverviewFrame::layoutChanged); + + m_ui->m_tickerLabel1->setText(CurrencyAdapter::instance().getCurrencyTicker().toUpper()); + m_ui->m_tickerLabel2->setText(CurrencyAdapter::instance().getCurrencyTicker().toUpper()); + m_ui->m_tickerLabel3->setText(CurrencyAdapter::instance().getCurrencyTicker().toUpper()); + + m_ui->m_recentTransactionsView->setItemDelegate(new RecentTransactionsDelegate(this)); + m_ui->m_recentTransactionsView->setModel(m_transactionModel.data()); + reset(); +} + +OverviewFrame::~OverviewFrame() { +} + +void OverviewFrame::transactionsInserted(const QModelIndex& _parent, int _first, int _last) { + for (quint32 i = _first; i <= _last; ++i) { + QModelIndex recentModelIndex = m_transactionModel->index(i, 0); + m_ui->m_recentTransactionsView->openPersistentEditor(recentModelIndex); + } +} + +void OverviewFrame::layoutChanged() { + for (quint32 i = 0; i <= m_transactionModel->rowCount(); ++i) { + QModelIndex recent_index = m_transactionModel->index(i, 0); + m_ui->m_recentTransactionsView->openPersistentEditor(recent_index); + } +} + +void OverviewFrame::updateActualBalance(quint64 _balance) { + m_ui->m_actualBalanceLabel->setText(CurrencyAdapter::instance().formatAmount(_balance)); + quint64 pendingBalance = WalletAdapter::instance().getPendingBalance(); + m_ui->m_totalBalanceLabel->setText(CurrencyAdapter::instance().formatAmount(_balance + pendingBalance)); +} + +void OverviewFrame::updatePendingBalance(quint64 _balance) { + m_ui->m_pendingBalanceLabel->setText(CurrencyAdapter::instance().formatAmount(_balance)); + quint64 actualBalance = WalletAdapter::instance().getActualBalance(); + m_ui->m_totalBalanceLabel->setText(CurrencyAdapter::instance().formatAmount(_balance + actualBalance)); +} + +void OverviewFrame::reset() { + updateActualBalance(0); + updatePendingBalance(0); +} + +} + +#include "OverviewFrame.moc" diff --git a/src/gui/OverviewFrame.h b/src/gui/OverviewFrame.h new file mode 100644 index 0000000..e44456b --- /dev/null +++ b/src/gui/OverviewFrame.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +namespace Ui { +class OverviewFrame; +} + +namespace WalletGui { + +class RecentTransactionsModel; + +class OverviewFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(OverviewFrame) + +public: + OverviewFrame(QWidget* _parent); + ~OverviewFrame(); + +private: + QScopedPointer m_ui; + QSharedPointer m_transactionModel; + + void transactionsInserted(const QModelIndex& _parent, int _first, int _last); + void transactionsRemoved(const QModelIndex& _parent, int _first, int _last); + void layoutChanged(); + void updateActualBalance(quint64 _balance); + void updatePendingBalance(quint64 _balance); + void reset(); +}; + +} diff --git a/src/gui/PasswordDialog.cpp b/src/gui/PasswordDialog.cpp new file mode 100644 index 0000000..bd1d982 --- /dev/null +++ b/src/gui/PasswordDialog.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "PasswordDialog.h" + +#include "ui_passworddialog.h" + +namespace WalletGui { + +PasswordDialog::PasswordDialog(bool _error, QWidget* _parent) : QDialog(_parent), m_ui(new Ui::PasswordDialog) { + m_ui->setupUi(this); + if (!_error) { + m_ui->m_errorLabel->hide(); + } + + adjustSize(); +} + +PasswordDialog::~PasswordDialog() { +} + +QString PasswordDialog::getPassword() const { + return m_ui->m_passwordEdit->text(); +} + +} diff --git a/src/gui/PasswordDialog.h b/src/gui/PasswordDialog.h new file mode 100644 index 0000000..ee2c68c --- /dev/null +++ b/src/gui/PasswordDialog.h @@ -0,0 +1,30 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class PasswordDialog; +} + +namespace WalletGui { + +class PasswordDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(PasswordDialog) + +public: + PasswordDialog(bool _error, QWidget* _parent); + ~PasswordDialog(); + + QString getPassword() const; + +private: + QScopedPointer m_ui; +}; + +} diff --git a/src/gui/ReceiveFrame.cpp b/src/gui/ReceiveFrame.cpp new file mode 100644 index 0000000..b167cca --- /dev/null +++ b/src/gui/ReceiveFrame.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include "ReceiveFrame.h" +#include "WalletAdapter.h" + +#include "ui_receiveframe.h" + +namespace WalletGui { + +ReceiveFrame::ReceiveFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::ReceiveFrame) { + m_ui->setupUi(this); + connect(&WalletAdapter::instance(), &WalletAdapter::updateWalletAddressSignal, this, &ReceiveFrame::updateWalletAddress); + connect(&WalletAdapter::instance(), &WalletAdapter::walletCloseCompletedSignal, this, &ReceiveFrame::reset); +} + +ReceiveFrame::~ReceiveFrame() { +} + +void ReceiveFrame::updateWalletAddress(const QString& _address) { + m_ui->m_addressEdit->setText(_address); +} + +void ReceiveFrame::reset() { + m_ui->m_addressEdit->clear(); +} + +void ReceiveFrame::copyAddress() { + QApplication::clipboard()->setText(m_ui->m_addressEdit->text()); +} + +} diff --git a/src/gui/ReceiveFrame.h b/src/gui/ReceiveFrame.h new file mode 100644 index 0000000..c407acf --- /dev/null +++ b/src/gui/ReceiveFrame.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class ReceiveFrame; +} + +namespace WalletGui { + +class ReceiveFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(ReceiveFrame) + +public: + ReceiveFrame(QWidget* _parent); + ~ReceiveFrame(); + +private: + QScopedPointer m_ui; + + void updateWalletAddress(const QString& _address); + void reset(); + + Q_SLOT void copyAddress(); +}; + +} diff --git a/src/gui/RecentTransactionsModel.cpp b/src/gui/RecentTransactionsModel.cpp new file mode 100644 index 0000000..1a0992c --- /dev/null +++ b/src/gui/RecentTransactionsModel.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "RecentTransactionsModel.h" +#include "SortedTransactionsModel.h" + +namespace WalletGui { + +RecentTransactionsModel::RecentTransactionsModel() : QSortFilterProxyModel() { + setSourceModel(&SortedTransactionsModel::instance()); + setDynamicSortFilter(true); + connect(sourceModel(), &QAbstractItemModel::rowsInserted, this, &RecentTransactionsModel::invalidateFilter); +} + +RecentTransactionsModel::~RecentTransactionsModel() { +} + +int RecentTransactionsModel::columnCount(const QModelIndex& _parent) const { + return 1; +} + +QVariant RecentTransactionsModel::data(const QModelIndex& _index, int _role) const { + if(_role == Qt::DecorationRole) { + return QVariant(); + } + + return QSortFilterProxyModel::data(_index, _role); +} + +bool RecentTransactionsModel::filterAcceptsRow(int _sourceRow, const QModelIndex& _sourceParent) const { + return _sourceRow < 6; +} + + +} diff --git a/src/gui/RecentTransactionsModel.h b/src/gui/RecentTransactionsModel.h new file mode 100644 index 0000000..7a86cca --- /dev/null +++ b/src/gui/RecentTransactionsModel.h @@ -0,0 +1,25 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace WalletGui { + +class RecentTransactionsModel : public QSortFilterProxyModel { + Q_OBJECT + Q_DISABLE_COPY(RecentTransactionsModel) + +public: + RecentTransactionsModel(); + ~RecentTransactionsModel(); + + int columnCount(const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex& _index, int _role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + bool filterAcceptsRow(int _source_row, const QModelIndex& _source_parent) const Q_DECL_OVERRIDE; +}; + +} diff --git a/src/gui/SendFrame.cpp b/src/gui/SendFrame.cpp new file mode 100644 index 0000000..9ac69ad --- /dev/null +++ b/src/gui/SendFrame.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "AddressBookModel.h" +#include "CurrencyAdapter.h" +#include "MainWindow.h" +#include "NodeAdapter.h" +#include "SendFrame.h" +#include "TransferFrame.h" +#include "WalletAdapter.h" +#include "WalletEvents.h" + +#include "ui_sendframe.h" + +namespace WalletGui { + +const quint64 MINIMAL_FEE = 1000000; + +SendFrame::SendFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::SendFrame) { + m_ui->setupUi(this); + clearAllClicked(); + mixinValueChanged(m_ui->m_mixinSlider->value()); + + connect(&WalletAdapter::instance(), &WalletAdapter::walletSendTransactionCompletedSignal, this, &SendFrame::sendTransactionCompleted, + Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletActualBalanceUpdatedSignal, this, &SendFrame::walletActualBalanceUpdated, + Qt::QueuedConnection); + + m_ui->m_tickerLabel->setText(CurrencyAdapter::instance().getCurrencyTicker().toUpper()); +} + +SendFrame::~SendFrame() { +} + +void SendFrame::addRecipientClicked() { + TransferFrame* newTransfer = new TransferFrame(m_ui->m_transfersScrollarea); + m_ui->m_send_frame_layout->insertWidget(m_transfers.size(), newTransfer); + m_transfers.append(newTransfer); + if (m_transfers.size() == 1) { + newTransfer->disableRemoveButton(true); + } else { + m_transfers[0]->disableRemoveButton(false); + } + + connect(newTransfer, &TransferFrame::destroyed, [this](QObject* _obj) { + m_transfers.removeOne(static_cast(_obj)); + if (m_transfers.size() == 1) { + m_transfers[0]->disableRemoveButton(true); + } + }); +} + +void SendFrame::clearAllClicked() { + Q_FOREACH (TransferFrame* transfer, m_transfers) { + transfer->close(); + } + + m_transfers.clear(); + addRecipientClicked(); + m_ui->m_paymentIdEdit->clear(); + m_ui->m_mixinSlider->setValue(2); +} + +void SendFrame::sendClicked() { + QVector walletTransfers; + Q_FOREACH (TransferFrame * transfer, m_transfers) { + QString address = transfer->getAddress(); + if (!CurrencyAdapter::instance().validateAddress(address)) { + QCoreApplication::postEvent( + &MainWindow::instance(), + new ShowMessageEvent(tr("Invalid recipient address"), QtCriticalMsg)); + return; + } + + CryptoNote::Transfer walletTransfer; + walletTransfer.address = address.toStdString(); + uint64_t amount = CurrencyAdapter::instance().parseAmount(transfer->getAmountString()); + walletTransfer.amount = amount; + walletTransfers.push_back(walletTransfer); + QString label = transfer->getLabel(); + if (!label.isEmpty()) { + AddressBookModel::instance().addAddress(label, address); + } + } + + quint64 fee = MINIMAL_FEE; + if (fee < MINIMAL_FEE) { + QCoreApplication::postEvent(&MainWindow::instance(), new ShowMessageEvent(tr("Minimum allowed fee is 0.01"), QtCriticalMsg)); + return; + } + + if (WalletAdapter::instance().isOpen()) { + WalletAdapter::instance().sendTransaction(walletTransfers, fee, m_ui->m_paymentIdEdit->text(), m_ui->m_mixinSlider->value()); + } +} + +void SendFrame::mixinValueChanged(int _value) { + m_ui->m_mixinEdit->setText(QString::number(_value)); +} + +void SendFrame::sendTransactionCompleted(CryptoNote::TransactionId _id, bool _result, const QString& _errorText) { + Q_UNUSED(_id); + Q_UNUSED(_result); + Q_UNUSED(_errorText); + clearAllClicked(); +} + +void SendFrame::walletActualBalanceUpdated(quint64 _balance) { + m_ui->m_balanceLabel->setText(CurrencyAdapter::instance().formatAmount(_balance)); +} + +} diff --git a/src/gui/SendFrame.h b/src/gui/SendFrame.h new file mode 100644 index 0000000..eceac98 --- /dev/null +++ b/src/gui/SendFrame.h @@ -0,0 +1,41 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +#include + +namespace Ui { + class SendFrame; +} + +namespace WalletGui { + +class TransferFrame; + +class SendFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(SendFrame) + +public: + SendFrame(QWidget* _parent); + ~SendFrame(); + +private: + QScopedPointer m_ui; + QList m_transfers; + + void sendTransactionCompleted(CryptoNote::TransactionId _id, bool _result, const QString& _error_text); + void walletActualBalanceUpdated(quint64 _balance); + + Q_SLOT void addRecipientClicked(); + Q_SLOT void clearAllClicked(); + Q_SLOT void mixinValueChanged(int _value); + Q_SLOT void sendClicked(); +}; + +} diff --git a/src/gui/SortedTransactionsModel.cpp b/src/gui/SortedTransactionsModel.cpp new file mode 100644 index 0000000..47fe856 --- /dev/null +++ b/src/gui/SortedTransactionsModel.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include "SortedTransactionsModel.h" +#include "TransactionsModel.h" + +namespace WalletGui { + +SortedTransactionsModel& SortedTransactionsModel::instance() { + static SortedTransactionsModel inst; + return inst; +} + +SortedTransactionsModel::SortedTransactionsModel() : QSortFilterProxyModel() { + setSourceModel(&TransactionsModel::instance()); + setDynamicSortFilter(true); + sort(TransactionsModel::COLUMN_DATE, Qt::DescendingOrder); +} + +SortedTransactionsModel::~SortedTransactionsModel() { +} + +bool SortedTransactionsModel::lessThan(const QModelIndex& _left, const QModelIndex& _right) const { + QDateTime leftDate = _left.data(TransactionsModel::ROLE_DATE).toDateTime(); + QDateTime rightDate = _right.data(TransactionsModel::ROLE_DATE).toDateTime(); + if((rightDate.isNull() || !rightDate.isValid()) && (leftDate.isNull() || !leftDate.isValid())) { + return _left.row() < _right.row(); + } + + if(leftDate.isNull() || !leftDate.isValid()) { + return false; + } + + if(rightDate.isNull() || !rightDate.isValid()) { + return true; + } + + return leftDate < rightDate; +} + +} diff --git a/src/gui/SortedTransactionsModel.h b/src/gui/SortedTransactionsModel.h new file mode 100644 index 0000000..bd46883 --- /dev/null +++ b/src/gui/SortedTransactionsModel.h @@ -0,0 +1,29 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace WalletGui { + +class SortedTransactionsModel : public QSortFilterProxyModel { + Q_OBJECT + Q_DISABLE_COPY(SortedTransactionsModel) + +public: + static SortedTransactionsModel& instance(); + +protected: + bool lessThan(const QModelIndex& _left, const QModelIndex& _right) const Q_DECL_OVERRIDE; + +private: + SortedTransactionsModel(); + ~SortedTransactionsModel(); +}; + + + +} diff --git a/src/gui/TransactionDetailsDialog.cpp b/src/gui/TransactionDetailsDialog.cpp new file mode 100644 index 0000000..e3c3313 --- /dev/null +++ b/src/gui/TransactionDetailsDialog.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include "CurrencyAdapter.h" +#include "TransactionDetailsDialog.h" +#include "TransactionsModel.h" + +#include "ui_transactiondetailsdialog.h" + +namespace WalletGui { + +TransactionDetailsDialog::TransactionDetailsDialog(const QModelIndex& _index, QWidget* _parent) : QDialog(_parent), + m_ui(new Ui::TransactionDetailsDialog), m_detailsTemplate( + "\n" + "Status: %1


\n" + "Date: %2


\n" + "To: %4


\n" + "Amount: %5


\n" + "Fee: %6


\n" + "Transaction hash: %8

") { + m_ui->setupUi(this); + + QModelIndex index = TransactionsModel::instance().index(_index.data(TransactionsModel::ROLE_ROW).toInt(), + _index.data(TransactionsModel::ROLE_ROW).toInt()); + + quint64 numberOfConfirmations = index.data(TransactionsModel::ROLE_NUMBER_OF_CONFIRMATIONS).value(); + QString amountText = index.sibling(index.row(), TransactionsModel::COLUMN_AMOUNT).data().toString() + " " + + CurrencyAdapter::instance().getCurrencyTicker().toUpper(); + QString feeText = CurrencyAdapter::instance().formatAmount(index.data(TransactionsModel::ROLE_FEE).value()) + " " + + CurrencyAdapter::instance().getCurrencyTicker().toUpper(); + + m_ui->m_detailsBrowser->setHtml(m_detailsTemplate.arg(QString("%1 confirmations").arg(numberOfConfirmations)). + arg(index.sibling(index.row(), TransactionsModel::COLUMN_DATE).data().toString()).arg(index.sibling(index.row(), + TransactionsModel::COLUMN_ADDRESS).data().toString()).arg(amountText).arg(feeText). + arg(index.sibling(index.row(), TransactionsModel::COLUMN_HASH).data().toString())); +} + +TransactionDetailsDialog::~TransactionDetailsDialog() { +} + +} diff --git a/src/gui/TransactionDetailsDialog.h b/src/gui/TransactionDetailsDialog.h new file mode 100644 index 0000000..0d116bb --- /dev/null +++ b/src/gui/TransactionDetailsDialog.h @@ -0,0 +1,29 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class TransactionDetailsDialog; +} + +namespace WalletGui { + +class TransactionDetailsDialog : public QDialog { + Q_OBJECT + Q_DISABLE_COPY(TransactionDetailsDialog) + +public: + TransactionDetailsDialog(const QModelIndex &_index, QWidget* _parent); + ~TransactionDetailsDialog(); + +private: + QScopedPointer m_ui; + const QString m_detailsTemplate; +}; + +} diff --git a/src/gui/TransactionFrame.cpp b/src/gui/TransactionFrame.cpp new file mode 100644 index 0000000..3cf53ed --- /dev/null +++ b/src/gui/TransactionFrame.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include + +#include "MainWindow.h" +#include "TransactionFrame.h" +#include "TransactionsModel.h" + +#include "ui_transactionframe.h" + +namespace WalletGui { + +class RecentTransactionDelegate : public QStyledItemDelegate { + Q_OBJECT + +public: + RecentTransactionDelegate(QObject* _parent) : QStyledItemDelegate(_parent) { + + } + + ~RecentTransactionDelegate() { + } + + void setEditorData(QWidget* _editor, const QModelIndex& _index) const Q_DECL_OVERRIDE { + switch(_index.column()) { + case TransactionsModel::COLUMN_AMOUNT: + case TransactionsModel::COLUMN_HASH: + case TransactionsModel::COLUMN_DATE: + static_cast(_editor)->setText(_index.data().toString()); + return; + case TransactionsModel::COLUMN_TYPE: + static_cast(_editor)->setPixmap(_index.data(TransactionsModel::ROLE_ICON).value()); + return; + default: + return; + } + } +}; + +TransactionFrame::TransactionFrame(const QModelIndex& _index, QWidget* _parent) : QFrame(_parent), + m_ui(new Ui::TransactionFrame), m_dataMapper(this), m_index(_index) { + m_ui->setupUi(this); + QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + font.setPixelSize(11); + m_ui->m_hashLabel->setFont(font); + + m_dataMapper.setModel(const_cast(m_index.model())); + m_dataMapper.setItemDelegate(new RecentTransactionDelegate(this)); + m_dataMapper.addMapping(m_ui->m_iconLabel, TransactionsModel::COLUMN_TYPE); + m_dataMapper.addMapping(m_ui->m_amountLabel, TransactionsModel::COLUMN_AMOUNT); + m_dataMapper.addMapping(m_ui->m_timeLabel, TransactionsModel::COLUMN_DATE); + m_dataMapper.addMapping(m_ui->m_hashLabel, TransactionsModel::COLUMN_HASH); + m_dataMapper.setCurrentModelIndex(m_index); +} + +TransactionFrame::~TransactionFrame() { +} + +void TransactionFrame::mousePressEvent(QMouseEvent* _event) { + MainWindow::instance().scrollToTransaction(TransactionsModel::instance().index(m_index.data(TransactionsModel::ROLE_ROW).toInt(), 0)); +} + +} + +#include "TransactionFrame.moc" diff --git a/src/gui/TransactionFrame.h b/src/gui/TransactionFrame.h new file mode 100644 index 0000000..ba5a971 --- /dev/null +++ b/src/gui/TransactionFrame.h @@ -0,0 +1,36 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include +#include +#include + +namespace Ui { +class TransactionFrame; +} + +namespace WalletGui { + +class TransactionFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(TransactionFrame) + +public: + TransactionFrame(const QModelIndex &_index, QWidget* _parent); + ~TransactionFrame(); + +protected: + void mousePressEvent(QMouseEvent* _event) Q_DECL_OVERRIDE; + +private: + QScopedPointer m_ui; + QDataWidgetMapper m_dataMapper; + QPersistentModelIndex m_index; +}; + +} diff --git a/src/gui/TransactionsFrame.cpp b/src/gui/TransactionsFrame.cpp new file mode 100644 index 0000000..97a533d --- /dev/null +++ b/src/gui/TransactionsFrame.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include + +#include "MainWindow.h" +#include "SortedTransactionsModel.h" +#include "TransactionsFrame.h" +#include "TransactionDetailsDialog.h" +#include "TransactionsListModel.h" +#include "TransactionsModel.h" + +#include "ui_transactionsframe.h" + +namespace WalletGui { + +TransactionsFrame::TransactionsFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::TransactionsFrame), + m_transactionsModel(new TransactionsListModel) { + m_ui->setupUi(this); + m_ui->m_transactionsView->setModel(m_transactionsModel.data()); + m_ui->m_transactionsView->header()->setSectionResizeMode(TransactionsModel::COLUMN_STATE, QHeaderView::Fixed); + m_ui->m_transactionsView->header()->resizeSection(TransactionsModel::COLUMN_STATE, 25); + m_ui->m_transactionsView->header()->resizeSection(TransactionsModel::COLUMN_DATE, 140); + m_ui->m_transactionsView->header()->resizeSection(TransactionsModel::COLUMN_ADDRESS, 400); +} + +TransactionsFrame::~TransactionsFrame() { +} + +void TransactionsFrame::scrollToTransaction(const QModelIndex& _index) { + QModelIndex sortedModelIndex = SortedTransactionsModel::instance().mapFromSource(_index); + QModelIndex index = static_cast(m_ui->m_transactionsView->model())->mapFromSource(sortedModelIndex); + m_ui->m_transactionsView->scrollTo(index); + m_ui->m_transactionsView->setFocus(); + m_ui->m_transactionsView->setCurrentIndex(index); +} + +void TransactionsFrame::exportToCsv() { + QString file = QFileDialog::getSaveFileName(&MainWindow::instance(), tr("Select CSV file"), QDir::homePath(), "CSV (*.csv)"); + if (!file.isEmpty()) { + QByteArray csv = TransactionsModel::instance().toCsv(); + QFile f(file); + if (f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + f.write(csv); + f.close(); + } + } +} + +void TransactionsFrame::showTransactionDetails(const QModelIndex& _index) { + if (!_index.isValid()) { + return; + } + + TransactionDetailsDialog dlg(_index, &MainWindow::instance()); + dlg.exec(); +} + +} diff --git a/src/gui/TransactionsFrame.h b/src/gui/TransactionsFrame.h new file mode 100644 index 0000000..7bbbddd --- /dev/null +++ b/src/gui/TransactionsFrame.h @@ -0,0 +1,37 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +namespace Ui { +class TransactionsFrame; +} + +namespace WalletGui { + +class TransactionsListModel; + +class TransactionsFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(TransactionsFrame) + +public: + TransactionsFrame(QWidget* _parent); + ~TransactionsFrame(); + + void scrollToTransaction(const QModelIndex& _index); + +private: + QScopedPointer m_ui; + QScopedPointer m_transactionsModel; + + Q_SLOT void exportToCsv(); + Q_SLOT void showTransactionDetails(const QModelIndex& _index); +}; + +} diff --git a/src/gui/TransactionsListModel.cpp b/src/gui/TransactionsListModel.cpp new file mode 100644 index 0000000..1921a17 --- /dev/null +++ b/src/gui/TransactionsListModel.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "SortedTransactionsModel.h" +#include "TransactionsListModel.h" +#include "TransactionsModel.h" + +namespace WalletGui { + +TransactionsListModel::TransactionsListModel() : QSortFilterProxyModel() { + setSourceModel(&SortedTransactionsModel::instance()); +} + +TransactionsListModel::~TransactionsListModel() { +} + +bool TransactionsListModel::filterAcceptsColumn(int _sourceColumn, const QModelIndex& _sourceParent) const { + quint32 column = sourceModel()->headerData(_sourceColumn, Qt::Horizontal, TransactionsModel::ROLE_COLUMN).toUInt(); + return column != TransactionsModel::COLUMN_HASH && column != TransactionsModel::COLUMN_FEE && column != TransactionsModel::COLUMN_HEIGHT && + column != TransactionsModel::COLUMN_TYPE; +} + +} diff --git a/src/gui/TransactionsListModel.h b/src/gui/TransactionsListModel.h new file mode 100644 index 0000000..ead5f02 --- /dev/null +++ b/src/gui/TransactionsListModel.h @@ -0,0 +1,23 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace WalletGui { + +class TransactionsListModel : public QSortFilterProxyModel { + Q_OBJECT + +public: + TransactionsListModel(); + ~TransactionsListModel(); + +protected: + bool filterAcceptsColumn(int _sourceColumn, const QModelIndex& _sourceParent) const Q_DECL_OVERRIDE; +}; + +} diff --git a/src/gui/TransactionsModel.cpp b/src/gui/TransactionsModel.cpp new file mode 100644 index 0000000..a0dc94e --- /dev/null +++ b/src/gui/TransactionsModel.cpp @@ -0,0 +1,394 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include + +#include "CurrencyAdapter.h" +#include "NodeAdapter.h" +#include "TransactionsModel.h" +#include "WalletAdapter.h" + +namespace WalletGui { + +enum class TransactionType : quint8 {MINED, INPUT, OUTPUT, INOUT}; + +const int TRANSACTIONS_MODEL_COLUMN_COUNT = + TransactionsModel::staticMetaObject.enumerator(TransactionsModel::staticMetaObject.indexOfEnumerator("Columns")).keyCount(); + +namespace { + +QPixmap getTransactionIcon(TransactionType _transactionType) { + switch (_transactionType) { + case TransactionType::MINED: + return QPixmap(":icons/tx-mined"); + case TransactionType::INPUT: + return QPixmap(":icons/tx-input"); + case TransactionType::OUTPUT: + return QPixmap(":icons/tx-output"); + case TransactionType::INOUT: + return QPixmap(":icons/tx-inout"); + default: + break; + } + + return QPixmap(); +} + +} + +TransactionsModel& TransactionsModel::instance() { + static TransactionsModel inst; + return inst; +} + +TransactionsModel::TransactionsModel() : QAbstractItemModel() { + connect(&WalletAdapter::instance(), &WalletAdapter::reloadWalletTransactionsSignal, this, &TransactionsModel::reloadWalletTransactions, + Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletTransactionCreatedSignal, this, + static_cast(&TransactionsModel::appendTransaction), Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletTransactionUpdatedSignal, this, &TransactionsModel::updateWalletTransaction, + Qt::QueuedConnection); + connect(&NodeAdapter::instance(), &NodeAdapter::localBlockchainUpdatedSignal, this, &TransactionsModel::localBlockchainUpdated, + Qt::QueuedConnection); + connect(&WalletAdapter::instance(), &WalletAdapter::walletCloseCompletedSignal, this, &TransactionsModel::reset, + Qt::QueuedConnection); +} + +TransactionsModel::~TransactionsModel() { +} + +Qt::ItemFlags TransactionsModel::flags(const QModelIndex& _index) const { + Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if(_index.column() == COLUMN_HASH) { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + +int TransactionsModel::columnCount(const QModelIndex& _parent) const { + return TRANSACTIONS_MODEL_COLUMN_COUNT; +} + +int TransactionsModel::rowCount(const QModelIndex& _parent) const { + return m_transfers.size(); +} + +QVariant TransactionsModel::headerData(int _section, Qt::Orientation _orientation, int _role) const { + if(_orientation != Qt::Horizontal) { + return QVariant(); + } + + switch(_role) { + case Qt::DisplayRole: + switch(_section) { + case COLUMN_STATE: + return QVariant(); + case COLUMN_DATE: + return tr("Date"); + case COLUMN_TYPE: + return tr("Type"); + case COLUMN_HASH: + return tr("Hash"); + case COLUMN_ADDRESS: + return tr("Address"); + case COLUMN_AMOUNT: + return tr("Amount"); + case COLUMN_PAYMENT_ID: + return tr("PaymentID"); + default: + break; + } + + case Qt::TextAlignmentRole: + if (_section == COLUMN_AMOUNT) { + return static_cast(Qt::AlignRight | Qt::AlignVCenter); + } + + return QVariant(); + + case ROLE_COLUMN: + return _section; + } + + return QVariant(); +} + +QVariant TransactionsModel::data(const QModelIndex& _index, int _role) const { + if(!_index.isValid()) { + return QVariant(); + } + + CryptoNote::TransactionInfo transaction; + CryptoNote::Transfer transfer; + CryptoNote::TransactionId transactionId = m_transfers.value(_index.row()).first; + CryptoNote::TransferId transferId = m_transfers.value(_index.row()).second; + + if(!WalletAdapter::instance().getTransaction(transactionId, transaction) || + (m_transfers.value(_index.row()).second != CryptoNote::INVALID_TRANSFER_ID && + !WalletAdapter::instance().getTransfer(transferId, transfer))) { + return QVariant(); + } + + switch(_role) { + case Qt::DisplayRole: + case Qt::EditRole: + return getDisplayRole(_index); + + case Qt::DecorationRole: + return getDecorationRole(_index); + + case Qt::TextAlignmentRole: + return getAlignmentRole(_index); + + default: + return getUserRole(_index, _role, transactionId, transaction, transferId, transfer); + } + + return QVariant(); +} + +QModelIndex TransactionsModel::index(int _row, int _column, const QModelIndex& _parent) const { + if(_parent.isValid()) { + return QModelIndex(); + } + + return createIndex(_row, _column, _row); +} + +QModelIndex TransactionsModel::parent(const QModelIndex& _index) const { + return QModelIndex(); +} + +QByteArray TransactionsModel::toCsv() const { + QByteArray res; + res.append("\"State\",\"Date\",\"Amount\",\"Fee\",\"Hash\",\"Height\",\"Address\",\"Payment ID\"\n"); + for (quint32 row = 0; row < rowCount(); ++row) { + QModelIndex ind = index(row, 0); + res.append("\"").append(ind.data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_DATE).data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_AMOUNT).data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_FEE).data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_HASH).data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_HEIGHT).data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_ADDRESS).data().toString().toUtf8()).append("\","); + res.append("\"").append(ind.sibling(row, COLUMN_PAYMENT_ID).data().toString().toUtf8()).append("\"\n"); + } + + return res; +} + +QVariant TransactionsModel::getDisplayRole(const QModelIndex& _index) const { + switch(_index.column()) { + case COLUMN_DATE: { + QDateTime date = _index.data(ROLE_DATE).toDateTime(); + return (date.isNull() || !date.isValid() ? "-" : date.toString("dd-MM-yy HH:mm")); + } + + case COLUMN_HASH: + return _index.data(ROLE_HASH).toByteArray().toHex().toUpper(); + + case COLUMN_ADDRESS: { + TransactionType transactionType = static_cast(_index.data(ROLE_TYPE).value()); + QString transactionAddress = _index.data(ROLE_ADDRESS).toString(); + if (transactionType == TransactionType::INPUT || transactionType == TransactionType::MINED || + transactionType == TransactionType::INOUT) { + return QString(tr("me (%1)").arg(WalletAdapter::instance().getAddress())); + } else if (transactionAddress.isEmpty()) { + return tr("(n/a)"); + } + + return transactionAddress; + } + + case COLUMN_AMOUNT: { + qint64 amount = _index.data(ROLE_AMOUNT).value(); + QString amountStr = CurrencyAdapter::instance().formatAmount(qAbs(amount)); + return (amount < 0 ? "-" + amountStr : amountStr); + } + + case COLUMN_PAYMENT_ID: + return _index.data(ROLE_PAYMENT_ID); + + case COLUMN_FEE: { + qint64 fee = _index.data(ROLE_FEE).value(); + return CurrencyAdapter::instance().formatAmount(fee); + } + + case COLUMN_HEIGHT: + return QString::number(_index.data(ROLE_HEIGHT).value()); + + default: + break; + } + + return QVariant(); +} + +QVariant TransactionsModel::getDecorationRole(const QModelIndex& _index) const { + if(_index.column() == COLUMN_STATE) { + quint64 numberOfConfirmations = _index.data(ROLE_NUMBER_OF_CONFIRMATIONS).value(); + if(numberOfConfirmations == 0) { + return QPixmap(":icons/unconfirmed"); + } else if(numberOfConfirmations < 2) { + return QPixmap(":icons/clock1"); + } else if(numberOfConfirmations < 4) { + return QPixmap(":icons/clock2"); + } else if(numberOfConfirmations < 6) { + return QPixmap(":icons/clock3"); + } else if(numberOfConfirmations < 8) { + return QPixmap(":icons/clock4"); + } else if(numberOfConfirmations < 10) { + return QPixmap(":icons/clock5"); + } else { + return QPixmap(":icons/transaction"); + } + } else if (_index.column() == COLUMN_ADDRESS) { + return _index.data(ROLE_ICON).value().scaled(20, 20, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + + return QVariant(); +} + +QVariant TransactionsModel::getAlignmentRole(const QModelIndex& _index) const { + return headerData(_index.column(), Qt::Horizontal, Qt::TextAlignmentRole); +} + +QVariant TransactionsModel::getUserRole(const QModelIndex& _index, int _role, CryptoNote::TransactionId _transactionId, + CryptoNote::TransactionInfo& _transaction, CryptoNote::TransferId _transferId, CryptoNote::Transfer& _transfer) const { + switch(_role) { + case ROLE_DATE: + return (_transaction.timestamp > 0 ? QDateTime::fromTime_t(_transaction.timestamp) : QDateTime()); + + case ROLE_TYPE: { + QString transactionAddress = _index.data(ROLE_ADDRESS).toString(); + if(_transaction.isCoinbase) { + return static_cast(TransactionType::MINED); + } else if (!transactionAddress.compare(WalletAdapter::instance().getAddress())) { + return static_cast(TransactionType::INOUT); + } else if(_transaction.totalAmount < 0) { + return static_cast(TransactionType::OUTPUT); + } + + return static_cast(TransactionType::INPUT); + } + + case ROLE_HASH: + return QByteArray(reinterpret_cast(&_transaction.hash.front()), _transaction.hash.size()); + + case ROLE_ADDRESS: + return QString::fromStdString(_transfer.address); + + case ROLE_AMOUNT: + return static_cast(_transferId == CryptoNote::INVALID_TRANSFER_ID ? _transaction.totalAmount : -_transfer.amount); + + case ROLE_PAYMENT_ID: + return NodeAdapter::instance().extractPaymentId(_transaction.extra); + + case ROLE_ICON: { + TransactionType transactionType = static_cast(_index.data(ROLE_TYPE).value()); + return getTransactionIcon(transactionType); + } + + case ROLE_TRANSACTION_ID: + return QVariant::fromValue(_transactionId); + + case ROLE_HEIGHT: + return static_cast(_transaction.blockHeight); + + case ROLE_FEE: + return static_cast(_transaction.fee); + + case ROLE_NUMBER_OF_CONFIRMATIONS: + return (_transaction.blockHeight == CryptoNote::UNCONFIRMED_TRANSACTION_HEIGHT ? 0 : + NodeAdapter::instance().getLastKnownBlockHeight() - _transaction.blockHeight + 1); + + case ROLE_COLUMN: + return headerData(_index.column(), Qt::Horizontal, ROLE_COLUMN); + + case ROLE_ROW: + return _index.row(); + } + + return QVariant(); +} + +void TransactionsModel::reloadWalletTransactions() { + beginResetModel(); + m_transfers.clear(); + m_transactionRow.clear(); + endResetModel(); + + quint32 row_count = 0; + for (CryptoNote::TransactionId transactionId = 0; transactionId < WalletAdapter::instance().getTransactionCount(); ++transactionId) { + appendTransaction(transactionId, row_count); + } + + if (row_count > 0) { + beginInsertRows(QModelIndex(), 0, row_count - 1); + endInsertRows(); + } +} + +void TransactionsModel::appendTransaction(CryptoNote::TransactionId _transactionId, quint32& _insertedRowCount) { + CryptoNote::TransactionInfo transaction; + if (!WalletAdapter::instance().getTransaction(_transactionId, transaction)) { + return; + } + + if (transaction.transferCount) { + m_transactionRow[_transactionId] = qMakePair(m_transfers.size(), transaction.transferCount); + for (CryptoNote::TransferId transfer_id = transaction.firstTransferId; + transfer_id < transaction.firstTransferId + transaction.transferCount; ++transfer_id) { + m_transfers.append(TransactionTransferId(_transactionId, transfer_id)); + ++_insertedRowCount; + } + } else { + m_transfers.append(TransactionTransferId(_transactionId, CryptoNote::INVALID_TRANSFER_ID)); + m_transactionRow[_transactionId] = qMakePair(m_transfers.size() - 1, 1); + ++_insertedRowCount; + } +} + +void TransactionsModel::appendTransaction(CryptoNote::TransactionId _transactionId) { + if (m_transactionRow.contains(_transactionId)) { + return; + } + + quint32 oldRowCount = rowCount(); + quint32 insertedRowCount = 0; + for (quint64 transactionId = m_transactionRow.size(); transactionId <= _transactionId; ++transactionId) { + appendTransaction(transactionId, insertedRowCount); + } + + if (insertedRowCount > 0) { + beginInsertRows(QModelIndex(), oldRowCount, oldRowCount + insertedRowCount - 1); + endInsertRows(); + } +} + +void TransactionsModel::updateWalletTransaction(CryptoNote::TransactionId _id) { + quint32 firstRow = m_transactionRow.value(_id).first; + quint32 lastRow = firstRow + m_transactionRow.value(_id).second - 1; + Q_EMIT dataChanged(index(firstRow, COLUMN_DATE), index(lastRow, COLUMN_DATE)); +} + +void TransactionsModel::localBlockchainUpdated(quint64 _height) { + if(rowCount() > 0) { + Q_EMIT dataChanged(index(0, COLUMN_STATE), index(rowCount() - 1, COLUMN_STATE)); + } +} + +void TransactionsModel::reset() { + beginResetModel(); + m_transfers.clear(); + m_transactionRow.clear(); + endResetModel(); +} + +} diff --git a/src/gui/TransactionsModel.h b/src/gui/TransactionsModel.h new file mode 100644 index 0000000..883e9ea --- /dev/null +++ b/src/gui/TransactionsModel.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include +#include + +#include + +namespace WalletGui { + +typedef QPair TransactionTransferId; + +class TransactionsModel : public QAbstractItemModel { + Q_OBJECT + Q_ENUMS(Columns) + Q_ENUMS(Roles) + +public: + enum Columns{ + COLUMN_STATE = 0, COLUMN_DATE, COLUMN_AMOUNT, COLUMN_ADDRESS, COLUMN_PAYMENT_ID, COLUMN_HASH, COLUMN_FEE, + COLUMN_HEIGHT, COLUMN_TYPE + }; + + enum Roles{ + ROLE_DATE = Qt::UserRole, ROLE_TYPE, ROLE_HASH, ROLE_ADDRESS, ROLE_AMOUNT, ROLE_PAYMENT_ID, ROLE_ICON, + ROLE_TRANSACTION_ID, ROLE_HEIGHT, ROLE_FEE, ROLE_NUMBER_OF_CONFIRMATIONS, ROLE_COLUMN, ROLE_ROW + }; + + static TransactionsModel& instance(); + + Qt::ItemFlags flags(const QModelIndex& _index) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + + QVariant headerData(int _section, Qt::Orientation _orientation, int _role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex& _index, int _role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int _row, int _column, const QModelIndex& _parent = QModelIndex()) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex& _index) const Q_DECL_OVERRIDE; + + QByteArray toCsv() const; + +private: + QVector m_transfers; + QHash > m_transactionRow; + + TransactionsModel(); + ~TransactionsModel(); + + QVariant getDisplayRole(const QModelIndex& _index) const; + QVariant getDecorationRole(const QModelIndex& _index) const; + QVariant getAlignmentRole(const QModelIndex& _index) const; + QVariant getUserRole(const QModelIndex& _index, int _role, CryptoNote::TransactionId _transactionId, CryptoNote::TransactionInfo& _transaction, + CryptoNote::TransferId _transferId, CryptoNote::Transfer& _transfer) const; + + void reloadWalletTransactions(); + void appendTransaction(CryptoNote::TransactionId _id, quint32& _row_count); + void appendTransaction(CryptoNote::TransactionId _id); + void updateWalletTransaction(CryptoNote::TransactionId _id); + void localBlockchainUpdated(quint64 _height); + void reset(); +}; + +} diff --git a/src/gui/TransferFrame.cpp b/src/gui/TransferFrame.cpp new file mode 100644 index 0000000..a19dc24 --- /dev/null +++ b/src/gui/TransferFrame.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include + +#include "AddressBookDialog.h" +#include "MainWindow.h" +#include "CurrencyAdapter.h" +#include "TransferFrame.h" + +#include "ui_transferframe.h" + +namespace WalletGui { + +TransferFrame::TransferFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::TransferFrame) { + m_ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + m_ui->m_amountSpin->setSuffix(" " + CurrencyAdapter::instance().getCurrencyTicker().toUpper()); +} + +TransferFrame::~TransferFrame() { +} + +QString TransferFrame::getAddress() const { + return m_ui->m_addressEdit->text().trimmed(); +} + +QString TransferFrame::getLabel() const { + return m_ui->m_labelEdit->text().trimmed(); +} + +qreal TransferFrame::getAmount() const { + return m_ui->m_amountSpin->value(); +} + +QString TransferFrame::getAmountString() const { + return m_ui->m_amountSpin->cleanText(); +} + +void TransferFrame::disableRemoveButton(bool _disable) { + m_ui->m_removeButton->setDisabled(_disable); +} + +void TransferFrame::addressBookClicked() { + AddressBookDialog dlg(&MainWindow::instance()); + if(dlg.exec() == QDialog::Accepted) { + m_ui->m_addressEdit->setText(dlg.getAddress()); + } +} + +void TransferFrame::pasteClicked() { + m_ui->m_addressEdit->setText(QApplication::clipboard()->text()); +} + +} diff --git a/src/gui/TransferFrame.h b/src/gui/TransferFrame.h new file mode 100644 index 0000000..e6bee51 --- /dev/null +++ b/src/gui/TransferFrame.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace Ui { +class TransferFrame; +} + +namespace WalletGui { + +class TransferFrame : public QFrame { + Q_OBJECT + Q_DISABLE_COPY(TransferFrame) + +public: + TransferFrame(QWidget* _parent); + ~TransferFrame(); + + QString getAddress() const; + QString getLabel() const; + qreal getAmount() const; + QString getAmountString() const; + + void disableRemoveButton(bool _disable); + +private: + QScopedPointer m_ui; + + Q_SLOT void addressBookClicked(); + Q_SLOT void pasteClicked(); +}; + +} diff --git a/src/gui/WalletEvents.h b/src/gui/WalletEvents.h new file mode 100644 index 0000000..4e55a78 --- /dev/null +++ b/src/gui/WalletEvents.h @@ -0,0 +1,37 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include + +namespace WalletGui +{ + enum class WalletEventType : quint32 { + ShowMessage = QEvent::User + }; + + class ShowMessageEvent : public QEvent { + Q_DISABLE_COPY(ShowMessageEvent) + + public: + ShowMessageEvent(const QString &_messageText, QtMsgType _messageType) : QEvent(static_cast(WalletEventType::ShowMessage)), + m_messageText(_messageText), m_messgaeType(_messageType) { + } + + QString messageText() const { + return m_messageText; + } + + QtMsgType messageType() const { + return m_messgaeType; + } + + private: + QString m_messageText; + QtMsgType m_messgaeType; + }; + +} diff --git a/src/gui/ui/aboutdialog.ui b/src/gui/ui/aboutdialog.ui new file mode 100644 index 0000000..ffc592f --- /dev/null +++ b/src/gui/ui/aboutdialog.ui @@ -0,0 +1,102 @@ + + + AboutDialog + + + + 0 + 0 + 592 + 240 + + + + + 0 + 0 + + + + + 592 + 240 + + + + + 592 + 240 + + + + About %1 + + + + + + + 0 + 0 + + + + <html><head/><body><p>CryptoNote coin version %1</p><p>CryptoNote coin is the next generation anonymous cryptocurrency based on CryptoNote.</p><p>Copyright (c) 2012-2015. CryptoNote Developers</p><p><a href="http://opensource.org/licenses/MIT"><span style=" text-decoration: underline; color:#0000ff;">http://opensource.org/licenses/MIT</span></a></p><p><a href="https://cryptonote.org/"><span style=" text-decoration: underline; color:#0000ff;">https://cryptonote.org/</span></a></p></body></html> + + + Qt::RichText + + + true + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + m_closeButton + clicked() + AboutDialog + accept() + + + 538 + 285 + + + 294 + 156 + + + + + diff --git a/src/gui/ui/addressbookdialog.ui b/src/gui/ui/addressbookdialog.ui new file mode 100644 index 0000000..45f41ff --- /dev/null +++ b/src/gui/ui/addressbookdialog.ui @@ -0,0 +1,81 @@ + + + AddressBookDialog + + + + 0 + 0 + 747 + 525 + + + + Select address + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Choose + + + + + + + + + + + m_okButton + clicked() + AddressBookDialog + accept() + + + 694 + 499 + + + 373 + 262 + + + + + m_addressBookView + doubleClicked(QModelIndex) + AddressBookDialog + accept() + + + 373 + 242 + + + 373 + 262 + + + + + diff --git a/src/gui/ui/addressbookframe.ui b/src/gui/ui/addressbookframe.ui new file mode 100644 index 0000000..43438f9 --- /dev/null +++ b/src/gui/ui/addressbookframe.ui @@ -0,0 +1,142 @@ + + + AddressBookFrame + + + + 0 + 0 + 874 + 585 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + + New address + + + + :/icons/add:/icons/add + + + + + + + false + + + Delete address + + + + :/icons/remove:/icons/remove + + + + + + + false + + + Copy address + + + + :/icons/copy:/icons/copy + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + m_addAddressButton + clicked() + AddressBookFrame + addClicked() + + + 53 + 556 + + + 436 + 292 + + + + + m_deleteAddressButton + clicked() + AddressBookFrame + deleteClicked() + + + 148 + 556 + + + 436 + 292 + + + + + m_copyAddressButton + clicked() + AddressBookFrame + copyClicked() + + + 243 + 556 + + + 436 + 292 + + + + + + addClicked() + deleteClicked() + copyClicked() + + diff --git a/src/gui/ui/changepassworddialog.ui b/src/gui/ui/changepassworddialog.ui new file mode 100644 index 0000000..79526c7 --- /dev/null +++ b/src/gui/ui/changepassworddialog.ui @@ -0,0 +1,207 @@ + + + ChangePasswordDialog + + + + 0 + 0 + 338 + 166 + + + + + 0 + 0 + + + + + 338 + 0 + + + + + 338 + 16777215 + + + + Change password + + + + + + false + + + Ok + + + true + + + + + + + QLineEdit::Password + + + + + + + Old password: + + + + + + + QLineEdit::Password + + + + + + + Cancel + + + + + + + + 0 + 0 + + + + New password: + + + + + + + + 0 + 0 + + + + QLineEdit::Password + + + + + + + + 0 + 0 + + + + Confirm: + + + + + + + color: #ff0000; font: 11px; + + + Password not confirmed + + + Qt::AlignCenter + + + + + + + m_oldPasswordEdit + m_newPasswordEdit + m_newPasswordConfirmationEdit + m_okButton + m_cancelButton + + + + + m_newPasswordEdit + textChanged(QString) + ChangePasswordDialog + checkPassword(QString) + + + 223 + 64 + + + 168 + 82 + + + + + m_newPasswordConfirmationEdit + textChanged(QString) + ChangePasswordDialog + checkPassword(QString) + + + 223 + 103 + + + 168 + 82 + + + + + m_okButton + clicked() + ChangePasswordDialog + accept() + + + 169 + 141 + + + 168 + 82 + + + + + m_cancelButton + clicked() + ChangePasswordDialog + reject() + + + 277 + 141 + + + 168 + 82 + + + + + + checkPassword(QString) + + diff --git a/src/gui/ui/exitwidget.ui b/src/gui/ui/exitwidget.ui new file mode 100644 index 0000000..7aa24bc --- /dev/null +++ b/src/gui/ui/exitwidget.ui @@ -0,0 +1,36 @@ + + + ExitWidget + + + + 0 + 0 + 385 + 66 + + + + Saving data + + + + + + + + + + + + + %1 wallet is saving data. +Please wait... + + + + + + + + diff --git a/src/gui/ui/mainwindow.ui b/src/gui/ui/mainwindow.ui new file mode 100644 index 0000000..6dd6806 --- /dev/null +++ b/src/gui/ui/mainwindow.ui @@ -0,0 +1,628 @@ + + + MainWindow + + + + 0 + 0 + 920 + 600 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + MainWindow + + + + :/images/cryptonote:/images/cryptonote + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + + + 0 + 0 + 920 + 27 + + + + + File + + + + + + + + + Settings + + + + + + + + + + + Help + + + + + + + + + + + toolBar + + + false + + + Qt::ToolButtonTextBesideIcon + + + TopToolBarArea + + + false + + + + + + + + + + + true + + + true + + + + :/icons/overview:/icons/overview + + + Overview + + + + + true + + + true + + + + :/icons/send:/icons/send + + + Send + + + + + true + + + true + + + + :/icons/receive:/icons/receive + + + Receive + + + + + true + + + true + + + + :/icons/transactions:/icons/transactions + + + Transactions + + + + + true + + + Exit + + + Ctrl+Q + + + + + true + + + true + + + + :/icons/address-book:/icons/address-book + + + Address Book + + + + + true + + + Create wallet + + + + + true + + + Open wallet + + + + + true + + + Encrypt wallet + + + + + true + + + Change password + + + Change password + + + + + true + + + About + + + + + true + + + About Qt + + + + + true + + + Backup wallet + + + + + true + + + Start on system login + + + + + true + + + Minimize to tray + + + + + true + + + Close to tray + + + + + + WalletGui::OverviewFrame + QFrame +
gui/OverviewFrame.h
+ 1 +
+ + WalletGui::SendFrame + QFrame +
gui/SendFrame.h
+ 1 +
+ + WalletGui::ReceiveFrame + QFrame +
gui/ReceiveFrame.h
+ 1 +
+ + WalletGui::TransactionsFrame + QFrame +
gui/TransactionsFrame.h
+ 1 +
+ + WalletGui::AddressBookFrame + QFrame +
gui/AddressBookFrame.h
+ 1 +
+
+ + + + + + m_overviewAction + toggled(bool) + m_overviewFrame + setVisible(bool) + + + -1 + -1 + + + 112 + 333 + + + + + m_sendAction + toggled(bool) + m_sendFrame + setVisible(bool) + + + -1 + -1 + + + 337 + 333 + + + + + m_receiveAction + toggled(bool) + m_receiveFrame + setVisible(bool) + + + -1 + -1 + + + 562 + 333 + + + + + m_transactionsAction + toggled(bool) + m_transactionsFrame + setVisible(bool) + + + -1 + -1 + + + 787 + 333 + + + + + m_addressBookAction + toggled(bool) + m_addressBookFrame + setVisible(bool) + + + -1 + -1 + + + 809 + 320 + + + + + m_createWalletAction + triggered() + MainWindow + createWallet() + + + -1 + -1 + + + 449 + 299 + + + + + m_openWalletAction + triggered() + MainWindow + openWallet() + + + -1 + -1 + + + 449 + 299 + + + + + m_encryptWalletAction + triggered() + MainWindow + encryptWallet() + + + -1 + -1 + + + 449 + 299 + + + + + m_changePasswordAction + triggered() + MainWindow + encryptWallet() + + + -1 + -1 + + + 449 + 299 + + + + + m_aboutQtAction + triggered() + MainWindow + aboutQt() + + + -1 + -1 + + + 449 + 299 + + + + + m_backupWalletAction + triggered() + MainWindow + backupWallet() + + + -1 + -1 + + + 449 + 299 + + + + + m_aboutCryptonoteAction + triggered() + MainWindow + about() + + + -1 + -1 + + + 459 + 299 + + + + + m_startOnLoginAction + triggered(bool) + MainWindow + setStartOnLogin(bool) + + + -1 + -1 + + + 459 + 299 + + + + + m_minimizeToTrayAction + triggered(bool) + MainWindow + setMinimizeToTray(bool) + + + -1 + -1 + + + 459 + 299 + + + + + m_closeToTrayAction + triggered(bool) + MainWindow + setCloseToTray(bool) + + + -1 + -1 + + + 459 + 299 + + + + + + createWallet() + openWallet() + encryptWallet() + aboutQt() + backupWallet() + about() + setStartOnLogin(bool) + setMinimizeToTray(bool) + setCloseToTray(bool) + +
diff --git a/src/gui/ui/newaddressdialog.ui b/src/gui/ui/newaddressdialog.ui new file mode 100644 index 0000000..758eef1 --- /dev/null +++ b/src/gui/ui/newaddressdialog.ui @@ -0,0 +1,133 @@ + + + NewAddressDialog + + + + 0 + 0 + 590 + 127 + + + + New address + + + + + + + 0 + 0 + + + + Label: + + + + + + + + 0 + 0 + + + + QLineEdit::Normal + + + + + + + + 0 + 0 + + + + Address: + + + + + + + QLineEdit::Normal + + + + + + + Qt::Horizontal + + + + 338 + 20 + + + + + + + + true + + + Ok + + + true + + + + + + + Cancel + + + + + + + + + m_okButton + clicked() + NewAddressDialog + accept() + + + 410 + 102 + + + 294 + 63 + + + + + m_cancelButton + clicked() + NewAddressDialog + reject() + + + 526 + 102 + + + 294 + 63 + + + + + diff --git a/src/gui/ui/newpassworddialog.ui b/src/gui/ui/newpassworddialog.ui new file mode 100644 index 0000000..14badb2 --- /dev/null +++ b/src/gui/ui/newpassworddialog.ui @@ -0,0 +1,180 @@ + + + NewPasswordDialog + + + + 0 + 0 + 338 + 133 + + + + + 0 + 0 + + + + Enter password + + + + + + false + + + Ok + + + true + + + + + + + Cancel + + + + + + + + 0 + 0 + + + + Password: + + + + + + + + 0 + 0 + + + + QLineEdit::Password + + + + + + + QLineEdit::Password + + + + + + + + 0 + 0 + + + + Confirm: + + + + + + + color: #ff0000; font: 11px; + + + Password not confirmed + + + Qt::AlignCenter + + + + + + + m_passwordEdit + m_passwordConfirmationEdit + m_okButton + m_cancelButton + + + + + m_okButton + clicked() + NewPasswordDialog + accept() + + + 147 + 102 + + + 147 + 63 + + + + + m_cancelButton + clicked() + NewPasswordDialog + reject() + + + 241 + 102 + + + 147 + 63 + + + + + m_passwordEdit + textChanged(QString) + NewPasswordDialog + checkPassword(QString) + + + 206 + 25 + + + 168 + 64 + + + + + m_passwordConfirmationEdit + textChanged(QString) + NewPasswordDialog + checkPassword(QString) + + + 206 + 64 + + + 168 + 64 + + + + + + checkPassword(QString) + + diff --git a/src/gui/ui/overviewframe.ui b/src/gui/ui/overviewframe.ui new file mode 100644 index 0000000..7b5b99f --- /dev/null +++ b/src/gui/ui/overviewframe.ui @@ -0,0 +1,290 @@ + + + OverviewFrame + + + + 0 + 0 + 866 + 590 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + 5 + + + 0 + + + + + + 0 + 160 + + + + + 16777215 + 160 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + 75 + true + + + + 0.00 + + + + + + + + 75 + true + + + + 0.00 + + + + + + + Total: + + + + + + + + + + + + + + + 75 + true + + + + Wallet + + + + + + + Unconfirmed: + + + + + + + Balance: + + + + + + + Qt::Horizontal + + + + + + + + 75 + true + + + + 0.00 + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 425 + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + + + 75 + true + + + + Recent transactions + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::NoFocus + + + background-color: transparent; + + + QFrame::NoFrame + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + false + + + true + + + false + + + true + + + false + + + + + + + + + + + diff --git a/src/gui/ui/passworddialog.ui b/src/gui/ui/passworddialog.ui new file mode 100644 index 0000000..4a65b8d --- /dev/null +++ b/src/gui/ui/passworddialog.ui @@ -0,0 +1,131 @@ + + + PasswordDialog + + + + 0 + 0 + 338 + 103 + + + + + 338 + 0 + + + + + 338 + 16777215 + + + + Enter password + + + + + + + 0 + 0 + + + + QLineEdit::Password + + + + + + + Cancel + + + + + + + + 0 + 0 + + + + Password: + + + + + + + true + + + Ok + + + true + + + + + + + + 122 + 0 + + + + color: #ff0000; font: 11px; + + + Wrong password + + + Qt::AlignCenter + + + + + + + + + m_okButton + clicked() + PasswordDialog + accept() + + + 142 + 84 + + + 158 + 52 + + + + + m_cancelButton + clicked() + PasswordDialog + reject() + + + 257 + 84 + + + 158 + 52 + + + + + diff --git a/src/gui/ui/receiveframe.ui b/src/gui/ui/receiveframe.ui new file mode 100644 index 0000000..8071d82 --- /dev/null +++ b/src/gui/ui/receiveframe.ui @@ -0,0 +1,91 @@ + + + ReceiveFrame + + + + 0 + 0 + 846 + 592 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Address: + + + + + + + true + + + + + + + ... + + + + :/icons/copy:/icons/copy + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + m_copyButton + clicked() + ReceiveFrame + copyAddress() + + + 818 + 26 + + + 422 + 295 + + + + + + copyAddress() + + diff --git a/src/gui/ui/sendframe.ui b/src/gui/ui/sendframe.ui new file mode 100644 index 0000000..69040c6 --- /dev/null +++ b/src/gui/ui/sendframe.ui @@ -0,0 +1,320 @@ + + + SendFrame + + + + 0 + 0 + 866 + 590 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 840 + 446 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 445 + + + + + + + + + + + + 5 + + + + + + 200 + 16777215 + + + + 10 + + + 1 + + + 2 + + + false + + + Qt::Horizontal + + + QSlider::TicksBothSides + + + 1 + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 298 + 20 + + + + + + + + PaymentID: + + + + + + + + 30 + 16777215 + + + + + + + 5 + + + Qt::AlignCenter + + + true + + + + + + + + + + Anonimity level: + + + + + + + + + + + Send + + + + :/icons/send:/icons/send + + + + + + + Clear All + + + + :/icons/remove:/icons/remove + + + + + + + Add Recipient + + + + :/icons/add:/icons/add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Balance: + + + + + + + 0.00 + + + + + + + + + + + + + + + + m_paymentIdEdit + m_mixinSlider + m_sendButton + m_clearAllButton + m_addRecipientButton + m_transfersScrollarea + m_mixinEdit + + + + + + + m_addRecipientButton + clicked() + SendFrame + addRecipientClicked() + + + 282 + 561 + + + 432 + 294 + + + + + m_clearAllButton + clicked() + SendFrame + clearAllClicked() + + + 154 + 561 + + + 432 + 294 + + + + + m_sendButton + clicked() + SendFrame + sendClicked() + + + 53 + 561 + + + 432 + 294 + + + + + m_mixinSlider + valueChanged(int) + SendFrame + mixinValueChanged(int) + + + 221 + 520 + + + 432 + 294 + + + + + + sendClicked() + clearAllClicked() + addRecipientClicked() + mixinValueChanged(int) + + diff --git a/src/gui/ui/transactiondetailsdialog.ui b/src/gui/ui/transactiondetailsdialog.ui new file mode 100644 index 0000000..4b324e1 --- /dev/null +++ b/src/gui/ui/transactiondetailsdialog.ui @@ -0,0 +1,65 @@ + + + TransactionDetailsDialog + + + + 0 + 0 + 736 + 357 + + + + Transaction details + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + m_closeButton + clicked() + TransactionDetailsDialog + accept() + + + 619 + 331 + + + 335 + 178 + + + + + diff --git a/src/gui/ui/transactionframe.ui b/src/gui/ui/transactionframe.ui new file mode 100644 index 0000000..f5d70a6 --- /dev/null +++ b/src/gui/ui/transactionframe.ui @@ -0,0 +1,156 @@ + + + TransactionFrame + + + + 0 + 0 + 346 + 64 + + + + + 0 + 64 + + + + + 540 + 64 + + + + Frame + + + true + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + 20 + + + + + TextLabel + + + + + + + TextLabel + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + -1 + + + Qt::NoTextInteraction + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/src/gui/ui/transactionsframe.ui b/src/gui/ui/transactionsframe.ui new file mode 100644 index 0000000..e4868c4 --- /dev/null +++ b/src/gui/ui/transactionsframe.ui @@ -0,0 +1,125 @@ + + + TransactionsFrame + + + + 0 + 0 + 830 + 614 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + QAbstractScrollArea::AdjustIgnored + + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::CurrentChanged|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + true + + + false + + + true + + + false + + + false + + + true + + + false + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Export + + + + :/icons/export:/icons/export + + + + + + + + + + + + + m_csvButton + clicked() + TransactionsFrame + exportToCsv() + + + 761 + 587 + + + 414 + 306 + + + + + m_transactionsView + doubleClicked(QModelIndex) + TransactionsFrame + showTransactionDetails(QModelIndex) + + + 414 + 287 + + + 414 + 306 + + + + + + exportToCsv() + showTransactionDetails(QModelIndex) + + diff --git a/src/gui/ui/transferframe.ui b/src/gui/ui/transferframe.ui new file mode 100644 index 0000000..c68ec8a --- /dev/null +++ b/src/gui/ui/transferframe.ui @@ -0,0 +1,195 @@ + + + TransferFrame + + + + 0 + 0 + 885 + 121 + + + + Frame + + + QFrame::Box + + + QFrame::Sunken + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Pay To: + + + + + + + ... + + + + :/icons/address-book:/icons/address-book + + + + + + + ... + + + + :/icons/paste:/icons/paste + + + + + + + Enter a label for this address to add it to your address book + + + + + + + Amount: + + + + + + + ... + + + + :/icons/remove:/icons/remove + + + + + + + Qt::Horizontal + + + + 558 + 17 + + + + + + + + Label: + + + + + + + Qt::LeftToRight + + + + + + 8 + + + 999999999.999999046325684 + + + + + + + + + + m_addressEdit + m_labelEdit + m_amountSpin + m_addressBookButton + m_pasteButton + m_removeButton + + + + + + + m_removeButton + clicked() + TransferFrame + close() + + + 862 + 21 + + + 442 + 60 + + + + + m_addressBookButton + clicked() + TransferFrame + addressBookClicked() + + + 786 + 21 + + + 442 + 60 + + + + + m_pasteButton + clicked() + TransferFrame + pasteClicked() + + + 824 + 21 + + + 442 + 60 + + + + + + addressBookClicked() + pasteClicked() + + diff --git a/src/icons/add.png b/src/icons/add.png new file mode 100644 index 0000000..ea53fc3 Binary files /dev/null and b/src/icons/add.png differ diff --git a/src/icons/address-book.png b/src/icons/address-book.png new file mode 100644 index 0000000..33a2d91 Binary files /dev/null and b/src/icons/address-book.png differ diff --git a/src/icons/clock1.png b/src/icons/clock1.png new file mode 100644 index 0000000..9f0aa5d Binary files /dev/null and b/src/icons/clock1.png differ diff --git a/src/icons/clock2.png b/src/icons/clock2.png new file mode 100644 index 0000000..bad00cc Binary files /dev/null and b/src/icons/clock2.png differ diff --git a/src/icons/clock3.png b/src/icons/clock3.png new file mode 100644 index 0000000..7314d53 Binary files /dev/null and b/src/icons/clock3.png differ diff --git a/src/icons/clock4.png b/src/icons/clock4.png new file mode 100644 index 0000000..07f5bfa Binary files /dev/null and b/src/icons/clock4.png differ diff --git a/src/icons/clock5.png b/src/icons/clock5.png new file mode 100644 index 0000000..27e9630 Binary files /dev/null and b/src/icons/clock5.png differ diff --git a/src/icons/connected.png b/src/icons/connected.png new file mode 100644 index 0000000..df42ee2 Binary files /dev/null and b/src/icons/connected.png differ diff --git a/src/icons/disconnected.png b/src/icons/disconnected.png new file mode 100644 index 0000000..dca1059 Binary files /dev/null and b/src/icons/disconnected.png differ diff --git a/src/icons/editcopy.png b/src/icons/editcopy.png new file mode 100644 index 0000000..7807c59 Binary files /dev/null and b/src/icons/editcopy.png differ diff --git a/src/icons/editpaste.png b/src/icons/editpaste.png new file mode 100644 index 0000000..e217e30 Binary files /dev/null and b/src/icons/editpaste.png differ diff --git a/src/icons/export.png b/src/icons/export.png new file mode 100644 index 0000000..5c1f519 Binary files /dev/null and b/src/icons/export.png differ diff --git a/src/icons/lock_closed.png b/src/icons/lock_closed.png new file mode 100644 index 0000000..6de207d Binary files /dev/null and b/src/icons/lock_closed.png differ diff --git a/src/icons/lock_open.png b/src/icons/lock_open.png new file mode 100644 index 0000000..23ce324 Binary files /dev/null and b/src/icons/lock_open.png differ diff --git a/src/icons/overview.png b/src/icons/overview.png new file mode 100644 index 0000000..a274f0c Binary files /dev/null and b/src/icons/overview.png differ diff --git a/src/icons/receive.png b/src/icons/receive.png new file mode 100644 index 0000000..8ed337c Binary files /dev/null and b/src/icons/receive.png differ diff --git a/src/icons/remove.png b/src/icons/remove.png new file mode 100644 index 0000000..224d2c2 Binary files /dev/null and b/src/icons/remove.png differ diff --git a/src/icons/send.png b/src/icons/send.png new file mode 100644 index 0000000..43c3d79 Binary files /dev/null and b/src/icons/send.png differ diff --git a/src/icons/sync_sprite.png b/src/icons/sync_sprite.png new file mode 100644 index 0000000..d9c9ce0 Binary files /dev/null and b/src/icons/sync_sprite.png differ diff --git a/src/icons/synced.png b/src/icons/synced.png new file mode 100644 index 0000000..9fad384 Binary files /dev/null and b/src/icons/synced.png differ diff --git a/src/icons/transaction.png b/src/icons/transaction.png new file mode 100644 index 0000000..8a804b0 Binary files /dev/null and b/src/icons/transaction.png differ diff --git a/src/icons/transactions.png b/src/icons/transactions.png new file mode 100644 index 0000000..ac955c7 Binary files /dev/null and b/src/icons/transactions.png differ diff --git a/src/icons/tx_inout.png b/src/icons/tx_inout.png new file mode 100644 index 0000000..5f092f9 Binary files /dev/null and b/src/icons/tx_inout.png differ diff --git a/src/icons/tx_input.png b/src/icons/tx_input.png new file mode 100644 index 0000000..a2d324e Binary files /dev/null and b/src/icons/tx_input.png differ diff --git a/src/icons/tx_mined.png b/src/icons/tx_mined.png new file mode 100644 index 0000000..a7acc6c Binary files /dev/null and b/src/icons/tx_mined.png differ diff --git a/src/icons/tx_output.png b/src/icons/tx_output.png new file mode 100644 index 0000000..a7c5ebf Binary files /dev/null and b/src/icons/tx_output.png differ diff --git a/src/icons/unconfirmed.png b/src/icons/unconfirmed.png new file mode 100644 index 0000000..cfe1a1c Binary files /dev/null and b/src/icons/unconfirmed.png differ diff --git a/src/images/clock.gif b/src/images/clock.gif new file mode 100644 index 0000000..31b6c2c Binary files /dev/null and b/src/images/clock.gif differ diff --git a/src/images/cryptonote.icns b/src/images/cryptonote.icns new file mode 100644 index 0000000..1f82a07 Binary files /dev/null and b/src/images/cryptonote.icns differ diff --git a/src/images/cryptonote.ico b/src/images/cryptonote.ico new file mode 100644 index 0000000..8b6a715 Binary files /dev/null and b/src/images/cryptonote.ico differ diff --git a/src/images/cryptonote.png b/src/images/cryptonote.png new file mode 100644 index 0000000..cace7ed Binary files /dev/null and b/src/images/cryptonote.png differ diff --git a/src/images/splash.png b/src/images/splash.png new file mode 100644 index 0000000..4fa4aaf Binary files /dev/null and b/src/images/splash.png differ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..bf49ad9 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include +#include +#include +#include + +#include "CommandLineParser.h" +#include "CurrencyAdapter.h" +#include "NodeAdapter.h" +#include "Settings.h" +#include "SignalHandler.h" +#include "WalletAdapter.h" + +#include "gui/MainWindow.h" + +#define DEBUG 1 + +using namespace WalletGui; + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + app.setApplicationName(CurrencyAdapter::instance().getCurrencyName() + "wallet"); + app.setApplicationVersion(Settings::instance().getVersion()); + app.setQuitOnLastWindowClosed(false); + +#ifndef Q_OS_MAC + QApplication::setStyle(QStyleFactory::create("Fusion")); +#endif + + CommandLineParser cmdLineParser; + Settings::instance().setCommandLineParser(&cmdLineParser); + bool cmdLineParseResult = cmdLineParser.process(); + Settings::instance().load(); + +#ifdef Q_OS_WIN + if(!cmdLineParseResult) { + QMessageBox::critical(nullptr, QObject::tr("Error"), cmdLineParser.errorText()); + return app.exec(); + } else if (cmdLineParser.hasHelpOption()) { + QMessageBox::information(nullptr, QObject::tr("Help"), cmdLineParser.helpText()); + return app.exec(); + } +#endif + + QString dataDirPath = Settings::instance().getDataDir().absolutePath(); + if (!QDir().exists(dataDirPath)) { + QDir().mkpath(dataDirPath); + } + + QLockFile lockFile(Settings::instance().getDataDir().absoluteFilePath(QApplication::applicationName() + ".lock")); + if (!lockFile.tryLock()) { + QMessageBox::warning(nullptr, QObject::tr("Fail"), QString("%1 wallet already running").arg(CurrencyAdapter::instance().getCurrencyDisplayName())); + return 0; + } + + QLocale::setDefault(QLocale::c()); + + SignalHandler::instance().init(); + QObject::connect(&SignalHandler::instance(), &SignalHandler::quitSignal, &app, &QApplication::quit); + + QSplashScreen* splash = new QSplashScreen(QPixmap(":images/splash"), Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); + if (!splash->isVisible()) { + splash->show(); + } + + splash->showMessage(QObject::tr("Loading blockchain..."), Qt::AlignLeft | Qt::AlignBottom, Qt::white); + app.processEvents(); + qRegisterMetaType("CryptoNote::TransactionId"); + qRegisterMetaType("quintptr"); + if (!NodeAdapter::instance().init()) { + return 0; + } + + splash->finish(&MainWindow::instance()); + MainWindow::instance().show(); + WalletAdapter::instance().open(""); + QObject::connect(QApplication::instance(), &QApplication::aboutToQuit, []() { + MainWindow::instance().quit(); + if (WalletAdapter::instance().isOpen()) { + WalletAdapter::instance().close(); + } + + NodeAdapter::instance().deinit(); + }); + + return app.exec(); +} diff --git a/src/miniupnpcstrings.h b/src/miniupnpcstrings.h new file mode 100644 index 0000000..4a4765b --- /dev/null +++ b/src/miniupnpcstrings.h @@ -0,0 +1,11 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef MINIUPNPCSTRINGS_H_INCLUDED +#define MINIUPNPCSTRINGS_H_INCLUDED + +#define OS_STRING "Windows" +#define MINIUPNPC_VERSION_STRING "1.9" + +#endif diff --git a/src/resources.qrc b/src/resources.qrc new file mode 100644 index 0000000..e973685 --- /dev/null +++ b/src/resources.qrc @@ -0,0 +1,36 @@ + + + icons/sync_sprite.png + icons/synced.png + icons/lock_closed.png + icons/lock_open.png + icons/tx_mined.png + icons/editcopy.png + icons/add.png + icons/tx_input.png + icons/tx_output.png + icons/address-book.png + icons/editpaste.png + icons/remove.png + icons/receive.png + icons/transactions.png + icons/send.png + icons/overview.png + icons/clock1.png + icons/clock2.png + icons/clock3.png + icons/clock4.png + icons/clock5.png + icons/transaction.png + icons/connected.png + icons/disconnected.png + icons/export.png + icons/tx_inout.png + icons/unconfirmed.png + + + images/splash.png + images/cryptonote.png + images/clock.gif + +