Compare commits
282 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c69023135 | ||
|
|
fad8dab165 | ||
|
|
67be96e6d5 | ||
|
|
9ce88c6aeb | ||
|
|
7eeda0a8f0 | ||
|
|
9220937c4b | ||
|
|
6aaf294f0b | ||
|
|
721a0cbf91 | ||
|
|
d1bde4f8b2 | ||
|
|
0db4c36db0 | ||
|
|
dcc040a119 | ||
|
|
63e32c4a83 | ||
|
|
7972d8a219 | ||
|
|
872b81fe39 | ||
|
|
6136e88fcb | ||
|
|
c77637d141 | ||
|
|
0d047035fb | ||
|
|
b5aa659757 | ||
|
|
88d297d8cc | ||
|
|
352da92229 | ||
|
|
99907e539a | ||
|
|
e8bcabe66b | ||
|
|
829414ed01 | ||
|
|
36262f029a | ||
|
|
13ee9b1f26 | ||
|
|
085abb798a | ||
|
|
c137a6ea36 | ||
|
|
2cea0fc669 | ||
|
|
aaa1cb47c0 | ||
|
|
1577ce278b | ||
|
|
9d48f96fc8 | ||
|
|
1a51e93e55 | ||
|
|
b62b821002 | ||
|
|
f3ee46175c | ||
|
|
17a3c51a43 | ||
|
|
410897ced7 | ||
|
|
2fd3974194 | ||
|
|
7c50e1ff4b | ||
|
|
dec5dcf25c | ||
|
|
c9d36b1302 | ||
|
|
d931022963 | ||
|
|
df885b6eaa | ||
|
|
f78c95b73b | ||
|
|
b22fb24e47 | ||
|
|
8c8faf29e4 | ||
|
|
b7b1221221 | ||
|
|
38c3534ead | ||
|
|
f631b75f0a | ||
|
|
11fd19e0f8 | ||
|
|
1572b4cf79 | ||
|
|
10a47d783e | ||
|
|
14cc1d096a | ||
|
|
840421fd7b | ||
|
|
51739dfb22 | ||
|
|
be5e6772a4 | ||
|
|
c869390272 | ||
|
|
50c22ca300 | ||
|
|
f1b4eb6c56 | ||
|
|
d37c4ddef2 | ||
|
|
2522ff13fe | ||
|
|
596b71b293 | ||
|
|
8159b75be3 | ||
|
|
7677103f1b | ||
|
|
fead82b198 | ||
|
|
f55bf48bad | ||
|
|
e0faddf964 | ||
|
|
a00ed6a75a | ||
|
|
b54127e997 | ||
|
|
c672d8016d | ||
|
|
ccd8eb1c3a | ||
|
|
d5b165bde2 | ||
|
|
c978027933 | ||
|
|
9deec4dad0 | ||
|
|
e306992ce8 | ||
|
|
19daa074ca | ||
|
|
1c62edeff4 | ||
|
|
34439af67e | ||
|
|
e1e862bce4 | ||
|
|
0b2e74cdb5 | ||
|
|
3fee17e564 | ||
|
|
1e7d8293cb | ||
|
|
a3fc6754e0 | ||
|
|
8354c251c5 | ||
|
|
8f5053bd61 | ||
|
|
92b0a115f4 | ||
|
|
6a3e1aaf40 | ||
|
|
c32e11d3e8 | ||
|
|
9580c19da3 | ||
|
|
27532dc1bf | ||
|
|
c0e0626b84 | ||
|
|
c6d5c5dc3a | ||
|
|
749460fb46 | ||
|
|
c9ee76c2ee | ||
|
|
94dbf179d5 | ||
|
|
7deecbfdf6 | ||
|
|
29a742ba26 | ||
|
|
47559e51be | ||
|
|
f64dcde600 | ||
|
|
52c090b82f | ||
|
|
1edc068047 | ||
|
|
d18af7da72 | ||
|
|
53066ae92b | ||
|
|
333c9ee311 | ||
|
|
ff4de8e8f7 | ||
|
|
ef5d855950 | ||
|
|
e6f30578c0 | ||
|
|
f1e3926192 | ||
|
|
7f0c19950b | ||
|
|
1580c3a574 | ||
|
|
c8f4355e15 | ||
|
|
39561f8ead | ||
|
|
b15dbbb9b0 | ||
|
|
a73a4363ec | ||
|
|
55cbc562b6 | ||
|
|
e6c4c32d01 | ||
|
|
e36d4a918f | ||
|
|
0355ca2747 | ||
|
|
c946905907 | ||
|
|
a8bd2ab77e | ||
|
|
149e373367 | ||
|
|
fa3c8b5f89 | ||
|
|
e0f6577afd | ||
|
|
43f0854de9 | ||
|
|
f38e460842 | ||
|
|
527adcba17 | ||
|
|
0de1ba9f51 | ||
|
|
66e769603c | ||
|
|
15038850c2 | ||
|
|
efc9ad45e4 | ||
|
|
754a968706 | ||
|
|
c20a0ef928 | ||
|
|
eab98e3a48 | ||
|
|
2a8960bc2c | ||
|
|
fe68f59763 | ||
|
|
80e8dd6aef | ||
|
|
c82bd94bc3 | ||
|
|
28ca9503df | ||
|
|
73cc400ae8 | ||
|
|
8b7438ace2 | ||
|
|
210248e6ef | ||
|
|
f82948f4b0 | ||
|
|
da4e0dbf0f | ||
|
|
9dd3f4fecb | ||
|
|
abbe042c8a | ||
|
|
72a3b346bb | ||
|
|
d662e46146 | ||
|
|
bbc9ca8f08 | ||
|
|
f7271d1c7b | ||
|
|
afe1ae9b9c | ||
|
|
7f5b8ea0ad | ||
|
|
aa5000f556 | ||
|
|
8b6978b2a5 | ||
|
|
86d21a34ba | ||
|
|
4354a76df9 | ||
|
|
b616a14f88 | ||
|
|
7536e922e9 | ||
|
|
5bf0dd9684 | ||
|
|
cdf4ce2d6f | ||
|
|
37a8bcb525 | ||
|
|
2f8c0ca499 | ||
|
|
38612c1285 | ||
|
|
1d3a201077 | ||
|
|
a91a4f51ab | ||
|
|
52fbbae484 | ||
|
|
8f70fb4f79 | ||
|
|
c439d6814b | ||
|
|
f26f1469ca | ||
|
|
487e706f06 | ||
|
|
503c1af5f8 | ||
|
|
ca79525fdb | ||
|
|
7db5de082d | ||
|
|
ce6cc47afe | ||
|
|
a10a94f51c | ||
|
|
1833c16e39 | ||
|
|
d7207bfde3 | ||
|
|
5265e52b8b | ||
|
|
b6fdb709ba | ||
|
|
9aef3bab33 | ||
|
|
4b55197e18 | ||
|
|
11617d2f76 | ||
|
|
765e93cfd0 | ||
|
|
d31e661cd1 | ||
|
|
7f9b28c05f | ||
|
|
4a7ccd8d82 | ||
|
|
be954cc2c4 | ||
|
|
387e643ae9 | ||
|
|
6f71d47806 | ||
|
|
135c970ad9 | ||
|
|
fd5d1f584e | ||
|
|
2195c67f58 | ||
|
|
5c13624596 | ||
|
|
cfdba59584 | ||
|
|
4141832a4d | ||
|
|
5f183da6e3 | ||
|
|
30c54b1c6e | ||
|
|
c1da3d1c97 | ||
|
|
cbd03229dd | ||
|
|
2fc1b287dd | ||
|
|
7c8c6a116f | ||
|
|
ef93139a80 | ||
|
|
1b7844ec34 | ||
|
|
8f63e8870f | ||
|
|
286c75aa5b | ||
|
|
0a4d65dd99 | ||
|
|
83ccadb6a8 | ||
|
|
eae7eff9db | ||
|
|
2102e4be0d | ||
|
|
02eec351b9 | ||
|
|
ce4cb6512d | ||
|
|
3d24300963 | ||
|
|
9748974ce0 | ||
|
|
120b5285fb | ||
|
|
b022735506 | ||
|
|
33c1a6f4fc | ||
|
|
4b0dcb95bf | ||
|
|
fdb7f806fa | ||
|
|
a810bf3eb7 | ||
|
|
a99eef68f5 | ||
|
|
7ebdb884a1 | ||
|
|
c7f9ac926b | ||
|
|
9b98e0a2f5 | ||
|
|
3835387eea | ||
|
|
238b1b777f | ||
|
|
3af99e91e4 | ||
|
|
07ecca5af4 | ||
|
|
b245d0af7a | ||
|
|
585fb2810d | ||
|
|
0784532001 | ||
|
|
dcbdae0954 | ||
|
|
2bef74fe8a | ||
|
|
81d5dd1cae | ||
|
|
50767570f0 | ||
|
|
1d5b940349 | ||
|
|
3563d44d99 | ||
|
|
ffceda9159 | ||
|
|
a75a0fb8c5 | ||
|
|
5fa64c34ec | ||
|
|
376421667a | ||
|
|
8a73fd241e | ||
|
|
9760886eff | ||
|
|
34089599cd | ||
|
|
4a7a98034e | ||
|
|
79f78f48e2 | ||
|
|
fee81ba210 | ||
|
|
a078705ec6 | ||
|
|
de6a9b6779 | ||
|
|
3f13a5c9a4 | ||
|
|
fb7470a2a6 | ||
|
|
e2c6ae6472 | ||
|
|
ea25b71ca6 | ||
|
|
5ebe3f5092 | ||
|
|
8e4124f06a | ||
|
|
e47fd5f760 | ||
|
|
8767c71107 | ||
|
|
2156a6533b | ||
|
|
7eac690d44 | ||
|
|
31adc6cfd6 | ||
|
|
d582fd338d | ||
|
|
eed51e3ffa | ||
|
|
6a889bdaa1 | ||
|
|
5f27a45910 | ||
|
|
e1258e0ada | ||
|
|
925cced7d7 | ||
|
|
94083e746f | ||
|
|
a49d579bd3 | ||
|
|
6ed7fcec67 | ||
|
|
6d7db135e7 | ||
|
|
042400b83f | ||
|
|
df54439972 | ||
|
|
86252506f0 | ||
|
|
a9fea2462b | ||
|
|
58987b2ec6 | ||
|
|
aecd218d15 | ||
|
|
856843b52a | ||
|
|
fc740a89ab | ||
|
|
0643607ec3 | ||
|
|
5872bc8a2b | ||
|
|
d07da76383 | ||
|
|
dede10ea1a | ||
|
|
d21c22b444 | ||
|
|
b5fafb55c9 | ||
|
|
48aab5c6e5 |
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
*
|
||||
72
.github/workflows/build.yml
vendored
@@ -6,47 +6,89 @@ jobs:
|
||||
build-macos:
|
||||
|
||||
runs-on: macOS-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- name: "qmake"
|
||||
cmd: "export PATH=$PATH:/usr/local/opt/qt/bin && ./build.sh"
|
||||
- name: "cmake"
|
||||
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release -j3"
|
||||
name: build-macos-${{ matrix.toolchain.name }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: update brew and install dependencies
|
||||
run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5
|
||||
run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5 pkg-config
|
||||
- name: build
|
||||
run: export PATH=$PATH:/usr/local/opt/qt/bin && ./build.sh
|
||||
run: ${{ matrix.toolchain.cmd }}
|
||||
- name: test qml
|
||||
run: build/release/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml
|
||||
|
||||
build-ubuntu:
|
||||
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- name: "qmake"
|
||||
cmd: "./build.sh"
|
||||
- name: "cmake"
|
||||
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release -j3"
|
||||
name: build-ubuntu-${{ matrix.toolchain.name }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: remove bundled boost
|
||||
run: sudo rm -rf /usr/local/share/boost
|
||||
- name: set apt conf
|
||||
run: |
|
||||
echo "Acquire::Retries \"3\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||
echo "Acquire::http::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||
echo "Acquire::ftp::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||
- name: update apt
|
||||
run: sudo apt update
|
||||
- name: install monero dependencies
|
||||
run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev
|
||||
run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler
|
||||
- name: install monero gui dependencies
|
||||
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev xvfb
|
||||
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev libgcrypt20-dev xvfb
|
||||
- name: build
|
||||
run: ./build.sh
|
||||
run: ${{ matrix.toolchain.cmd }}
|
||||
- name: test qml
|
||||
run: xvfb-run -a build/release/bin/monero-wallet-gui --test-qml
|
||||
|
||||
build-windows:
|
||||
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- name: "qmake"
|
||||
cmd: "./build.sh release"
|
||||
- name: "cmake"
|
||||
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release-win64 -j2"
|
||||
name: build-windows-${{ matrix.toolchain.name }}
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: numworks/setup-msys2@v1
|
||||
- name: update pacman
|
||||
run: msys2do pacman -Syu --noconfirm
|
||||
- name: install monero dependencies
|
||||
run: msys2do pacman -S --noconfirm mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: eine/setup-msys2@v1
|
||||
with:
|
||||
update: true
|
||||
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5 mingw-w64-x86_64-libgcrypt
|
||||
- name: build
|
||||
run: msys2do ./build.sh release
|
||||
run: |
|
||||
sed -i 's/CONFIG\ +=\ qtquickcompiler//' monero-wallet-gui.pro
|
||||
${{ matrix.toolchain.cmd }}
|
||||
- name: test qml
|
||||
run: msys2do build/release/bin/monero-wallet-gui --test-qml
|
||||
run: build/release/bin/monero-wallet-gui --test-qml
|
||||
|
||||
1
.gitignore
vendored
@@ -13,6 +13,7 @@ monero-wallet-gui_plugin_import.cpp
|
||||
monero-wallet-gui_qml_plugin_import.cpp
|
||||
*.qmlc
|
||||
*.jsc
|
||||
qml_qmlcache.qrc
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
|
||||
220
CMakeLists.txt
@@ -3,20 +3,24 @@ project(monero-gui)
|
||||
|
||||
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
|
||||
|
||||
set(VERSION_MAJOR "14")
|
||||
set(VERSION_MAJOR "16")
|
||||
set(VERSION_MINOR "0")
|
||||
set(VERSION_REVISION "3")
|
||||
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||
set(VERSION "0.${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||
|
||||
# libwallet requires a static build, so we only allow static compilation
|
||||
set(STATIC ON)
|
||||
option(STATIC "Link libraries statically, requires static Qt")
|
||||
|
||||
option(USE_DEVICE_TREZOR ON)
|
||||
option(USE_DEVICE_TREZOR "Trezor support compilation" ON)
|
||||
option(ENABLE_PASS_STRENGTH_METER "Disable zxcvbn" OFF)
|
||||
option(WITH_SCANNER "Enable webcam QR scanner" OFF)
|
||||
option(DEV_MODE "Checkout latest monero master on build" OFF)
|
||||
|
||||
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckLinkerFlag)
|
||||
include(FindCcache)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
@@ -27,7 +31,6 @@ endif()
|
||||
set(BUILD_GUI_DEPS ON)
|
||||
set(ARCH "x86-64")
|
||||
set(BUILD_64 ON)
|
||||
set(INSTALL_VENDORED_LIBUNBOUND=ON)
|
||||
|
||||
function (add_c_flag_if_supported flag var)
|
||||
string(REPLACE "-" "_" supported ${flag}_c)
|
||||
@@ -78,6 +81,7 @@ endif()
|
||||
add_subdirectory(monero)
|
||||
set_property(TARGET wallet_merged PROPERTY FOLDER "monero")
|
||||
get_directory_property(ARCH_WIDTH DIRECTORY "monero" DEFINITION ARCH_WIDTH)
|
||||
get_directory_property(UNBOUND_LIBRARY DIRECTORY "monero" DEFINITION UNBOUND_LIBRARY)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DQT_NO_DEBUG)
|
||||
@@ -135,6 +139,9 @@ monero_gui_add_library(gui_version SOURCES version.js DEPENDS genversiongui)
|
||||
message(STATUS "${CMAKE_MODULE_PATH}")
|
||||
|
||||
# OpenSSL
|
||||
if(APPLE AND NOT OPENSSL_ROOT_DIR)
|
||||
execute_process(COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
|
||||
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
|
||||
@@ -166,7 +173,10 @@ message(STATUS "libhidapi: libraries at ${HIDAPI_LIBRARIES}")
|
||||
if(DEBUG)
|
||||
set(Boost_DEBUG ON)
|
||||
endif()
|
||||
find_package(Boost 1.62 REQUIRED COMPONENTS
|
||||
if(APPLE AND NOT BOOST_ROOT)
|
||||
execute_process(COMMAND brew --prefix boost OUTPUT_VARIABLE BOOST_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
find_package(Boost 1.58 REQUIRED COMPONENTS
|
||||
system
|
||||
filesystem
|
||||
thread
|
||||
@@ -194,21 +204,6 @@ if(MINGW)
|
||||
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
|
||||
message(STATUS "MSYS location: ${msys2_install_path}")
|
||||
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/Qt/labs/folderlistmodel")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/Qt/labs/settings")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtGraphicalEffects")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtGraphicalEffects/private")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtMultimedia")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick.2")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Controls")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Controls.2")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Dialogs")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Dialogs/Private")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Layouts")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/PrivateWidgets")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Templates.2")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Window.2")
|
||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/XmlListModel")
|
||||
# This is necessary because otherwise CMake will make Boost libraries -lfoo
|
||||
# rather than a full path. Unfortunately, this makes the shared libraries get
|
||||
# linked due to a bug in CMake which misses putting -static flags around the
|
||||
@@ -221,35 +216,66 @@ endif()
|
||||
set(QT5_LIBRARIES
|
||||
Qt5Core
|
||||
Qt5Quick
|
||||
Qt5QuickControls2
|
||||
Qt5Widgets
|
||||
Qt5Gui
|
||||
Qt5Network
|
||||
Qt5Qml
|
||||
Qt5Multimedia
|
||||
Qt5Xml
|
||||
Qt5XmlPatterns
|
||||
Qt5Svg
|
||||
Qt5Xml
|
||||
)
|
||||
|
||||
if(WITH_SCANNER)
|
||||
list(APPEND QT5_LIBRARIES Qt5Multimedia)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND QT5_LIBRARIES Qt5MacExtras)
|
||||
|
||||
if(NOT CMAKE_PREFIX_PATH)
|
||||
execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE QT5_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
list(APPEND CMAKE_PREFIX_PATH ${QT5_DIR})
|
||||
endif()
|
||||
|
||||
if(CMAKE_PREFIX_PATH)
|
||||
include_directories(${CMAKE_PREFIX_PATH}/include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
# TODO: drop this once we switch to Qt 5.14+
|
||||
pkg_check_modules(Qt5QmlModels_PKG_CONFIG QUIET Qt5QmlModels)
|
||||
if(Qt5QmlModels_PKG_CONFIG_FOUND)
|
||||
list(APPEND QT5_LIBRARIES Qt5QmlModels)
|
||||
endif()
|
||||
|
||||
# TODO: drop this once we switch to Qt 5.12+
|
||||
find_package(Qt5XmlPatterns QUIET)
|
||||
if(Qt5XmlPatterns_FOUND)
|
||||
list(APPEND QT5_LIBRARIES Qt5XmlPatterns)
|
||||
endif()
|
||||
|
||||
foreach(QT5_MODULE ${QT5_LIBRARIES})
|
||||
find_package(${QT5_MODULE} REQUIRED)
|
||||
include_directories(${${QT5_MODULE}_INCLUDE_DIRS})
|
||||
endforeach()
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(PKGCONFIG_FOUND)
|
||||
pkg_check_modules(QT5_PKG_CONFIG ${QT5_LIBRARIES})
|
||||
pkg_check_modules(QT5_PKG_CONFIG REQUIRED ${QT5_LIBRARIES})
|
||||
|
||||
if(QT5_PKG_CONFIG_FOUND)
|
||||
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
|
||||
if(STATIC)
|
||||
set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC")
|
||||
endif()
|
||||
|
||||
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
|
||||
include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS})
|
||||
link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS})
|
||||
if(QT5_PKG_CONFIG_FOUND)
|
||||
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
|
||||
if(STATIC)
|
||||
set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(JOIN ${QT5_PKG_CONFIG}_LDFLAGS_OTHER " " ${QT5_PKG_CONFIG}_LDFLAGS_OTHER)
|
||||
list(JOIN ${QT5_PKG_CONFIG}_LIBRARIES " " ${QT5_PKG_CONFIG}_LIBRARIES)
|
||||
endif()
|
||||
|
||||
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
|
||||
include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS})
|
||||
link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
list(APPEND QT5_LIBRARIES
|
||||
@@ -260,37 +286,95 @@ list(APPEND QT5_LIBRARIES
|
||||
)
|
||||
|
||||
if(STATIC)
|
||||
set(QT5_LIBRARIES
|
||||
qtquickcontrols2plugin # has to be the first one, depends on Qt5QuickControls2
|
||||
${QT5_LIBRARIES}
|
||||
declarative_multimedia
|
||||
set(QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/folderlistmodel)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/settings)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects/private)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtMultimedia)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick.2)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls.2)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs/Private)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Layouts)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/PrivateWidgets)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Templates.2)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Window.2)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/XmlListModel)
|
||||
|
||||
set(QT5_EXTRA_LIBRARIES_LIST
|
||||
qtquicktemplates2plugin
|
||||
Qt5QuickTemplates2
|
||||
qtquickcontrols2plugin
|
||||
Qt5QuickControls2
|
||||
dialogplugin
|
||||
dialogsprivateplugin
|
||||
qmlfolderlistmodelplugin
|
||||
qmlsettingsplugin
|
||||
qmlxmllistmodelplugin
|
||||
qquicklayoutsplugin
|
||||
Qt5EventDispatcherSupport
|
||||
Qt5FontDatabaseSupport
|
||||
Qt5MultimediaQuick_p
|
||||
Qt5PacketProtocol
|
||||
Qt5ThemeSupport
|
||||
)
|
||||
|
||||
if(WITH_SCANNER)
|
||||
list(APPEND QT5_EXTRA_LIBRARIES_LIST
|
||||
declarative_multimedia
|
||||
Qt5MultimediaQuick_p
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND QT5_EXTRA_LIBRARIES_LIST
|
||||
qtgraphicaleffectsplugin
|
||||
qtgraphicaleffectsprivate
|
||||
qtquick2plugin
|
||||
qtquickcontrolsplugin
|
||||
qtquicktemplates2plugin
|
||||
widgetsplugin
|
||||
windowplugin
|
||||
)
|
||||
|
||||
set(QT5_EXTRA_LIBRARIES)
|
||||
foreach(LIBRARY ${QT5_EXTRA_LIBRARIES_LIST})
|
||||
find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS} REQUIRED)
|
||||
list(APPEND QT5_EXTRA_LIBRARIES ${${LIBRARY}_LIBRARY})
|
||||
endforeach()
|
||||
|
||||
if(MINGW)
|
||||
list(APPEND QT5_LIBRARIES qtfreetype)
|
||||
list(APPEND QT5_EXTRA_LIBRARIES qtfreetype)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
list(APPEND QT5_LIBRARIES D3D11 Dwrite D2d1)
|
||||
list(APPEND QT5_EXTRA_LIBRARIES D3D11 Dwrite D2d1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(QT5_LIBRARIES
|
||||
${QT5_EXTRA_LIBRARIES}
|
||||
${QT5_LIBRARIES}
|
||||
)
|
||||
|
||||
set(QT5_INTEGRATION_LIBRARIES_LIST
|
||||
Qt5EventDispatcherSupport
|
||||
Qt5PacketProtocol
|
||||
Qt5ThemeSupport
|
||||
Qt5FontDatabaseSupport
|
||||
)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
list(APPEND QT5_INTEGRATION_LIBRARIES_LIST
|
||||
Qt5XcbQpa
|
||||
xcb-static
|
||||
Qt5ServiceSupport
|
||||
Qt5GlxSupport
|
||||
)
|
||||
endif()
|
||||
|
||||
foreach(LIBRARY ${QT5_INTEGRATION_LIBRARIES_LIST})
|
||||
find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS} REQUIRED)
|
||||
list(APPEND QT5_LIBRARIES ${${LIBRARY}_LIBRARY})
|
||||
endforeach()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
pkg_check_modules(X11XCB_XCBGLX_FONTCONFIG REQUIRED x11-xcb xcb-glx fontconfig)
|
||||
list(APPEND QT5_LIBRARIES ${X11XCB_XCBGLX_FONTCONFIG_STATIC_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Using Boost include dir at ${Boost_INCLUDE_DIRS}")
|
||||
@@ -305,7 +389,9 @@ if(MINGW)
|
||||
else()
|
||||
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
|
||||
endif()
|
||||
elseif(APPLE OR OPENBSD OR ANDROID)
|
||||
elseif(APPLE)
|
||||
set(EXTRA_LIBRARIES "-framework AppKit")
|
||||
elseif(OPENBSD OR ANDROID)
|
||||
set(EXTRA_LIBRARIES "")
|
||||
elseif(FREEBSD)
|
||||
set(EXTRA_LIBRARIES execinfo)
|
||||
@@ -369,7 +455,7 @@ if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VER
|
||||
endif()
|
||||
|
||||
# linker
|
||||
if (NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
|
||||
if (NOT APPLE AND NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
|
||||
# Windows binaries die on startup with PIE when compiled with GCC
|
||||
add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS)
|
||||
endif()
|
||||
@@ -384,21 +470,35 @@ if (noexecheap_SUPPORTED)
|
||||
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
|
||||
endif()
|
||||
|
||||
# some windows linker bits
|
||||
if (WIN32)
|
||||
add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS)
|
||||
add_linker_flag_if_supported(-Wl,--nxcompat LD_SECURITY_FLAGS)
|
||||
add_linker_flag_if_supported(-Wl,--high-entropy-va LD_SECURITY_FLAGS)
|
||||
endif()
|
||||
|
||||
if(STATIC)
|
||||
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
||||
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
||||
if(MINGW)
|
||||
add_linker_flag_if_supported(-static STATIC_FLAGS)
|
||||
else()
|
||||
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
||||
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
|
||||
# is fixed in the code (Issue #847), force compiler to be conservative.
|
||||
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
|
||||
add_cxx_flag_if_supported(-fno-strict-aliasing CXX_SECURITY_FLAGS)
|
||||
|
||||
add_c_flag_if_supported(-fPIC C_SECURITY_FLAGS)
|
||||
add_cxx_flag_if_supported(-fPIC CXX_SECURITY_FLAGS)
|
||||
|
||||
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
|
||||
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
|
||||
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_SECURITY_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_SECURITY_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
|
||||
|
||||
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
|
||||
@@ -417,8 +517,6 @@ if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(translations)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
# Required to make wallet_merged build before the gui
|
||||
add_dependencies(monero-gui wallet_merged)
|
||||
|
||||
|
||||
64
DEPLOY.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# macOS:
|
||||
|
||||
Use macOS 10.12 - 10.13 for better backwards compability.
|
||||
|
||||
1. `HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" brew install boost zmq libpgm miniupnpc libsodium expat libunwind-headers protobuf libgcrypt`
|
||||
|
||||
2. `HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" brew install --HEAD hidapi`
|
||||
|
||||
3. Get the latest LTS from here: https://www.qt.io/offline-installers and install
|
||||
|
||||
4. `export PATH=$PATH:$HOME/Qt5.12.8/5.12.8/clang_64/bin`
|
||||
|
||||
5. `git clone https://github.com/monero-project/monero-gui`
|
||||
|
||||
6. `git checkout v0.X.Y.Z`
|
||||
|
||||
7. `sed -i '' s/ARCH=\"native\"/ARCH=\"x86-64\"/g get_libwallet_api.sh`
|
||||
|
||||
8. `sed -i '' s/-O2/-O0/g monero-wallet-gui.pro`
|
||||
|
||||
9. `./build.sh`
|
||||
|
||||
10. `cd build && make deploy`
|
||||
|
||||
11. `cd release/bin/monero-wallet-gui.app/Contents/PlugIns/imageformats/`
|
||||
|
||||
12. `cp ~/Qt5.12.8/5.12.8/clang_64/plugins/imageformats/libqsvg.dylib .`
|
||||
|
||||
13. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/clang_64/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
|
||||
|
||||
14. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
|
||||
|
||||
15. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/lib/QtSvg.framework/Versions/5/QtSvg @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
|
||||
|
||||
16. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/clang_64/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
|
||||
|
||||
17. Replace the `monerod` binary inside `monero-wallet-gui.app/Contents/MacOS/` with one built using deterministic builds / gitian.
|
||||
|
||||
## Codesigning and notarizing
|
||||
|
||||
1. Save the following text as `entitlements.plist`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.disable-executable-page-protection</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
2. `codesign --deep --force --verify --verbose --options runtime --timestamp --entitlements entitlements.plist --sign 'XXXXXXXXXX' monero-wallet-gui.app`
|
||||
|
||||
You can check if this step worked by using `codesign -dvvv monero-wallet-gui.app`
|
||||
|
||||
3. `hdiutil create -fs HFS+ -srcfolder monero-gui-v0.X.Y.Z -volname monero-wallet-gui monero-gui-mac-x64-v0.X.Y.Z.dmg`
|
||||
|
||||
4. `xcrun altool -t osx --file monero-gui-mac-x64-v0.X.Y.Z.dmg --primary-bundle-id org.monero-project.monero-wallet-gui.dmg --notarize-app --username email@address.org`
|
||||
|
||||
5. `xcrun altool --notarization-info aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee -u email@address.org`
|
||||
|
||||
6. `xcrun stapler staple -v monero-gui-mac-x64-v0.X.Y.Z.dmg`
|
||||
185
Dockerfile
Normal file
@@ -0,0 +1,185 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
ARG THREADS=1
|
||||
|
||||
RUN apt update
|
||||
|
||||
RUN apt install -y automake git pkg-config python xutils-dev && \
|
||||
git clone -b xorgproto-2020.1 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xorgproto && \
|
||||
cd xorgproto && \
|
||||
git reset --hard c62e8203402cafafa5ba0357b6d1c019156c9f36 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xcbproto && \
|
||||
cd xcbproto && \
|
||||
git reset --hard 6398e42131eedddde0d98759067dde933191f049 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y libtool-bin && \
|
||||
git clone -b libXau-1.0.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxau && \
|
||||
cd libxau && \
|
||||
git reset --hard d9443b2c57b512cfb250b35707378654d86c7dea && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y libpthread-stubs0-dev && \
|
||||
git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb && \
|
||||
cd libxcb && \
|
||||
git reset --hard d34785a34f28fa6a00f8ce00d87e3132ff0f6467 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
|
||||
cd zlib && \
|
||||
git reset --hard cacf7f1d4e3d44d871b605da3b647f07d718623f && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b VER-2-10-2 --depth 1 https://git.sv.nongnu.org/r/freetype/freetype2.git && \
|
||||
cd freetype2 && \
|
||||
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \
|
||||
./autogen.sh && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --with-zlib=no && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b R_2_2_9 --depth 1 https://github.com/libexpat/libexpat && \
|
||||
cd libexpat/expat && \
|
||||
git reset --hard a7bc26b69768f7fb24f0c7976fae24b157b85b13 && \
|
||||
./buildconf.sh && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y autopoint gettext gperf libpng12-dev && \
|
||||
git clone -b 2.13.92 --depth 1 https://gitlab.freedesktop.org/fontconfig/fontconfig && \
|
||||
cd fontconfig && \
|
||||
git reset --hard b1df1101a643ae16cdfa1d83b939de2497b1bf27 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static --sysconfdir=/etc --localstatedir=/var && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \
|
||||
cd icu/icu4c/source && \
|
||||
git reset --hard e2d85306162d3a0691b070b4f0a73e4012433444 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-tests --disable-samples && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y wget && \
|
||||
wget https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.gz && \
|
||||
echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" > hashsum.txt && \
|
||||
sha256sum -c hashsum.txt && \
|
||||
tar -xvzf boost_1_73_0.tar.gz && \
|
||||
cd boost_1_73_0 && \
|
||||
./bootstrap.sh && \
|
||||
./b2 --with-atomic --with-system --with-filesystem --with-thread --with-date_time --with-chrono --with-regex --with-serialization --with-program_options --with-locale variant=release link=static runtime-link=static cflags='-fPIC' cxxflags='-fPIC' install -a --prefix=/usr
|
||||
|
||||
RUN wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz && \
|
||||
echo "ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46 openssl-1.1.1g.tar.gz" > hashsum.txt && \
|
||||
sha256sum -c hashsum.txt && \
|
||||
tar -xzf openssl-1.1.1g.tar.gz && \
|
||||
cd openssl-1.1.1g && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./config no-asm no-shared no-zlib-dynamic --openssldir=/usr && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN wget https://download.qt.io/archive/qt/5.9/5.9.7/single/qt-everywhere-opensource-src-5.9.7.tar.xz && \
|
||||
echo "1c3852aa48b5a1310108382fb8f6185560cefc3802e81ecc099f4e62ee38516c qt-everywhere-opensource-src-5.9.7.tar.xz" > hashsum.txt && \
|
||||
sha256sum -c hashsum.txt && \
|
||||
tar -xf qt-everywhere-opensource-src-5.9.7.tar.xz
|
||||
|
||||
RUN apt install -y libgl1-mesa-dev libglib2.0-dev libxkbcommon-dev && \
|
||||
cd qt-everywhere-opensource-src-5.9.7 && \
|
||||
sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \
|
||||
sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \
|
||||
sed -ri s/\(Libs:.*\)/\\1\ -lXau/ /usr/local/lib/pkgconfig/xcb.pc && \
|
||||
./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \
|
||||
-opengl desktop -qpa xcb -system-freetype -fontconfig -glib \
|
||||
-no-dbus -no-openssl -no-sql-sqlite -no-use-gold-linker \
|
||||
-qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
|
||||
-skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \
|
||||
-skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing \
|
||||
-skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools \
|
||||
-skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview \
|
||||
-skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
|
||||
-nomake examples -nomake tests -nomake tools && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN cd qt-everywhere-opensource-src-5.9.7/qttools/src/linguist/lrelease && \
|
||||
qmake && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y libudev-dev && \
|
||||
git clone -b v1.0.23 --depth 1 https://github.com/libusb/libusb && \
|
||||
cd libusb && \
|
||||
git reset --hard e782eeb2514266f6738e242cdcb18e3ae1ed06fa && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
|
||||
cd hidapi && \
|
||||
git reset --hard 7da5cc91fc0d2dbe4df4f08cd31f6ca1a262418f && \
|
||||
./bootstrap && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b libX11-1.6.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libx11 && \
|
||||
cd libx11 && \
|
||||
git reset --hard db7cca17ad7807e92a928da9d4c68a00f4836da2 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b libXext-1.3.4 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxext && \
|
||||
cd libxext && \
|
||||
git reset --hard ebb167f34a3514783966775fb12573c4ed209625 && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y libsodium-dev && \
|
||||
git clone -b v4.3.2 --depth 1 https://github.com/zeromq/libzmq && \
|
||||
cd libzmq && \
|
||||
git reset --hard a84ffa12b2eb3569ced199660bac5ad128bff1f0 && \
|
||||
./autogen.sh && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-libunwind --with-libsodium && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git && \
|
||||
cd libgpg-error && \
|
||||
git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 && \
|
||||
./autogen.sh && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-doc --disable-tests && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b libgcrypt-1.8.5 --depth 1 git://git.gnupg.org/libgcrypt.git && \
|
||||
cd libgcrypt && \
|
||||
git reset --hard 56606331bc2a80536db9fc11ad53695126007298 && \
|
||||
./autogen.sh && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-doc && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN git clone -b v3.10.0 --depth 1 https://github.com/protocolbuffers/protobuf && \
|
||||
cd protobuf && \
|
||||
git reset --hard 6d4e7fd7966c989e38024a8ea693db83758944f1 && \
|
||||
./autogen.sh && \
|
||||
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --enable-static --disable-shared && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install
|
||||
|
||||
RUN apt install -y cmake libusb-1.0-0-dev
|
||||
@@ -64,7 +64,6 @@ Rectangle {
|
||||
signal addressBookClicked()
|
||||
signal miningClicked()
|
||||
signal signClicked()
|
||||
signal merchantClicked()
|
||||
signal accountClicked()
|
||||
|
||||
function selectItem(pos) {
|
||||
@@ -72,7 +71,6 @@ Rectangle {
|
||||
if(pos === "History") menuColumn.previousButton = historyButton
|
||||
else if(pos === "Transfer") menuColumn.previousButton = transferButton
|
||||
else if(pos === "Receive") menuColumn.previousButton = receiveButton
|
||||
else if(pos === "Merchant") menuColumn.previousButton = merchantButton
|
||||
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
|
||||
else if(pos === "Mining") menuColumn.previousButton = miningButton
|
||||
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
|
||||
@@ -265,6 +263,10 @@ Rectangle {
|
||||
anchors.leftMargin: 58
|
||||
anchors.baseline: currencyLabel.baseline
|
||||
color: MoneroComponents.Style.blackTheme ? "white" : "black"
|
||||
Binding on color {
|
||||
when: balancePart1MouseArea.containsMouse || balancePart2MouseArea.containsMouse
|
||||
value: MoneroComponents.Style.orange
|
||||
}
|
||||
text: {
|
||||
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
|
||||
return balanceFiatString.split('.')[0] + "."
|
||||
@@ -286,14 +288,6 @@ Rectangle {
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: {
|
||||
balancePart1.color = MoneroComponents.Style.orange
|
||||
balancePart2.color = MoneroComponents.Style.orange
|
||||
}
|
||||
onExited: {
|
||||
balancePart1.color = Qt.binding(function() { return MoneroComponents.Style.blackTheme ? "white" : "black" })
|
||||
balancePart2.color = Qt.binding(function() { return MoneroComponents.Style.blackTheme ? "white" : "black" })
|
||||
}
|
||||
onClicked: {
|
||||
console.log("Copied to clipboard");
|
||||
clipboard.setText(balancePart1.text + balancePart2.text);
|
||||
@@ -307,7 +301,7 @@ Rectangle {
|
||||
anchors.left: balancePart1.right
|
||||
anchors.leftMargin: 2
|
||||
anchors.baseline: currencyLabel.baseline
|
||||
color: MoneroComponents.Style.blackTheme ? "white" : "black"
|
||||
color: balancePart1.color
|
||||
text: {
|
||||
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
|
||||
return balanceFiatString.split('.')[1]
|
||||
@@ -317,11 +311,10 @@ Rectangle {
|
||||
}
|
||||
font.pixelSize: 16
|
||||
MouseArea {
|
||||
id: balancePart2MouseArea
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: balancePart1MouseArea.entered()
|
||||
onExited: balancePart1MouseArea.exited()
|
||||
onClicked: balancePart1MouseArea.clicked(mouse)
|
||||
}
|
||||
}
|
||||
@@ -454,30 +447,6 @@ Rectangle {
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- Merchant tab ---------------
|
||||
|
||||
MoneroComponents.MenuButton {
|
||||
id: merchantButton
|
||||
visible: appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Merchant") + translationManager.emptyString
|
||||
symbol: qsTr("U") + translationManager.emptyString
|
||||
under: receiveButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = merchantButton
|
||||
panel.merchantClicked()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.MenuButtonDivider {
|
||||
visible: merchantButton.present && appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- History tab ---------------
|
||||
|
||||
MoneroComponents.MenuButton {
|
||||
|
||||
12
Makefile
@@ -28,13 +28,21 @@ clean:
|
||||
scanner:
|
||||
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},ON) -D WITH_SCANNER=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||
|
||||
release:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
|
||||
release-static:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||
|
||||
debug-static-win64:
|
||||
mkdir -p $(builddir)/debug && cd $(builddir)/debug && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||
mkdir -p $(builddir)/debug && cd $(builddir)/debug && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||
|
||||
debug-static-mac64:
|
||||
mkdir -p $(builddir)/debug
|
||||
cd $(builddir)/debug && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" $(topdir) && $(MAKE)
|
||||
|
||||
release-static-win64:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||
|
||||
release-win64:
|
||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=OFF -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||
|
||||
@@ -50,7 +50,10 @@ Rectangle {
|
||||
property alias contentHeight: mainFlickable.contentHeight
|
||||
property alias flickable: mainFlickable
|
||||
|
||||
property Transfer transferView: Transfer { }
|
||||
property Transfer transferView: Transfer {
|
||||
onPaymentClicked: root.paymentClicked(address, paymentId, amount, mixinCount, priority, description)
|
||||
onSweepUnmixableClicked: root.sweepUnmixableClicked()
|
||||
}
|
||||
property Receive receiveView: Receive { }
|
||||
property Merchant merchantView: Merchant { }
|
||||
property TxKey txkeyView: TxKey { }
|
||||
@@ -260,18 +263,4 @@ Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: borderLeft.right
|
||||
}
|
||||
|
||||
/* connect "payment" click */
|
||||
Connections {
|
||||
ignoreUnknownSignals: false
|
||||
target: transferView
|
||||
onPaymentClicked : {
|
||||
console.log("MiddlePanel: paymentClicked")
|
||||
paymentClicked(address, paymentId, amount, mixinCount, priority, description)
|
||||
}
|
||||
onSweepUnmixableClicked : {
|
||||
console.log("MiddlePanel: sweepUnmixableClicked")
|
||||
sweepUnmixableClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
README.md
@@ -36,7 +36,7 @@ As with many development projects, the repository on Github is considered to be
|
||||
|
||||
Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard.
|
||||
|
||||
The Monero donation address is: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`)
|
||||
The Monero donation address is: `888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`)
|
||||
|
||||
The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H`
|
||||
|
||||
@@ -62,6 +62,11 @@ Do you speak a second language and would like to help translate the Monero GUI?
|
||||
|
||||
If you need help/support or any info you can contact the localization workgroup on the IRC channel #monero-translations (relayed on matrix/riot and MatterMost) or by email at translate[at]getmonero[dot]org. For more info about the Localization workgroup: [github.com/monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations)
|
||||
|
||||
Status of the translations:
|
||||
<a href="https://translate.getmonero.org/engage/monero/?utm_source=widget">
|
||||
<img src="https://translate.getmonero.org/widgets/monero/-/gui-wallet/horizontal-auto.svg" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
## Installing the Monero GUI from a package
|
||||
|
||||
Packages are available for
|
||||
@@ -76,6 +81,28 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
*Note*: Qt 5.9.7 is the minimum version required to build the GUI.
|
||||
|
||||
### Building Linux static binaries with Docker (any OS)
|
||||
|
||||
1. Install Docker [https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)
|
||||
2. Clone the repository
|
||||
```
|
||||
git clone --recursive https://github.com/monero-project/monero-gui.git
|
||||
```
|
||||
3. Prepare build environment
|
||||
```
|
||||
cd monero-gui
|
||||
docker build --tag monero:build-env-gui --build-arg THREADS=4 .
|
||||
```
|
||||
\* `4` - number of CPU threads to use
|
||||
|
||||
4. Build
|
||||
```
|
||||
docker run --rm -it -v <MONERO_GUI_DIR_FULL_PATH>:/monero-gui -w /monero-gui monero:build-env-gui sh -c 'USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release-static -j4'
|
||||
```
|
||||
\* `<MONERO_GUI_DIR_FULL_PATH>` - absolute path to `monero-gui` directory
|
||||
\* `4` - number of CPU threads to use
|
||||
5. Monero GUI Linux static binaries will be placed in `monero-gui/build/release/bin` directory
|
||||
|
||||
### On Linux:
|
||||
|
||||
(Tested on Ubuntu 17.10 x64, Ubuntu 18.04 x64 and Gentoo x64)
|
||||
@@ -84,15 +111,15 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
- For Debian distributions (Debian, Ubuntu, Mint, Tails...)
|
||||
|
||||
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler`
|
||||
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev`
|
||||
|
||||
- For Gentoo
|
||||
|
||||
`sudo emerge app-arch/xz-utils app-doc/doxygen dev-cpp/gtest dev-libs/boost dev-libs/expat dev-libs/openssl dev-util/cmake media-gfx/graphviz net-dns/unbound net-libs/ldns net-libs/miniupnpc net-libs/zeromq sys-libs/libunwind dev-libs/libsodium dev-libs/hidapi`
|
||||
`sudo emerge app-arch/xz-utils app-doc/doxygen dev-cpp/gtest dev-libs/boost dev-libs/expat dev-libs/openssl dev-util/cmake media-gfx/graphviz net-dns/unbound net-libs/ldns net-libs/miniupnpc net-libs/zeromq sys-libs/libunwind dev-libs/libsodium dev-libs/hidapi dev-libs/libgcrypt`
|
||||
|
||||
- For Fedora
|
||||
|
||||
`sudo dnf install make automake cmake gcc-c++ boost-devel miniupnpc-devel graphviz doxygen unbound-devel libunwind-devel pkgconfig openssl-devel libcurl-devel hidapi-devel libusb-devel zeromq-devel`
|
||||
`sudo dnf install make automake cmake gcc-c++ boost-devel miniupnpc-devel graphviz doxygen unbound-devel libunwind-devel pkgconfig openssl-devel libcurl-devel hidapi-devel libusb-devel zeromq-devel libgcrypt-devel`
|
||||
|
||||
2. Install Qt:
|
||||
|
||||
@@ -144,7 +171,7 @@ The executable can be found in the build/release/bin folder.
|
||||
|
||||
3. Install [monero](https://github.com/monero-project/monero) dependencies:
|
||||
|
||||
`brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf`
|
||||
`brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf libgcrypt`
|
||||
|
||||
4. Install Qt:
|
||||
|
||||
@@ -169,6 +196,8 @@ The executable can be found in the build/release/bin folder.
|
||||
|
||||
The executable can be found in the `build/release/bin` folder.
|
||||
|
||||
For building an application bundle see `DEPLOY.md`.
|
||||
|
||||
### On Windows:
|
||||
|
||||
The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not officially supported anymore.
|
||||
@@ -180,7 +209,7 @@ The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not off
|
||||
3. Install MSYS2 packages for Monero dependencies; the needed 64-bit packages have `x86_64` in their names
|
||||
|
||||
```
|
||||
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb
|
||||
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb mingw-w64-x86_64-libgcrypt
|
||||
```
|
||||
|
||||
Optional : To build the flag `WITH_SCANNER`
|
||||
|
||||
7
build.sh
@@ -100,11 +100,8 @@ fi
|
||||
|
||||
# force version update
|
||||
get_tag
|
||||
echo "var GUI_VERSION = \"$TAGNAME\"" > version.js
|
||||
pushd "$MONERO_DIR"
|
||||
get_tag
|
||||
popd
|
||||
echo "var GUI_MONERO_VERSION = \"$TAGNAME\"" >> version.js
|
||||
GUI_VERSION=$(echo "$TAGNAME" | sed 's/^v\([[:digit:]]\)/\1/')
|
||||
echo "var GUI_VERSION = \"$GUI_VERSION\"" > version.js
|
||||
|
||||
cd build
|
||||
if ! QMAKE=$(find_command qmake qmake-qt5); then
|
||||
|
||||
56
cmake/FindCcache.cmake
Normal file
@@ -0,0 +1,56 @@
|
||||
# Copyright (c) 2014-2020, The Monero Project
|
||||
#
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are
|
||||
# permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
# conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
# of conditions and the following disclaimer in the documentation and/or other
|
||||
# materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without specific
|
||||
# prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
# - Try to find readline include dirs and libraries
|
||||
#
|
||||
# Automatically finds ccache build accelerator, if it's found in PATH.
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# project(monero)
|
||||
# include(FindCcache) # Include AFTER the project() macro to be able to reach the CMAKE_CXX_COMPILER variable
|
||||
#
|
||||
# Properties modified by this module:
|
||||
#
|
||||
# GLOBAL PROPERTY RULE_LAUNCH_COMPILE set to ccache, when ccache found
|
||||
# GLOBAL PROPERTY RULE_LAUNCH_LINK set to ccache, when ccache found
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if (CCACHE_FOUND)
|
||||
set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp")
|
||||
file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }")
|
||||
execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET)
|
||||
if (${RET} EQUAL 0)
|
||||
message("found usable ccache: ${CCACHE_FOUND}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}")
|
||||
else()
|
||||
message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}")
|
||||
endif()
|
||||
else()
|
||||
message("ccache NOT found!")
|
||||
endif()
|
||||
@@ -28,37 +28,40 @@
|
||||
#
|
||||
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
# Check what commit we're on
|
||||
execute_process(COMMAND "${GIT}" rev-parse --short=9 HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
function (git_get_version_tag git directory result_var)
|
||||
execute_process(COMMAND "${git}" rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${directory}
|
||||
OUTPUT_VARIABLE COMMIT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT COMMIT)
|
||||
message(WARNING "${directory}: cannot determine current commit. Make sure that you are building from a Git working tree")
|
||||
set(${result_var} "unknown" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(RET)
|
||||
# Something went wrong, set the version tag to -unknown
|
||||
|
||||
message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.")
|
||||
set(VERSIONTAG "unknown")
|
||||
configure_file("src/version.js.in" "${TO}")
|
||||
else()
|
||||
string(SUBSTRING ${COMMIT} 0 9 COMMIT)
|
||||
message(STATUS "You are currently on commit ${COMMIT}")
|
||||
|
||||
# Get all the tags
|
||||
execute_process(COMMAND "${GIT}" rev-list --tags --max-count=1 --abbrev-commit RESULT_VARIABLE RET OUTPUT_VARIABLE TAGGEDCOMMIT OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if(NOT TAGGEDCOMMIT)
|
||||
message(WARNING "Cannot determine most recent tag. Make sure that you are building either from a Git working tree or from a source archive.")
|
||||
set(VERSIONTAG "${COMMIT}")
|
||||
else()
|
||||
message(STATUS "The most recent tag was at ${TAGGEDCOMMIT}")
|
||||
|
||||
# Check if we're building that tagged commit or a different one
|
||||
if(COMMIT STREQUAL TAGGEDCOMMIT)
|
||||
message(STATUS "You are building a tagged release")
|
||||
set(VERSIONTAG "release")
|
||||
else()
|
||||
message(STATUS "You are ahead of or behind a tagged release")
|
||||
set(VERSIONTAG "${COMMIT}")
|
||||
endif()
|
||||
endif()
|
||||
execute_process(COMMAND "${git}" describe --tags --exact-match
|
||||
WORKING_DIRECTORY ${directory}
|
||||
OUTPUT_VARIABLE TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(TAG)
|
||||
message(STATUS "${directory}: building tagged release ${TAG}-${COMMIT}")
|
||||
set(${result_var} "${TAG}-${COMMIT}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
configure_file("src/version.js.in" "${TO}")
|
||||
endif()
|
||||
execute_process(COMMAND "${git}" describe --tags --long
|
||||
WORKING_DIRECTORY ${directory}
|
||||
OUTPUT_VARIABLE MOST_RECENT_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(MOST_RECENT_TAG)
|
||||
message(STATUS "${directory}: ahead of or behind a tagged release, building ${MOST_RECENT_TAG}")
|
||||
set(${result_var} "${MOST_RECENT_TAG}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "${directory}: building ${COMMIT} commit")
|
||||
set(${result_var} "${COMMIT}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -26,9 +26,8 @@
|
||||
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
function (write_static_version_header hash)
|
||||
set(VERSIONTAG "${hash}")
|
||||
configure_file("${CMAKE_SOURCE_DIR}/version.js.in" "${CMAKE_SOURCE_DIR}/version.js")
|
||||
function (write_static_version_header VERSION_TAG_GUI)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/version.js.in" "${CMAKE_CURRENT_SOURCE_DIR}/version.js")
|
||||
endfunction ()
|
||||
|
||||
find_package(Git QUIET)
|
||||
@@ -37,16 +36,14 @@ if ("$Format:$" STREQUAL "")
|
||||
write_static_version_header("release")
|
||||
elseif (GIT_FOUND OR Git_FOUND)
|
||||
message(STATUS "Found Git: ${GIT_EXECUTABLE}")
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/version.js"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
"-D" "GIT=${GIT_EXECUTABLE}"
|
||||
"-D" "TO=${CMAKE_SOURCE_DIR}/version.js"
|
||||
"-P" "cmake/GenVersionGui.cmake"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
include(GitGetVersionTag)
|
||||
git_get_version_tag(${GIT_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR} VERSION_TAG_GUI)
|
||||
STRING(REGEX REPLACE "^v([0-9])" "\\1" VERSION_TAG_GUI ${VERSION_TAG_GUI})
|
||||
write_static_version_header(${VERSION_TAG_GUI})
|
||||
else()
|
||||
message(STATUS "WARNING: Git was not found!")
|
||||
write_static_version_header("unknown")
|
||||
endif ()
|
||||
add_custom_target(genversiongui ALL
|
||||
DEPENDS "${CMAKE_SOURCE_DIR}/version.js")
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/version.js")
|
||||
|
||||
106
components/AdvancedOptionsItem.qml
Normal file
@@ -0,0 +1,106 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import FontAwesome 1.0
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
RowLayout {
|
||||
id: advancedOptionsItem
|
||||
|
||||
property alias title: title.text
|
||||
property alias button1: button1
|
||||
property alias button2: button2
|
||||
property alias button3: button3
|
||||
property alias helpTextLarge: helpTextLarge
|
||||
property alias helpTextSmall: helpTextSmall
|
||||
|
||||
RowLayout {
|
||||
id: titlecolumn
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
Layout.preferredWidth: 195
|
||||
Layout.maximumWidth: 195
|
||||
Layout.leftMargin: 10
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: title
|
||||
fontSize: 14
|
||||
}
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: iconLabel
|
||||
fontSize: 12
|
||||
text: FontAwesome.questionCircle
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
opacity: 0.3
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: helpText.visible = !helpText.visible
|
||||
onEntered: parent.opacity = 0.4
|
||||
onExited: parent.opacity = 0.3
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: separator
|
||||
Layout.fillWidth: true
|
||||
height: 10
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: false
|
||||
spacing: 12
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
StandardButton {
|
||||
id: button1
|
||||
small: true
|
||||
visible: button1.text
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: button2
|
||||
small: true
|
||||
visible: button2.text
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: button3
|
||||
small: true
|
||||
visible: button3.text
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: helpText
|
||||
visible: false
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: helpTextLarge
|
||||
visible: helpTextLarge.text
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 13
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: helpTextSmall
|
||||
visible: helpTextSmall.text
|
||||
Layout.leftMargin: 5
|
||||
textFormat: Text.RichText
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 12
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ Item {
|
||||
visible: checkBox.border
|
||||
anchors.fill: parent
|
||||
radius: 3
|
||||
color: "transparent"
|
||||
color: checkBox.enabled ? "transparent" : MoneroComponents.Style.inputBoxBackgroundDisabled
|
||||
border.color:
|
||||
if(checkBox.checked){
|
||||
return MoneroComponents.Style.inputBorderColorActive;
|
||||
|
||||
@@ -117,7 +117,7 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: datePicker
|
||||
onCurrentDateChanged: {
|
||||
function onCurrentDateChanged() {
|
||||
dateInput.setDate(datePicker.currentDate)
|
||||
}
|
||||
}
|
||||
|
||||
104
components/DevicePassphraseDialog.qml
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import "." as MoneroComponents
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var onAcceptedCallback
|
||||
property var onWalletEntryCallback
|
||||
property var onRejectedCallback
|
||||
|
||||
function open(canEnterOnDevice_) {
|
||||
var canEnterOnDevice = canEnterOnDevice_ !== null ? canEnterOnDevice_ : canEnterOnDevice
|
||||
root.visible = true;
|
||||
|
||||
if (canEnterOnDevice) {
|
||||
entryChooserDialog.okText = qsTr("Hardware wallet")
|
||||
entryChooserDialog.cancelText = qsTr("Computer")
|
||||
entryChooserDialog.open()
|
||||
} else {
|
||||
openPassphraseDialog()
|
||||
}
|
||||
}
|
||||
|
||||
function openPassphraseDialog() {
|
||||
root.visible = true
|
||||
passphraseDialog.openPassphraseDialog()
|
||||
}
|
||||
|
||||
function close() {
|
||||
root.visible = false;
|
||||
if (entryChooserDialog.visible)
|
||||
entryChooserDialog.close()
|
||||
if (passphraseDialog.visible)
|
||||
passphraseDialog.close()
|
||||
}
|
||||
|
||||
StandardDialog {
|
||||
id: entryChooserDialog
|
||||
title: qsTr("Hardware wallet passphrase") + translationManager.emptyString
|
||||
text: qsTr("Please select where you want to enter passphrase.\nIt is recommended to enter passphrase on the hardware wallet for better security.") + translationManager.emptyString
|
||||
|
||||
onAccepted: {
|
||||
if (onWalletEntryCallback){
|
||||
onWalletEntryCallback()
|
||||
}
|
||||
}
|
||||
|
||||
onRejected: {
|
||||
openPassphraseDialog()
|
||||
}
|
||||
|
||||
onCloseCallback: {
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
|
||||
PasswordDialog {
|
||||
id: passphraseDialog
|
||||
anchors.fill: parent
|
||||
passphraseDialogMode: true
|
||||
|
||||
onAcceptedPassphrase: {
|
||||
if (onAcceptedCallback)
|
||||
onAcceptedCallback(passphraseDialog.password);
|
||||
}
|
||||
|
||||
onRejectedPassphrase: {
|
||||
if (onRejectedCallback)
|
||||
onRejectedCallback();
|
||||
}
|
||||
|
||||
onCloseCallback: {
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ Item {
|
||||
property alias fontPixelSize: inlineText.font.pixelSize
|
||||
property alias fontFamily: inlineText.font.family
|
||||
property alias buttonColor: rect.color
|
||||
property alias buttonHeight: rect.height
|
||||
signal clicked()
|
||||
|
||||
function doClick() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
@@ -53,7 +53,7 @@ Drawer {
|
||||
y: titleBar.height
|
||||
|
||||
background: Rectangle {
|
||||
color: "#0d0d0d"
|
||||
color: MoneroComponents.Style.blackTheme ? "#0d0d0d" : "white"
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
@@ -79,9 +79,24 @@ Drawer {
|
||||
width: sideBar.width
|
||||
height: 32
|
||||
|
||||
Rectangle {
|
||||
id: flagRect
|
||||
height: 24
|
||||
width: 24
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "transparent"
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: flag
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 16
|
||||
anchors.leftMargin: 30
|
||||
font.bold: true
|
||||
font.pixelSize: 14
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
@@ -118,8 +133,10 @@ Drawer {
|
||||
// set wizard language settings
|
||||
wizard.language_locale = locale;
|
||||
wizard.language_wallet = wallet_language;
|
||||
wizard.language_language = display_name + " (" + locale_spl[1] + ") ";
|
||||
sideBar.close()
|
||||
wizard.language_language = display_name;
|
||||
|
||||
appWindow.showStatusMessage(qsTr("Language changed."), 3);
|
||||
appWindow.toggleLanguageView();
|
||||
}
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
@@ -134,15 +151,8 @@ Drawer {
|
||||
}
|
||||
}
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {
|
||||
// @TODO: QT 5.9 introduces `policy: ScrollBar.AlwaysOn`
|
||||
active: true
|
||||
contentItem.opacity: 0.7
|
||||
onActiveChanged: {
|
||||
if (!active) {
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
onActiveChanged: if (!active && !isMac) active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import FontAwesome 1.0
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
@@ -36,6 +37,10 @@ Item {
|
||||
property alias input: input
|
||||
property alias text: input.text
|
||||
|
||||
property bool password: false
|
||||
property bool passwordHidden: true
|
||||
property var passwordLinked: null
|
||||
|
||||
property alias placeholderText: placeholderLabel.text
|
||||
property bool placeholderCenter: false
|
||||
property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name
|
||||
@@ -48,7 +53,6 @@ Item {
|
||||
property alias validator: input.validator
|
||||
property alias readOnly : input.readOnly
|
||||
property alias cursorPosition: input.cursorPosition
|
||||
property alias echoMode: input.echoMode
|
||||
property alias inlineButton: inlineButtonId
|
||||
property alias inlineButtonText: inlineButtonId.text
|
||||
property alias inlineIcon: inlineIcon.visible
|
||||
@@ -109,6 +113,31 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function isPasswordHidden() {
|
||||
if (password) {
|
||||
return passwordHidden;
|
||||
}
|
||||
if (passwordLinked) {
|
||||
return passwordLinked.passwordHidden;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
text = "";
|
||||
if (!passwordLinked) {
|
||||
passwordHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function passwordToggle() {
|
||||
if (passwordLinked) {
|
||||
passwordLinked.passwordHidden = !passwordLinked.passwordHidden;
|
||||
} else {
|
||||
passwordHidden = !passwordHidden;
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: inputLabel
|
||||
anchors.top: parent.top
|
||||
@@ -210,6 +239,27 @@ Item {
|
||||
onTextChanged: item.textUpdated()
|
||||
topPadding: 10
|
||||
bottomPadding: 10
|
||||
echoMode: isPasswordHidden() ? TextInput.Password : TextInput.Normal
|
||||
|
||||
MoneroComponents.Label {
|
||||
visible: password || passwordLinked
|
||||
fontSize: 20
|
||||
text: isPasswordHidden() ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: eyeMouseArea.containsMouse ? 0.9 : 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
id: eyeMouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: passwordToggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
|
||||
@@ -87,6 +87,8 @@ ColumnLayout {
|
||||
|
||||
property alias inlineButton: inlineButtonId
|
||||
property bool inlineButtonVisible: false
|
||||
property alias inlineButton2: inlineButton2Id
|
||||
property bool inlineButton2Visible: false
|
||||
|
||||
signal labelButtonClicked();
|
||||
signal inputLabelLinkActivated();
|
||||
@@ -202,5 +204,12 @@ ColumnLayout {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
id: inlineButton2Id
|
||||
visible: (inlineButton2Id.text || inlineButton2Id.icon) && inlineButton2Visible ? true : false
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: inlineButtonVisible ? 48 : 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ Rectangle {
|
||||
height: present ? ((appWindow.height >= 800) ? 44 : 38 ) : 0
|
||||
|
||||
LinearGradient {
|
||||
visible: isOpenGL && button.checked
|
||||
visible: isOpenGL && (button.checked || buttonArea.containsMouse)
|
||||
height: parent.height
|
||||
width: 260
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -75,13 +75,15 @@ Rectangle {
|
||||
GradientStop { position: 0.0; color: MoneroComponents.Style.menuButtonGradientStart }
|
||||
GradientStop { position: 1.0; color: MoneroComponents.Style.menuButtonGradientStop }
|
||||
}
|
||||
opacity: button.checked ? 1 : 0.3
|
||||
}
|
||||
|
||||
// fallback hover effect when opengl is not available
|
||||
Rectangle {
|
||||
visible: !isOpenGL && button.checked
|
||||
visible: !isOpenGL && (button.checked || buttonArea.containsMouse)
|
||||
anchors.fill: parent
|
||||
color: MoneroComponents.Style.menuButtonFallbackBackgroundColor
|
||||
opacity: button.checked ? 1 : 0.3
|
||||
}
|
||||
|
||||
// button decorations that are subject to leftMargin offsets
|
||||
|
||||
@@ -41,9 +41,7 @@ import "../js/Utils.js" as Utils
|
||||
Item {
|
||||
id: root
|
||||
visible: false
|
||||
z: parent.z + 2
|
||||
|
||||
property bool isHidden: true
|
||||
property alias password: passwordInput1.text
|
||||
property string walletName
|
||||
property string errorText
|
||||
@@ -61,13 +59,11 @@ Item {
|
||||
signal closeCallback()
|
||||
|
||||
function _openInit(walletName, errorText) {
|
||||
isHidden = true
|
||||
capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||
passwordInput1.echoMode = TextInput.Password
|
||||
passwordInput2.echoMode = TextInput.Password
|
||||
passwordInput1.text = ""
|
||||
passwordInput2.text = ""
|
||||
passwordInput1.forceActiveFocus();
|
||||
passwordInput1.reset();
|
||||
passwordInput2.reset();
|
||||
if(!appWindow.currentWallet || appWindow.active)
|
||||
passwordInput1.input.forceActiveFocus();
|
||||
root.walletName = walletName ? walletName : ""
|
||||
errorTextLabel.text = errorText ? errorText : "";
|
||||
leftPanel.enabled = false
|
||||
@@ -116,10 +112,29 @@ Item {
|
||||
closeCallback();
|
||||
}
|
||||
|
||||
function toggleIsHidden() {
|
||||
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
||||
isHidden = !isHidden;
|
||||
function onOk() {
|
||||
if (!passwordDialogMode && passwordInput1.text !== passwordInput2.text) {
|
||||
return;
|
||||
}
|
||||
root.close()
|
||||
if (passwordDialogMode) {
|
||||
root.accepted()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
root.close()
|
||||
if (passwordDialogMode) {
|
||||
root.rejected()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -184,15 +199,11 @@ Item {
|
||||
text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
|
||||
}
|
||||
|
||||
MoneroComponents.Input {
|
||||
MoneroComponents.LineEdit {
|
||||
id: passwordInput1
|
||||
password: true
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
KeyNavigation.tab: {
|
||||
if (passwordDialogMode) {
|
||||
return okButton
|
||||
@@ -200,81 +211,12 @@ Item {
|
||||
return passwordInput2
|
||||
}
|
||||
}
|
||||
implicitHeight: 50
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
|
||||
MoneroEffects.ColorTransition {
|
||||
targetObj: parent
|
||||
blackColor: "black"
|
||||
whiteColor: "#A9FFFFFF"
|
||||
}
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden();
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.enabled: root.visible
|
||||
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
||||
Keys.onReturnPressed: {
|
||||
if (!passwordDialogMode && passwordInput1.text !== passwordInput2.text) {
|
||||
return;
|
||||
}
|
||||
root.close()
|
||||
if (passwordDialogMode) {
|
||||
root.accepted()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
if (passwordDialogMode) {
|
||||
root.rejected()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: root.onOk()
|
||||
Keys.onReturnPressed: root.onOk()
|
||||
Keys.onEscapePressed: root.onCancel()
|
||||
}
|
||||
|
||||
// padding
|
||||
@@ -298,81 +240,19 @@ Item {
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
MoneroComponents.Input {
|
||||
MoneroComponents.LineEdit {
|
||||
id: passwordInput2
|
||||
passwordLinked: passwordInput1
|
||||
visible: !passwordDialogMode
|
||||
Layout.topMargin: 6
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontLight.name
|
||||
font.pixelSize: 24
|
||||
echoMode: TextInput.Password
|
||||
KeyNavigation.tab: okButton
|
||||
implicitHeight: 50
|
||||
bottomPadding: 10
|
||||
leftPadding: 10
|
||||
topPadding: 10
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
||||
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
||||
|
||||
MoneroComponents.Label {
|
||||
fontSize: 20
|
||||
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||
opacity: 0.7
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 15
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
toggleIsHidden()
|
||||
}
|
||||
onEntered: {
|
||||
parent.opacity = 0.9
|
||||
parent.fontSize = 24
|
||||
}
|
||||
onExited: {
|
||||
parent.opacity = 0.7
|
||||
parent.fontSize = 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.enabled: root.visible
|
||||
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
||||
Keys.onReturnPressed: {
|
||||
if (passwordInput1.text === passwordInput2.text) {
|
||||
root.close()
|
||||
if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
}
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: root.onOk()
|
||||
Keys.onReturnPressed: root.onOk()
|
||||
Keys.onEscapePressed: root.onCancel()
|
||||
}
|
||||
|
||||
// padding
|
||||
@@ -397,16 +277,7 @@ Item {
|
||||
small: true
|
||||
text: qsTr("Cancel") + translationManager.emptyString
|
||||
KeyNavigation.tab: passwordInput1
|
||||
onClicked: {
|
||||
root.close()
|
||||
if (passwordDialogMode) {
|
||||
root.rejected()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.rejectedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.rejectedPassphrase()
|
||||
}
|
||||
}
|
||||
onClicked: onCancel()
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
@@ -415,16 +286,7 @@ Item {
|
||||
text: qsTr("Ok") + translationManager.emptyString
|
||||
KeyNavigation.tab: cancelButton
|
||||
enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text
|
||||
onClicked: {
|
||||
root.close()
|
||||
if (passwordDialogMode) {
|
||||
root.accepted()
|
||||
} else if (newPasswordDialogMode) {
|
||||
root.acceptedNewPassword()
|
||||
} else if (passphraseDialogMode) {
|
||||
root.acceptedPassphrase()
|
||||
}
|
||||
}
|
||||
onClicked: onOk()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,21 +29,23 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Window 2.1
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: MoneroComponents.Style.blackTheme ? "white" : "transparent"
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "white"
|
||||
visible: false
|
||||
radius: 10
|
||||
border.color: MoneroComponents.Style.blackTheme ? Qt.rgba(255, 255, 255, 0.25) : Qt.rgba(0, 0, 0, 0.25)
|
||||
border.width: 1
|
||||
z: 11
|
||||
property alias messageText: messageTitle.text
|
||||
property alias heightProgressText : heightProgress.text
|
||||
|
||||
width: 200
|
||||
height: 100
|
||||
opacity: 0.7
|
||||
width: 100
|
||||
height: 50
|
||||
|
||||
function show() {
|
||||
root.visible = true;
|
||||
@@ -56,44 +58,55 @@ Rectangle {
|
||||
ColumnLayout {
|
||||
id: rootLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.centerIn: parent
|
||||
|
||||
anchors.leftMargin: 30
|
||||
anchors.rightMargin: 30
|
||||
|
||||
spacing: 12
|
||||
spacing: 21
|
||||
|
||||
BusyIndicator {
|
||||
running: parent.visible
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
Layout.preferredHeight: 80
|
||||
|
||||
Image {
|
||||
id: imgLogo
|
||||
width: 60
|
||||
height: 60
|
||||
anchors.centerIn: parent
|
||||
source: "qrc:///images/monero-vector.svg"
|
||||
mipmap: true
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
running: parent.visible
|
||||
anchors.centerIn: imgLogo
|
||||
style: BusyIndicatorStyle {
|
||||
indicator: Image {
|
||||
visible: control.running
|
||||
source: "qrc:///images/busy-indicator.png"
|
||||
RotationAnimator on rotation {
|
||||
running: control.running
|
||||
loops: Animation.Infinite
|
||||
duration: 1000
|
||||
from: 0
|
||||
to: 360
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: messageTitle
|
||||
text: "Please wait..."
|
||||
font {
|
||||
pixelSize: 22
|
||||
}
|
||||
text: qsTr("Please wait...") + translationManager.emptyString
|
||||
font.pixelSize: 24
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
themeTransition: false
|
||||
color: "black"
|
||||
}
|
||||
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: heightProgress
|
||||
font {
|
||||
pixelSize: 18
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
themeTransition: false
|
||||
color: "black"
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ Rectangle {
|
||||
function updateProgress(currentBlock,targetBlock, blocksToSync, statusTxt){
|
||||
if(targetBlock > 0) {
|
||||
var remaining = (currentBlock < targetBlock) ? targetBlock - currentBlock : 0
|
||||
var progressLevel = (blocksToSync > 0 && blocksToSync != remaining) ? (100*(blocksToSync - remaining)/blocksToSync).toFixed(0) : (100*(currentBlock / targetBlock)).toFixed(0)
|
||||
var progressLevel = (blocksToSync > 0 ) ? (100*(blocksToSync - remaining)/blocksToSync).toFixed(0) : 100
|
||||
fillLevel = progressLevel
|
||||
if(typeof statusTxt != "undefined" && statusTxt != "") {
|
||||
progressText.text = statusTxt;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2014-2018, The Monero Project
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
@@ -53,6 +53,7 @@ Rectangle {
|
||||
script: {
|
||||
root.visible = true
|
||||
camera.captureMode = Camera.CaptureStillImage
|
||||
camera.cameraState = Camera.ActiveState
|
||||
camera.start()
|
||||
finder.enabled = true
|
||||
}
|
||||
@@ -65,6 +66,7 @@ Rectangle {
|
||||
camera.stop()
|
||||
root.visible = false
|
||||
finder.enabled = false
|
||||
camera.cameraState = Camera.UnloadedState
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +76,7 @@ Rectangle {
|
||||
id: camera
|
||||
objectName: "qrCameraQML"
|
||||
captureMode: Camera.CaptureStillImage
|
||||
cameraState: Camera.UnloadedState
|
||||
|
||||
focus {
|
||||
focusMode: Camera.FocusContinuous
|
||||
@@ -83,9 +86,14 @@ Rectangle {
|
||||
id : finder
|
||||
objectName: "QrFinder"
|
||||
onDecoded : {
|
||||
root.qrcode_decoded(address, payment_id, amount, tx_description, recipient_name, extra_parameters)
|
||||
root.state = "Stopped"
|
||||
}
|
||||
const parsed = walletManager.parse_uri_to_object(data);
|
||||
if (!parsed.error) {
|
||||
root.qrcode_decoded(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description, parsed.recipient_name, parsed.extra_parameters);
|
||||
root.state = "Stopped";
|
||||
} else {
|
||||
onNotifyError(parsed.error);
|
||||
}
|
||||
}
|
||||
onNotifyError : {
|
||||
if( warning )
|
||||
messageDialog.icon = StandardIcon.Critical
|
||||
|
||||
@@ -43,6 +43,9 @@ GridLayout {
|
||||
property alias daemonAddrLabelText: daemonAddr.labelText
|
||||
property alias daemonPortLabelText: daemonPort.labelText
|
||||
|
||||
property string initialAddress: ""
|
||||
property var initialHostPort: initialAddress.match(/^(.*?)(?:\:?(\d*))$/)
|
||||
|
||||
// TODO: LEGACY; remove these placeHolder variables when
|
||||
// the wizards get redesigned to the black-theme
|
||||
property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name
|
||||
@@ -58,6 +61,9 @@ GridLayout {
|
||||
property bool lineEditFontBold: false
|
||||
property int lineEditFontSize: 15
|
||||
|
||||
// Author: David M. Syzdek https://github.com/syzdek https://gist.github.com/syzdek/6086792
|
||||
readonly property var ipv6Regex: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/
|
||||
|
||||
signal editingFinished()
|
||||
signal textChanged()
|
||||
|
||||
@@ -91,8 +97,12 @@ GridLayout {
|
||||
fontColor: lineEditFontColor
|
||||
fontBold: lineEditFontBold
|
||||
fontSize: lineEditFontSize
|
||||
onEditingFinished: root.editingFinished()
|
||||
onEditingFinished: {
|
||||
text = text.replace(ipv6Regex, "[$1]");
|
||||
root.editingFinished();
|
||||
}
|
||||
onTextChanged: root.textChanged()
|
||||
text: initialHostPort[1]
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
@@ -114,5 +124,6 @@ GridLayout {
|
||||
|
||||
onEditingFinished: root.editingFinished()
|
||||
onTextChanged: root.textChanged()
|
||||
text: initialHostPort[2]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,71 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import FontAwesome 1.0
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
ColumnLayout {
|
||||
property alias buttonText: button.text
|
||||
property alias description: description.text
|
||||
property alias title: title.text
|
||||
id: settingsListItem
|
||||
property alias iconText: iconLabel.text
|
||||
property alias description: area.text
|
||||
property alias title: header.text
|
||||
property bool isLast: false
|
||||
signal clicked()
|
||||
|
||||
id: settingsListItem
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
// divider
|
||||
Layout.preferredHeight: 1
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 8
|
||||
color: MoneroComponents.Style.dividerColor
|
||||
opacity: MoneroComponents.Style.dividerOpacity
|
||||
}
|
||||
Layout.minimumHeight: 75
|
||||
Layout.preferredHeight: rect.height + 15
|
||||
color: "transparent"
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
Rectangle {
|
||||
id: divider
|
||||
anchors.topMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 1
|
||||
color: MoneroComponents.Style.dividerColor
|
||||
opacity: MoneroComponents.Style.dividerOpacity
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 0
|
||||
Rectangle {
|
||||
id: rect
|
||||
width: parent.width
|
||||
height: header.height + area.contentHeight
|
||||
color: "transparent";
|
||||
anchors.left: parent.left
|
||||
anchors.bottomMargin: 4
|
||||
anchors.topMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Rectangle {
|
||||
id: icon
|
||||
color: "transparent"
|
||||
height: 32
|
||||
width: 32
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 16
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: iconLabel
|
||||
fontSize: 32
|
||||
fontFamily: FontAwesome.fontFamilySolid
|
||||
anchors.centerIn: parent
|
||||
fontColor: MoneroComponents.Style.defaultFontColor
|
||||
styleName: "Solid"
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: title
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 20
|
||||
Layout.topMargin: 8
|
||||
id: header
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: 16
|
||||
anchors.top: parent.top
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
opacity: MoneroComponents.Style.blackTheme ? 1.0 : 0.8
|
||||
font.bold: true
|
||||
@@ -43,23 +73,43 @@ ColumnLayout {
|
||||
font.pixelSize: 16
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlainArea {
|
||||
id: description
|
||||
Text {
|
||||
id: area
|
||||
anchors.top: header.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: 16
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
colorBlackTheme: MoneroComponents.Style._b_dimmedFontColor
|
||||
colorWhiteTheme: MoneroComponents.Style._w_dimmedFontColor
|
||||
Layout.fillWidth: true
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 15
|
||||
horizontalAlignment: TextInput.AlignLeft
|
||||
wrapMode: Text.WordWrap;
|
||||
leftPadding: 0
|
||||
topPadding: 0
|
||||
width: parent.width - (icon.width + icon.anchors.leftMargin + anchors.leftMargin)
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: button
|
||||
small: true
|
||||
Rectangle {
|
||||
id: bottomDivider
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 1
|
||||
color: MoneroComponents.Style.dividerColor
|
||||
opacity: MoneroComponents.Style.dividerOpacity
|
||||
visible: settingsListItem.isLast
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: root.color = MoneroComponents.Style.titleBarButtonHoverColor
|
||||
onExited: root.color = "transparent"
|
||||
onClicked: {
|
||||
settingsListItem.clicked()
|
||||
}
|
||||
width: 135
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
components/Slider.qml
Normal file
@@ -0,0 +1,68 @@
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.0 as QtQuickControls
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
ColumnLayout {
|
||||
property alias from: slider.from
|
||||
property alias stepSize: slider.stepSize
|
||||
property alias to: slider.to
|
||||
property alias value: slider.value
|
||||
|
||||
property alias text: label.text
|
||||
|
||||
signal moved()
|
||||
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
id: label
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.pixelSize: 14
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QtQuickControls.Slider {
|
||||
id: slider
|
||||
leftPadding: 0
|
||||
snapMode: QtQuickControls.Slider.SnapAlways
|
||||
|
||||
background: Rectangle {
|
||||
x: parent.leftPadding
|
||||
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||
implicitWidth: 200
|
||||
implicitHeight: 4
|
||||
width: parent.availableWidth
|
||||
height: implicitHeight
|
||||
radius: 2
|
||||
color: MoneroComponents.Style.progressBarBackgroundColor
|
||||
|
||||
Rectangle {
|
||||
width: parent.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: MoneroComponents.Style.green
|
||||
radius: 2
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
|
||||
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||
implicitWidth: 18
|
||||
implicitHeight: 18
|
||||
radius: 8
|
||||
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
|
||||
border.color: MoneroComponents.Style.grey
|
||||
}
|
||||
|
||||
onMoved: parent.moved()
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,17 @@ import "../components" as MoneroComponents
|
||||
|
||||
Item {
|
||||
id: button
|
||||
property bool primary: true
|
||||
property string rightIcon: ""
|
||||
property string rightIconInactive: ""
|
||||
property string textColor: button.enabled? MoneroComponents.Style.buttonTextColor: MoneroComponents.Style.buttonTextColorDisabled
|
||||
property color textColor: !button.enabled
|
||||
? MoneroComponents.Style.buttonTextColorDisabled
|
||||
: primary
|
||||
? MoneroComponents.Style.buttonTextColor
|
||||
: MoneroComponents.Style.buttonSecondaryTextColor;
|
||||
property bool small: false
|
||||
property alias text: label.text
|
||||
property alias fontBold: label.font.bold
|
||||
property int fontSize: {
|
||||
if(small) return 14;
|
||||
else return 16;
|
||||
@@ -70,7 +76,9 @@ Item {
|
||||
when: buttonArea.containsMouse || button.focus
|
||||
PropertyChanges {
|
||||
target: buttonRect
|
||||
color: MoneroComponents.Style.buttonBackgroundColorHover
|
||||
color: primary
|
||||
? MoneroComponents.Style.buttonBackgroundColorHover
|
||||
: MoneroComponents.Style.buttonSecondaryBackgroundColorHover
|
||||
}
|
||||
},
|
||||
State {
|
||||
@@ -78,7 +86,9 @@ Item {
|
||||
when: button.enabled
|
||||
PropertyChanges {
|
||||
target: buttonRect
|
||||
color: MoneroComponents.Style.buttonBackgroundColor
|
||||
color: primary
|
||||
? MoneroComponents.Style.buttonBackgroundColor
|
||||
: MoneroComponents.Style.buttonSecondaryBackgroundColor
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
||||
@@ -90,6 +90,10 @@ Rectangle {
|
||||
|
||||
function close() {
|
||||
root.visible = false;
|
||||
// reset button text
|
||||
okButton.text = qsTr("OK")
|
||||
cancelButton.text = qsTr("Cancel")
|
||||
|
||||
closeCallback();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,11 +58,6 @@ Item {
|
||||
|
||||
onExpandedChanged: if(expanded) appWindow.currentItem = dropdown
|
||||
|
||||
// Workaroud for suspected memory leak in 5.8 causing malloc crash on app exit
|
||||
function update() {
|
||||
firstColText.text = columnid.currentIndex < repeater.model.rowCount() ? qsTr(repeater.model.get(columnid.currentIndex).column1) + translationManager.emptyString : ""
|
||||
}
|
||||
|
||||
Item {
|
||||
id: head
|
||||
anchors.left: parent.left
|
||||
@@ -80,15 +75,17 @@ Item {
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: firstColText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.right: dropIndicator.left
|
||||
anchors.rightMargin: 12
|
||||
elide: Text.ElideRight
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.bold: dropdown.headerFontBold
|
||||
font.pixelSize: dropdown.fontHeaderSize
|
||||
color: dropdown.textColor
|
||||
text: columnid.currentIndex < repeater.model.count ? qsTr(repeater.model.get(columnid.currentIndex).column1) + translationManager.emptyString : ""
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -96,7 +93,8 @@ Item {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: 32
|
||||
anchors.rightMargin: 12
|
||||
width: dropdownIcon.width
|
||||
|
||||
Image {
|
||||
id: dropdownIcon
|
||||
@@ -214,7 +212,6 @@ Item {
|
||||
popup.close()
|
||||
columnid.currentIndex = index
|
||||
changed();
|
||||
dropdown.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ QtObject {
|
||||
property string textSelectedColor: blackTheme ? _b_textSelectedColor : _w_textSelectedColor
|
||||
|
||||
property string inputBoxBackground: blackTheme ? _b_inputBoxBackground : _w_inputBoxBackground
|
||||
property string inputBoxBackgroundDisabled: blackTheme ? _b_inputBoxBackgroundDisabled : _w_inputBoxBackgroundDisabled
|
||||
property string inputBoxBackgroundError: blackTheme ? _b_inputBoxBackgroundError : _w_inputBoxBackgroundError
|
||||
property string inputBoxColor: blackTheme ? _b_inputBoxColor : _w_inputBoxColor
|
||||
property string legacy_placeholderFontColor: blackTheme ? _b_legacy_placeholderFontColor : _w_legacy_placeholderFontColor
|
||||
@@ -43,6 +44,9 @@ QtObject {
|
||||
property string buttonInlineBackgroundColor: blackTheme ? _b_buttonInlineBackgroundColor : _w_buttonInlineBackgroundColor
|
||||
property string buttonTextColor: blackTheme ? _b_buttonTextColor : _w_buttonTextColor
|
||||
property string buttonTextColorDisabled: blackTheme ? _b_buttonTextColorDisabled : _w_buttonTextColorDisabled
|
||||
property string buttonSecondaryBackgroundColor: "#d9d9d9"
|
||||
property string buttonSecondaryBackgroundColorHover: "#a6a6a6"
|
||||
property string buttonSecondaryTextColor: "#4d4d4d"
|
||||
property string dividerColor: blackTheme ? _b_dividerColor : _w_dividerColor
|
||||
property real dividerOpacity: blackTheme ? _b_dividerOpacity : _w_dividerOpacity
|
||||
|
||||
@@ -85,6 +89,7 @@ QtObject {
|
||||
property string _b_textSelectedColor: "white"
|
||||
|
||||
property string _b_inputBoxBackground: "black"
|
||||
property string _b_inputBoxBackgroundDisabled: Qt.rgba(255, 255, 255, 0.10)
|
||||
property string _b_inputBoxBackgroundError: "#FFDDDD"
|
||||
property string _b_inputBoxColor: "white"
|
||||
property string _b_legacy_placeholderFontColor: "#BABABA"
|
||||
@@ -141,6 +146,7 @@ QtObject {
|
||||
property string _w_textSelectedColor: "black"
|
||||
|
||||
property string _w_inputBoxBackground: "white"
|
||||
property string _w_inputBoxBackgroundDisabled: Qt.rgba(0, 0, 0, 0.20)
|
||||
property string _w_inputBoxBackgroundError: "#FFDDDD"
|
||||
property string _w_inputBoxColor: "black"
|
||||
property string _w_legacy_placeholderFontColor: "#BABABA"
|
||||
|
||||
@@ -309,8 +309,8 @@ Rectangle {
|
||||
width: 16
|
||||
image: MoneroComponents.Style.titleBarCloseSource
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
fontAwesomeFallbackIcon: FontAwesome.timesRectangle
|
||||
fontAwesomeFallbackSize: 18
|
||||
fontAwesomeFallbackIcon: FontAwesome.times
|
||||
fontAwesomeFallbackSize: 21
|
||||
fontAwesomeFallbackOpacity: MoneroComponents.Style.blackTheme ? 0.8 : 0.6
|
||||
opacity: 0.75
|
||||
}
|
||||
|
||||
203
components/UpdateDialog.qml
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import moneroComponents.Downloader 1.0
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
Popup {
|
||||
id: updateDialog
|
||||
|
||||
property bool active: false
|
||||
property bool allowed: true
|
||||
property string error: ""
|
||||
property string filename: ""
|
||||
property string hash: ""
|
||||
property double progress: url && downloader.total > 0 ? downloader.loaded * 100 / downloader.total : 0
|
||||
property string url: ""
|
||||
property bool valid: false
|
||||
property string version: ""
|
||||
|
||||
background: Rectangle {
|
||||
border.color: MoneroComponents.Style.appWindowBorderColor
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.middlePanelBackgroundColor
|
||||
}
|
||||
closePolicy: Popup.NoAutoClose
|
||||
padding: 20
|
||||
visible: active && allowed
|
||||
|
||||
function show(version, url, hash) {
|
||||
updateDialog.error = "";
|
||||
updateDialog.hash = hash;
|
||||
updateDialog.url = url;
|
||||
updateDialog.valid = false;
|
||||
updateDialog.version = version;
|
||||
updateDialog.active = true;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
spacing: updateDialog.padding
|
||||
|
||||
Text {
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.bold: true
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 18
|
||||
text: qsTr("New Monero version v%1 is available.").arg(updateDialog.version)
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
color: "red"
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 18
|
||||
text: updateDialog.error
|
||||
visible: text
|
||||
}
|
||||
|
||||
Text {
|
||||
id: statusText
|
||||
color: updateDialog.valid ? MoneroComponents.Style.green : MoneroComponents.Style.defaultFontColor
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 18
|
||||
visible: !errorText.visible
|
||||
|
||||
text: {
|
||||
if (!updateDialog.url) {
|
||||
return qsTr("Please visit getmonero.org for details") + translationManager.emptyString;
|
||||
}
|
||||
if (downloader.active) {
|
||||
return "%1 (%2%)"
|
||||
.arg(qsTr("Downloading"))
|
||||
.arg(updateDialog.progress.toFixed(1))
|
||||
+ translationManager.emptyString;
|
||||
}
|
||||
if (updateDialog.valid) {
|
||||
return qsTr("Update downloaded, signature verified") + translationManager.emptyString;
|
||||
}
|
||||
return qsTr("Do you want to download and verify new version?") + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: progressBar
|
||||
color: MoneroComponents.Style.lightGreyFontColor
|
||||
height: 3
|
||||
Layout.fillWidth: true
|
||||
visible: updateDialog.valid || downloader.active
|
||||
|
||||
Rectangle {
|
||||
color: MoneroComponents.Style.buttonBackgroundColor
|
||||
height: parent.height
|
||||
width: parent.width * updateDialog.progress / 100
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: parent.spacing
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: cancelButton
|
||||
fontBold: false
|
||||
primary: !updateDialog.url
|
||||
text: {
|
||||
if (!updateDialog.url) {
|
||||
return qsTr("Ok") + translationManager.emptyString;
|
||||
}
|
||||
if (updateDialog.valid || downloader.active || errorText.visible) {
|
||||
return qsTr("Cancel") + translationManager.emptyString;
|
||||
}
|
||||
return qsTr("Download later") + translationManager.emptyString;
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
downloader.cancel();
|
||||
updateDialog.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: downloadButton
|
||||
KeyNavigation.tab: cancelButton
|
||||
fontBold: false
|
||||
text: (updateDialog.error ? qsTr("Retry") : qsTr("Download")) + translationManager.emptyString
|
||||
visible: updateDialog.url && !updateDialog.valid && !downloader.active
|
||||
|
||||
onClicked: {
|
||||
updateDialog.error = "";
|
||||
updateDialog.filename = updateDialog.url.replace(/^.*\//, '');
|
||||
const downloadingStarted = downloader.get(updateDialog.url, updateDialog.hash, function(error) {
|
||||
if (error) {
|
||||
console.error("Download failed", error);
|
||||
updateDialog.error = qsTr("Download failed") + translationManager.emptyString;
|
||||
} else {
|
||||
updateDialog.valid = true;
|
||||
}
|
||||
});
|
||||
if (!downloadingStarted) {
|
||||
updateDialog.error = qsTr("Failed to start download") + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: saveButton
|
||||
KeyNavigation.tab: cancelButton
|
||||
fontBold: false
|
||||
onClicked: {
|
||||
const fullPath = oshelper.openSaveFileDialog(
|
||||
qsTr("Save as") + translationManager.emptyString,
|
||||
oshelper.downloadLocation(),
|
||||
updateDialog.filename);
|
||||
if (!fullPath) {
|
||||
return;
|
||||
}
|
||||
if (downloader.saveToFile(fullPath)) {
|
||||
cancelButton.clicked();
|
||||
oshelper.openContainingFolder(fullPath);
|
||||
} else {
|
||||
updateDialog.error = qsTr("Save operation failed") + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
text: qsTr("Save to file") + translationManager.emptyString
|
||||
visible: updateDialog.valid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Downloader {
|
||||
id: downloader
|
||||
}
|
||||
}
|
||||
@@ -149,6 +149,7 @@ Object {
|
||||
property string caretUp : "\uf0d8"
|
||||
property string cartArrowDown : "\uf218"
|
||||
property string cartPlus : "\uf217"
|
||||
property string cashRegister: "\uf788"
|
||||
property string cc : "\uf20a"
|
||||
property string ccAmex : "\uf1f3"
|
||||
property string ccDinersClub : "\uf24c"
|
||||
|
||||
@@ -17,7 +17,7 @@ if [ ! -d $MONERO_DIR/src ]; then
|
||||
fi
|
||||
git submodule update --remote
|
||||
git -C $MONERO_DIR fetch
|
||||
git -C $MONERO_DIR checkout v0.15.0.5
|
||||
git -C $MONERO_DIR checkout v0.16.0.3
|
||||
|
||||
# get monero core tag
|
||||
pushd $MONERO_DIR
|
||||
|
||||
BIN
images/busy-indicator.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
images/busy-indicator@2x.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 493 B |
2
images/monero-vector.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" viewBox="0 0 6000 6000" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs><clipPath id="a"><path d="m0 4500h4500v-4500h-4500z"/></clipPath></defs><g transform="matrix(1.3333 0 0 -1.3333 0 6e3)"><g clip-path="url(#a)"><g transform="translate(4128 2250.2)"><path d="m0 0c0-1037.2-840.79-1878.1-1878.1-1878.1-1037.2 0-1878 840.88-1878 1878.1 0 1037.3 840.8 1878.1 1878 1878.1 1037.3 0 1878.1-840.79 1878.1-1878.1" fill="#fff"/></g><g transform="translate(2250 4128.2)"><path d="m0 0c-1036.9 0-1879.1-842.06-1877.8-1878 0.262-207.26 33.308-406.63 95.342-593.12h561.88v1579.9l1220.6-1220.6 1220.6 1220.6v-1579.9h561.96c62.117 186.48 95.008 385.85 95.369 593.12 1.809 1037-840.89 1877.8-1877.9 1877.8z" fill="#f36e36"/></g><g transform="translate(1969.3 1735.8)"><path d="m0 0-532.67 532.7v-994.14h-407.26l-384.29-0.07c329.63-540.8 925.35-902.56 1604.9-902.56 679.54 0 1275.3 361.85 1605 902.65l-384.44-0.013h-407.27v994.14l-813.3-813.31-280.62 280.61z" fill="#575757"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 596 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 440 B |
|
Before Width: | Height: | Size: 575 B |
@@ -1,8 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="18" viewBox="0 0 10 18">
|
||||
<g fill="none" fill-rule="evenodd" opacity="1">
|
||||
<path fill="none" d="M-13-9h36v36h-36z" opacity="1"/>
|
||||
<g fill="#000" fill-rule="nonzero">
|
||||
<path d="M5 0C3.75 0 2.571.468 1.643 1.296A5.057 5.057 0 0 0 0 5.04c0 .396.321.72.714.72h.715a.72.72 0 0 0 .714-.72c0-.828.357-1.584.964-2.16A2.823 2.823 0 0 1 5 2.16c.107 0 .214 0 .321.036 1.322.144 2.358 1.224 2.5 2.52.143 1.188-.464 2.304-1.5 2.88-1.5.792-2.428 2.304-2.428 3.96v2.124c0 .396.321.72.714.72h.714a.72.72 0 0 0 .715-.72v-2.124c0-.828.5-1.62 1.285-2.052A4.98 4.98 0 0 0 9.93 4.5C9.714 2.16 7.857.288 5.57.036 5.393 0 5.18 0 5 0zM5.714 18H4.286a.358.358 0 0 1-.357-.36V16.2c0-.2.16-.36.357-.36h1.428c.198 0 .357.16.357.36v1.44c0 .2-.16.36-.357.36z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 848 B |
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014-2019, The Monero Project
|
||||
Copyright (c) 2014-2020, The Monero Project
|
||||
|
||||
All rights reserved.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
; Monero Carbon Chamaeleon GUI Wallet Installer for Windows
|
||||
; Copyright (c) 2017-2019, The Monero Project
|
||||
; Monero Nitrogen Nebula GUI Wallet Installer for Windows
|
||||
; Copyright (c) 2017-2020, The Monero Project
|
||||
; See LICENSE
|
||||
#define GuiVersion GetFileVersion("bin\monero-wallet-gui.exe")
|
||||
|
||||
@@ -62,7 +62,6 @@ Name: "en"; MessagesFile: "compiler:Default.isl"
|
||||
; .exe/.dll file possibly with version info).
|
||||
;
|
||||
; This is far more robust than relying on version info or on file dates (flag "comparetimestamp").
|
||||
; As of version 0.15.0.0, the Monero .exe files do not carry version info anyway in their .exe headers.
|
||||
; The only small drawback seems to be somewhat longer update times because each and every file is
|
||||
; copied again, even if already present with correct file date and identical content.
|
||||
;
|
||||
@@ -71,17 +70,18 @@ Name: "en"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
Source: {#file AddBackslash(SourcePath) + "ReadMe.htm"}; DestDir: "{app}"; DestName: "ReadMe.htm"; Flags: ignoreversion
|
||||
Source: "FinishImage.bmp"; Flags: dontcopy
|
||||
Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Monero GUI wallet exe and guide
|
||||
Source: "bin\monero-wallet-gui.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-gui-wallet-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Monero CLI wallet
|
||||
Source: "bin\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-gen-trusted-multisig.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-gen-trusted-multisig.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Monero wallet RPC interface implementation
|
||||
Source: "bin\monero-wallet-rpc.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-wallet-rpc.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Monero daemon
|
||||
Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
@@ -90,16 +90,17 @@ Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "monero-daemon.bat"; DestDir: "{app}"; Flags: ignoreversion;
|
||||
|
||||
; Monero blockchain utilities
|
||||
Source: "bin\monero-blockchain-export.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-mark-spent-outputs.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-usage.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-ancestry.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-depth.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-prune-known-spent-data.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-prune.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\monero-blockchain-stats.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-export.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-mark-spent-outputs.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-usage.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-ancestry.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-depth.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-prune-known-spent-data.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-prune.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-blockchain-stats.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "bin\extras\monero-gen-ssl-cert.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Qt Quick 2D Renderer fallback for systems / environments with "low-level graphics" i.e. without 3D support
|
||||
Source: "bin\start-low-graphics-mode.bat"; DestDir: "{app}"; Flags: ignoreversion
|
||||
@@ -202,7 +203,7 @@ begin
|
||||
// Additional wizard page for entering a special blockchain location
|
||||
blockChainDefaultDir := ExpandConstant('{commonappdata}\bitmonero');
|
||||
s := 'The default folder to store the Monero blockchain is ' + blockChainDefaultDir;
|
||||
s := s + '. As this will need more than 74 GB of free space, you may want to use a folder on a different drive.';
|
||||
s := s + '. As this will need more than 90 GB of free space, you may want to use a folder on a different drive.';
|
||||
s := s + ' If yes, specify that folder here.';
|
||||
|
||||
BlockChainDirPage := CreateInputDirPage(wpSelectDir,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Monero GUI Wallet Windows Installer #
|
||||
|
||||
Copyright (c) 2017-2019, The Monero Project
|
||||
Copyright (c) 2017-2020, The Monero Project
|
||||
|
||||
## Introduction ##
|
||||
|
||||
This is a *Inno Setup* script `Monero.iss` plus some related files
|
||||
that allows you to build a standalone Windows installer (.exe) for
|
||||
the GUI wallet that comes with the Carbon Chamaeleon release of Monero.
|
||||
the GUI wallet that comes with the Nitrogen Nebula release of Monero.
|
||||
|
||||
This turns the GUI wallet into a more or less standard Windows program,
|
||||
by default installed into a subdirectory of `C:\Program Files`, a
|
||||
@@ -18,7 +18,7 @@ Monero.
|
||||
As the setup script in file [Monero.iss](Monero.iss) has to list many
|
||||
files and directories of the GUI wallet package to install by name,
|
||||
this version of the script only works with exactly the GUI wallet
|
||||
for Monero release *Carbon Chamaeleon* that you find on
|
||||
for Monero release *Nitrogen Nebula* that you find on
|
||||
[the official download page](https://getmonero.org/downloads/).
|
||||
|
||||
It should however be easy to modify the script for future
|
||||
@@ -32,15 +32,15 @@ See [LICENSE](LICENSE).
|
||||
|
||||
You can only build on Windows, and the result is always a
|
||||
Windows .exe file that can act as a standalone installer for the
|
||||
Carbon Chamaeleon GUI wallet.
|
||||
Nitrogen Nebula GUI wallet.
|
||||
|
||||
Note that the installer build process is now reproducible / deterministic. For details check the file [Deterministic.md](Deterministic.md).
|
||||
|
||||
The build steps in detail:
|
||||
|
||||
1. Install *Inno Setup*. You can get it from [here](http://www.jrsoftware.org/isdl.php)
|
||||
2. Get the Inno Setup script plus related files by cloning the whole [monero-gui GitHub repository](https://github.com/monero-project/monero-gui); you will only need the files in the installer directory `installers\windows` however. Depending on development state, additionally instead of simply using `master` you may have to checkout a specific branch, like `release-v0.15`.
|
||||
3. The setup script is written to take the GUI wallet files from a subdirectory named `bin`; so create `installers\windows\bin`, get the zip file of the GUI wallet from [here](https://getmonero.org/downloads/), unpack it somewhere, and copy all the files and subdirectories in the single subdirectory there (currently named `monero-gui-0.15.0.0`) to this `bin` subdirectory
|
||||
2. Get the Inno Setup script plus related files by cloning the whole [monero-gui GitHub repository](https://github.com/monero-project/monero-gui); you will only need the files in the installer directory `installers\windows` however. Depending on development state, additionally instead of simply using `master` you may have to checkout a specific branch, like `release-v0.16`.
|
||||
3. The setup script is written to take the GUI wallet files from a subdirectory named `bin`; so create `installers\windows\bin`, get the zip file of the GUI wallet from [here](https://getmonero.org/downloads/), unpack it somewhere, and copy all the files and subdirectories in the single subdirectory there (currently named `monero-gui-0.16.0.0`) to this `bin` subdirectory
|
||||
4. Start Inno Setup, load `Monero.iss` and compile it
|
||||
5. The result i.e. the finished installer will be the file `mysetup.exe` in the `installers\windows\Output` subdirectory
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Monero Carbon Chamaeleon GUI Wallet</title>
|
||||
<title>Monero Nitrogen Nebula GUI Wallet</title>
|
||||
</head>
|
||||
|
||||
<body style="font-family: Arial, Helvetica, sans-serif">
|
||||
<h1>Monero Carbon Chamaeleon GUI Wallet</h1>
|
||||
<h1>Monero Nitrogen Nebula GUI Wallet</h1>
|
||||
|
||||
<p>Copyright (c) 2014-2019, The Monero Project</p>
|
||||
<p>Copyright (c) 2014-2020, The Monero Project</p>
|
||||
|
||||
<h2>Preface</h2>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<h2>Content of the Package</h2>
|
||||
|
||||
<p>You just installed the <i>Monero GUI wallet</i> for Windows, release Carbon Chamaeleon, version {#GuiVersion}.
|
||||
<p>You just installed the <i>Monero GUI wallet</i> for Windows, release Nitrogen Nebula, version {#GuiVersion}.
|
||||
The wallet enables you to send and receive Moneroj in a secure and very private way.
|
||||
</p>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
provides the most security and privacy possible for you.</p>
|
||||
|
||||
<p>However if your Internet access makes it difficult to run a full node, or if you have simply no room to store
|
||||
the blockchain locally (somewhat over 74 GB in November 2019, and of course growing), you can compromise and try to connect
|
||||
the blockchain locally (about 90 GB in May 2020, and of course growing), you can compromise and try to connect
|
||||
to a remote node. One way of finding such a node is checking
|
||||
<a href="https://moneroworld.com/#nodes">this page</a>.
|
||||
</p>
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
<p>The Monero software and especially the GUI wallet are "work in progress", and sometimes things go wrong.</p>
|
||||
|
||||
<p>Please note that despite any technical problems that you may encounter your moneroj are almost always safe: You may
|
||||
<p>Please note that despite any technical problems that you may encounter your Moneroj are almost always safe: You may
|
||||
not be able to move them or you even may not see how many you currently have, but you most probably won't loose any.
|
||||
But do remember that the seed needed to re-create the wallet <b>is</b> critical, however: <b>Never loose your
|
||||
seed!</b></p>
|
||||
|
||||
|
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 440 KiB |
42
js/Utils.js
@@ -55,36 +55,14 @@ function ago(epoch) {
|
||||
var now = new Date().getTime() / 1000;
|
||||
var delta = now - epoch;
|
||||
|
||||
if(delta < 60) {
|
||||
if (delta <= 1) {
|
||||
return 1 + " " + qsTr("second ago")
|
||||
} else {
|
||||
return Math.floor(delta) + " " + qsTr("seconds ago")
|
||||
}
|
||||
} else if (delta >= 60 && delta <= 3600) {
|
||||
if(delta >= 60 && delta < 120){
|
||||
return 1 + " " + qsTr("minute ago")
|
||||
} else {
|
||||
return parseInt(Math.floor(delta / 60)) + " " + qsTr("minutes ago")
|
||||
}
|
||||
} else if (delta >= 3600 && delta <= 86400) {
|
||||
if(delta >= 3600 && delta < 7200) {
|
||||
return 1 + " " + qsTr("hour ago")
|
||||
} else {
|
||||
return parseInt(Math.floor(delta / 60 / 60)) + " " + qsTr("hours ago")
|
||||
}
|
||||
} else if (delta >= 86400){
|
||||
if(delta >= 86400 && delta < 172800) {
|
||||
return 1 + " " + qsTr("day ago")
|
||||
} else {
|
||||
var _delta = parseInt(Math.floor(delta / 24 / 60 / 60));
|
||||
if(_delta === 1) {
|
||||
return 1 + " " + qsTr("day ago")
|
||||
} else {
|
||||
return _delta + " " + qsTr("days ago")
|
||||
}
|
||||
}
|
||||
}
|
||||
if(delta < 60)
|
||||
return qsTr("%n second(s) ago", "0", Math.floor(delta))
|
||||
else if (delta >= 60 && delta <= 3600)
|
||||
return qsTr("%n minute(s) ago", "0", Math.floor(delta / 60))
|
||||
else if (delta >= 3600 && delta <= 86400)
|
||||
return qsTr("%n hour(s) ago", "0", Math.floor(delta / 60 / 60))
|
||||
else if (delta >= 86400)
|
||||
return qsTr("%n day(s) ago", "0", Math.floor(delta / 24 / 60 / 60))
|
||||
}
|
||||
|
||||
function netTypeToString(){
|
||||
@@ -133,3 +111,7 @@ function capitalize(s){
|
||||
if (typeof s !== 'string') return ''
|
||||
return s.charAt(0).toUpperCase() + s.slice(1)
|
||||
}
|
||||
|
||||
function removeTrailingZeros(value) {
|
||||
return (value + '').replace(/(\.\d*[1-9])0+$/, '$1');
|
||||
}
|
||||
|
||||
11
js/Wizard.js
@@ -58,11 +58,6 @@ function switchPage(next) {
|
||||
}
|
||||
|
||||
function createWalletPath(isIOS, folder_path,account_name){
|
||||
// Remove trailing slash - (default on windows and mac)
|
||||
if (folder_path.substring(folder_path.length -1) === "/"){
|
||||
folder_path = folder_path.substring(0,folder_path.length -1)
|
||||
}
|
||||
|
||||
// Store releative path on ios.
|
||||
if(isIOS)
|
||||
folder_path = "";
|
||||
@@ -102,10 +97,6 @@ function tr(text) {
|
||||
return qsTr(text) + translationManager.emptyString
|
||||
}
|
||||
|
||||
function lineBreaksToSpaces(text) {
|
||||
return text.trim().replace(/(\r\n|\n|\r)/gm, " ");
|
||||
}
|
||||
|
||||
function usefulName(path) {
|
||||
// arbitrary "short enough" limit
|
||||
if (path.length < 32)
|
||||
@@ -115,7 +106,7 @@ function usefulName(path) {
|
||||
|
||||
function checkSeed(seed) {
|
||||
console.log("Checking seed")
|
||||
var wordsArray = lineBreaksToSpaces(seed).split(" ");
|
||||
var wordsArray = seed.split(/\s+/);
|
||||
return wordsArray.length === 25 || wordsArray.length === 24
|
||||
}
|
||||
|
||||
|
||||
323
main.qml
@@ -34,6 +34,7 @@ import QtQuick.Dialogs 1.2
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import moneroComponents.Wallet 1.0
|
||||
import moneroComponents.WalletManager 1.0
|
||||
import moneroComponents.PendingTransaction 1.0
|
||||
import moneroComponents.NetworkType 1.0
|
||||
import moneroComponents.Settings 1.0
|
||||
@@ -45,6 +46,7 @@ import "pages/merchant" as MoneroMerchant
|
||||
import "wizard"
|
||||
import "js/Utils.js" as Utils
|
||||
import "js/Windows.js" as Windows
|
||||
import "version.js" as Version
|
||||
|
||||
ApplicationWindow {
|
||||
id: appWindow
|
||||
@@ -66,12 +68,14 @@ ApplicationWindow {
|
||||
property bool walletSynced: false
|
||||
property int maxWindowHeight: (isAndroid || isIOS)? screenHeight : (screenHeight < 900)? 720 : 800;
|
||||
property bool daemonRunning: !persistentSettings.useRemoteNode && !disconnected
|
||||
property bool daemonStartStopInProgress: false
|
||||
property alias toolTip: toolTip
|
||||
property string walletName
|
||||
property bool viewOnly: false
|
||||
property bool foundNewBlock: false
|
||||
property bool qrScannerEnabled: (typeof builtWithScanner != "undefined") && builtWithScanner
|
||||
property int blocksToSync: 1
|
||||
property int firstBlockSeen
|
||||
property bool isMining: false
|
||||
property int walletMode: persistentSettings.walletMode
|
||||
property var cameraUi
|
||||
@@ -214,7 +218,7 @@ ApplicationWindow {
|
||||
appWindow.viewState = prevState;
|
||||
}
|
||||
};
|
||||
passwordDialog.open(usefulName(walletPath()));
|
||||
passwordDialog.open(usefulName(persistentSettings.wallet_path));
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
@@ -250,10 +254,9 @@ ApplicationWindow {
|
||||
|
||||
// enable timers
|
||||
userInActivityTimer.running = true;
|
||||
simpleModeConnectionTimer.running = true;
|
||||
|
||||
// wallet already opened with wizard, we just need to initialize it
|
||||
var wallet_path = walletPath();
|
||||
var wallet_path = persistentSettings.wallet_path;
|
||||
if(isIOS)
|
||||
wallet_path = moneroAccountsDir + wallet_path;
|
||||
// console.log("opening wallet at: ", wallet_path, "with password: ", appWindow.walletPassword);
|
||||
@@ -291,6 +294,7 @@ ApplicationWindow {
|
||||
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
|
||||
currentWallet.deviceButtonRequest.disconnect(onDeviceButtonRequest);
|
||||
currentWallet.deviceButtonPressed.disconnect(onDeviceButtonPressed);
|
||||
currentWallet.walletPassphraseNeeded.disconnect(onWalletPassphraseNeededWallet);
|
||||
currentWallet.transactionCommitted.disconnect(onTransactionCommitted);
|
||||
middlePanel.paymentClicked.disconnect(handlePayment);
|
||||
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
|
||||
@@ -358,6 +362,7 @@ ApplicationWindow {
|
||||
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
|
||||
currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
|
||||
currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
|
||||
currentWallet.walletPassphraseNeeded.connect(onWalletPassphraseNeededWallet);
|
||||
currentWallet.transactionCommitted.connect(onTransactionCommitted);
|
||||
middlePanel.paymentClicked.connect(handlePayment);
|
||||
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
|
||||
@@ -392,11 +397,6 @@ ApplicationWindow {
|
||||
return !persistentSettings.useRemoteNode || persistentSettings.is_trusted_daemon;
|
||||
}
|
||||
|
||||
function walletPath() {
|
||||
var wallet_path = persistentSettings.wallet_path
|
||||
return wallet_path;
|
||||
}
|
||||
|
||||
function usefulName(path) {
|
||||
// arbitrary "short enough" limit
|
||||
if (path.length < 32)
|
||||
@@ -474,9 +474,9 @@ ApplicationWindow {
|
||||
console.log("Wallet connection status changed " + status)
|
||||
middlePanel.updateStatus();
|
||||
leftPanel.networkStatus.connected = status
|
||||
|
||||
// Update fee multiplier dropdown on transfer page
|
||||
middlePanel.transferView.updatePriorityDropdown();
|
||||
if (status == Wallet.ConnectionStatus_Disconnected) {
|
||||
firstBlockSeen = 0;
|
||||
}
|
||||
|
||||
// If wallet isnt connected, advanced wallet mode and no daemon is running - Ask
|
||||
if (appWindow.walletMode >= 2 && !persistentSettings.useRemoteNode && !walletInitialized && disconnected) {
|
||||
@@ -558,19 +558,32 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function onWalletPassphraseNeeded(){
|
||||
function onWalletPassphraseNeededManager(on_device){
|
||||
onWalletPassphraseNeeded(walletManager, on_device)
|
||||
}
|
||||
|
||||
function onWalletPassphraseNeededWallet(on_device){
|
||||
onWalletPassphraseNeeded(currentWallet, on_device)
|
||||
}
|
||||
|
||||
function onWalletPassphraseNeeded(handler, on_device){
|
||||
hideProcessingSplash();
|
||||
|
||||
console.log(">>> wallet passphrase needed: ")
|
||||
passwordDialog.onAcceptedPassphraseCallback = function() {
|
||||
walletManager.onPassphraseEntered(passwordDialog.password);
|
||||
devicePassphraseDialog.onAcceptedCallback = function(passphrase) {
|
||||
handler.onPassphraseEntered(passphrase, false, false);
|
||||
appWindow.onWalletOpening();
|
||||
}
|
||||
passwordDialog.onRejectedPassphraseCallback = function() {
|
||||
walletManager.onPassphraseEntered("", true);
|
||||
devicePassphraseDialog.onWalletEntryCallback = function() {
|
||||
handler.onPassphraseEntered("", true, false);
|
||||
appWindow.onWalletOpening();
|
||||
}
|
||||
passwordDialog.openPassphraseDialog()
|
||||
devicePassphraseDialog.onRejectedCallback = function() {
|
||||
handler.onPassphraseEntered("", false, true);
|
||||
appWindow.onWalletOpening();
|
||||
}
|
||||
|
||||
devicePassphraseDialog.open(on_device)
|
||||
}
|
||||
|
||||
function onWalletUpdate() {
|
||||
@@ -613,18 +626,22 @@ ApplicationWindow {
|
||||
currentDaemonAddress = localDaemonAddress
|
||||
currentWallet.initAsync(currentDaemonAddress, isTrustedDaemon());
|
||||
walletManager.setDaemonAddressAsync(currentDaemonAddress);
|
||||
firstBlockSeen = 0;
|
||||
}
|
||||
|
||||
function onHeightRefreshed(bcHeight, dCurrentBlock, dTargetBlock) {
|
||||
// Daemon fully synced
|
||||
// TODO: implement onDaemonSynced or similar in wallet API and don't start refresh thread before daemon is synced
|
||||
// targetBlock = currentBlock = 1 before network connection is established.
|
||||
if (firstBlockSeen == 0 && dTargetBlock != 1) {
|
||||
firstBlockSeen = dCurrentBlock;
|
||||
}
|
||||
daemonSynced = dCurrentBlock >= dTargetBlock && dTargetBlock != 1
|
||||
walletSynced = bcHeight >= dTargetBlock
|
||||
|
||||
// Update progress bars
|
||||
if(!daemonSynced) {
|
||||
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, dTargetBlock-dCurrentBlock);
|
||||
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, dTargetBlock-firstBlockSeen);
|
||||
leftPanel.progressBar.updateProgress(0,dTargetBlock, dTargetBlock, qsTr("Waiting for daemon to sync"));
|
||||
} else {
|
||||
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, 0, qsTr("Daemon is synchronized (%1)").arg(dCurrentBlock.toFixed(0)));
|
||||
@@ -665,12 +682,11 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
function startDaemon(flags){
|
||||
daemonStartStopInProgress = true;
|
||||
|
||||
// Pause refresh while starting daemon
|
||||
currentWallet.pauseRefresh();
|
||||
|
||||
// Pause simplemode connection timer
|
||||
simpleModeConnectionTimer.stop();
|
||||
|
||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to start..."))
|
||||
const noSync = appWindow.walletMode === 0;
|
||||
const bootstrapNodeAddress = persistentSettings.walletMode < 2 ? "auto" : persistentSettings.bootstrapNodeAddress
|
||||
@@ -678,8 +694,10 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
function stopDaemon(callback){
|
||||
daemonStartStopInProgress = true;
|
||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."))
|
||||
daemonManager.stopAsync(persistentSettings.nettype, function(result) {
|
||||
daemonStartStopInProgress = false;
|
||||
hideProcessingSplash();
|
||||
callback(result);
|
||||
});
|
||||
@@ -687,13 +705,13 @@ ApplicationWindow {
|
||||
|
||||
function onDaemonStarted(){
|
||||
console.log("daemon started");
|
||||
daemonStartStopInProgress = false;
|
||||
hideProcessingSplash();
|
||||
currentWallet.connected(true);
|
||||
// resume refresh
|
||||
currentWallet.startRefresh();
|
||||
// resume simplemode connection timer
|
||||
appWindow.disconnectedEpoch = Utils.epoch();
|
||||
simpleModeConnectionTimer.start();
|
||||
}
|
||||
function onDaemonStopped(){
|
||||
currentWallet.connected(true);
|
||||
@@ -701,6 +719,7 @@ ApplicationWindow {
|
||||
|
||||
function onDaemonStartFailure(error) {
|
||||
console.log("daemon start failed");
|
||||
daemonStartStopInProgress = false;
|
||||
hideProcessingSplash();
|
||||
// resume refresh
|
||||
currentWallet.startRefresh();
|
||||
@@ -976,7 +995,11 @@ ApplicationWindow {
|
||||
informationPopup.open()
|
||||
currentWallet.refresh()
|
||||
currentWallet.disposeTransaction(transaction)
|
||||
currentWallet.store();
|
||||
currentWallet.storeAsync(function(success) {
|
||||
if (!success) {
|
||||
appWindow.showStatusMessage(qsTr("Failed to store the wallet"), 3);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// called on "getProof"
|
||||
@@ -1076,7 +1099,6 @@ ApplicationWindow {
|
||||
console.log("Displaying processing splash")
|
||||
if (typeof message != 'undefined') {
|
||||
splash.messageText = message
|
||||
splash.heightProgressText = ""
|
||||
}
|
||||
|
||||
leftPanel.enabled = false;
|
||||
@@ -1103,14 +1125,15 @@ ApplicationWindow {
|
||||
wizard.restart();
|
||||
wizard.wizardState = "wizardHome";
|
||||
rootItem.state = "wizard"
|
||||
// reset balance
|
||||
// reset balance, clear spendable funds message
|
||||
clearMoneroCardLabelText();
|
||||
leftPanel.minutesToUnlock = "";
|
||||
// reset fields
|
||||
middlePanel.addressBookView.clearFields();
|
||||
middlePanel.transferView.clearFields();
|
||||
middlePanel.receiveView.clearFields();
|
||||
// disable timers
|
||||
userInActivityTimer.running = false;
|
||||
simpleModeConnectionTimer.running = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1135,9 +1158,9 @@ ApplicationWindow {
|
||||
triggeredOnStart: false
|
||||
}
|
||||
|
||||
function fiatApiParseTicker(resp, currency){
|
||||
function fiatApiParseTicker(url, resp, currency){
|
||||
// parse & validate incoming JSON
|
||||
if(resp._url.startsWith("https://api.kraken.com/0/")){
|
||||
if(url.startsWith("https://api.kraken.com/0/")){
|
||||
if(resp.hasOwnProperty("error") && resp.error.length > 0 || !resp.hasOwnProperty("result")){
|
||||
appWindow.fiatApiError("Kraken API has error(s)");
|
||||
return;
|
||||
@@ -1146,14 +1169,14 @@ ApplicationWindow {
|
||||
var key = currency === "xmreur" ? "XXMRZEUR" : "XXMRZUSD";
|
||||
var ticker = resp.result[key]["o"];
|
||||
return ticker;
|
||||
} else if(resp._url.startsWith("https://api.coingecko.com/api/v3/")){
|
||||
} else if(url.startsWith("https://api.coingecko.com/api/v3/")){
|
||||
var key = currency === "xmreur" ? "eur" : "usd";
|
||||
if(!resp.hasOwnProperty("monero") || !resp["monero"].hasOwnProperty(key)){
|
||||
appWindow.fiatApiError("Coingecko API has error(s)");
|
||||
return;
|
||||
}
|
||||
return resp["monero"][key];
|
||||
} else if(resp._url.startsWith("https://min-api.cryptocompare.com/data/")){
|
||||
} else if(url.startsWith("https://min-api.cryptocompare.com/data/")){
|
||||
var key = currency === "xmreur" ? "EUR" : "USD";
|
||||
if(!resp.hasOwnProperty(key)){
|
||||
appWindow.fiatApiError("cryptocompare API has error(s)");
|
||||
@@ -1163,13 +1186,7 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function fiatApiGetCurrency(resp){
|
||||
// map response to `appWindow.fiatPriceAPIs` object
|
||||
if (!resp.hasOwnProperty('_url')){
|
||||
appWindow.fiatApiError("invalid JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
function fiatApiGetCurrency(url) {
|
||||
var apis = appWindow.fiatPriceAPIs;
|
||||
for (var api in apis){
|
||||
if (!apis.hasOwnProperty(api))
|
||||
@@ -1179,23 +1196,34 @@ ApplicationWindow {
|
||||
if(!apis[api].hasOwnProperty(cur))
|
||||
continue;
|
||||
|
||||
var url = apis[api][cur];
|
||||
if(url === resp._url){
|
||||
if (apis[api][cur] === url) {
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fiatApiJsonReceived(resp){
|
||||
function fiatApiJsonReceived(url, resp, error) {
|
||||
if (error) {
|
||||
appWindow.fiatApiError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resp = JSON.parse(resp);
|
||||
} catch (e) {
|
||||
appWindow.fiatApiError("bad JSON: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle incoming JSON, set ticker
|
||||
var currency = appWindow.fiatApiGetCurrency(resp);
|
||||
var currency = appWindow.fiatApiGetCurrency(url);
|
||||
if(typeof currency == "undefined"){
|
||||
appWindow.fiatApiError("could not get currency");
|
||||
return;
|
||||
}
|
||||
|
||||
var ticker = appWindow.fiatApiParseTicker(resp, currency);
|
||||
var ticker = appWindow.fiatApiParseTicker(url, resp, currency);
|
||||
if(ticker <= 0){
|
||||
appWindow.fiatApiError("could not get ticker");
|
||||
return;
|
||||
@@ -1227,7 +1255,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
var url = provider[userCurrency];
|
||||
Prices.getJSON(url);
|
||||
Network.getJSON(url, fiatApiJsonReceived);
|
||||
}
|
||||
|
||||
function fiatApiCurrencySymbol() {
|
||||
@@ -1283,9 +1311,8 @@ ApplicationWindow {
|
||||
walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
|
||||
walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
|
||||
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
|
||||
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
|
||||
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeededManager);
|
||||
IPC.uriHandler.connect(onUriHandler);
|
||||
Prices.priceJsonReceived.connect(appWindow.fiatApiJsonReceived);
|
||||
|
||||
if(typeof daemonManager != "undefined") {
|
||||
daemonManager.daemonStarted.connect(onDaemonStarted);
|
||||
@@ -1317,8 +1344,6 @@ ApplicationWindow {
|
||||
openWallet("wizard");
|
||||
}
|
||||
|
||||
checkUpdates();
|
||||
|
||||
if(persistentSettings.fiatPriceEnabled){
|
||||
appWindow.fiatApiRefresh();
|
||||
appWindow.fiatTimerStart();
|
||||
@@ -1363,10 +1388,14 @@ ApplicationWindow {
|
||||
property int segregationHeight: 0
|
||||
property int kdfRounds: 1
|
||||
property bool hideBalance: false
|
||||
property bool askPasswordBeforeSending: true
|
||||
property bool lockOnUserInActivity: true
|
||||
property int walletMode: 2
|
||||
property int lockOnUserInActivityInterval: 10 // minutes
|
||||
property bool blackTheme: true
|
||||
property bool checkForUpdates: true
|
||||
property bool autosave: true
|
||||
property int autosaveMinutes: 10
|
||||
|
||||
property bool fiatPriceEnabled: false
|
||||
property bool fiatPriceToggle: false
|
||||
@@ -1398,21 +1427,28 @@ ApplicationWindow {
|
||||
z: parent.z + 1
|
||||
id: transactionConfirmationPopup
|
||||
onAccepted: {
|
||||
var handleAccepted = function() {
|
||||
// Save transaction to file if view only wallet
|
||||
if (viewOnly) {
|
||||
saveTxDialog.open();
|
||||
} else {
|
||||
handleTransactionConfirmed()
|
||||
}
|
||||
}
|
||||
close();
|
||||
passwordDialog.onAcceptedCallback = function() {
|
||||
if(walletPassword === passwordDialog.password){
|
||||
// Save transaction to file if view only wallet
|
||||
if(viewOnly) {
|
||||
saveTxDialog.open();
|
||||
} else {
|
||||
handleTransactionConfirmed()
|
||||
}
|
||||
handleAccepted()
|
||||
} else {
|
||||
passwordDialog.showError(qsTr("Wrong password") + translationManager.emptyString);
|
||||
}
|
||||
}
|
||||
passwordDialog.onRejectedCallback = null;
|
||||
passwordDialog.open()
|
||||
if(!persistentSettings.askPasswordBeforeSending) {
|
||||
handleAccepted()
|
||||
} else {
|
||||
passwordDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,6 +1468,14 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.UpdateDialog {
|
||||
id: updateDialog
|
||||
|
||||
allowed: !passwordDialog.visible && !inputDialog.visible && !splash.visible
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
}
|
||||
|
||||
// Choose blockchain folder
|
||||
FileDialog {
|
||||
id: blockchainFileDialog
|
||||
@@ -1461,7 +1505,6 @@ ApplicationWindow {
|
||||
confirmationDialog.text += qsTr("Note: lmdb folder not found. A new folder will be created.") + "\n\n"
|
||||
|
||||
confirmationDialog.icon = StandardIcon.Question
|
||||
confirmationDialog.cancelText = qsTr("Cancel")
|
||||
|
||||
// Continue
|
||||
confirmationDialog.onAcceptedCallback = function() {
|
||||
@@ -1481,12 +1524,10 @@ ApplicationWindow {
|
||||
PasswordDialog {
|
||||
id: passwordDialog
|
||||
visible: false
|
||||
z: parent.z + 1
|
||||
z: parent.z + 2
|
||||
anchors.fill: parent
|
||||
property var onAcceptedCallback
|
||||
property var onRejectedCallback
|
||||
property var onAcceptedPassphraseCallback
|
||||
property var onRejectedPassphraseCallback
|
||||
onAccepted: {
|
||||
if (onAcceptedCallback)
|
||||
onAcceptedCallback();
|
||||
@@ -1510,14 +1551,13 @@ ApplicationWindow {
|
||||
informationPopup.open();
|
||||
}
|
||||
onRejectedNewPassword: {}
|
||||
onAcceptedPassphrase: {
|
||||
if (onAcceptedPassphraseCallback)
|
||||
onAcceptedPassphraseCallback();
|
||||
}
|
||||
onRejectedPassphrase: {
|
||||
if (onRejectedPassphraseCallback)
|
||||
onRejectedPassphraseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
DevicePassphraseDialog {
|
||||
id: devicePassphraseDialog
|
||||
visible: false
|
||||
z: parent.z + 1
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
InputDialog {
|
||||
@@ -1548,8 +1588,8 @@ ApplicationWindow {
|
||||
|
||||
ProcessingSplash {
|
||||
id: splash
|
||||
width: appWindow.width / 1.5
|
||||
height: appWindow.height / 2
|
||||
width: appWindow.width / 2
|
||||
height: appWindow.height / 2.66
|
||||
x: (appWindow.width - width) / 2
|
||||
y: (appWindow.height - height) / 2
|
||||
messageText: qsTr("Please wait...") + translationManager.emptyString
|
||||
@@ -1607,12 +1647,6 @@ ApplicationWindow {
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onMerchantClicked: {
|
||||
middlePanel.state = "Merchant";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onTxkeyClicked: {
|
||||
middlePanel.state = "TxKey";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
@@ -1687,16 +1721,10 @@ ApplicationWindow {
|
||||
anchors.fill: blurredArea
|
||||
source: blurredArea
|
||||
radius: 64
|
||||
visible: passwordDialog.visible || inputDialog.visible || splash.visible
|
||||
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible
|
||||
}
|
||||
|
||||
|
||||
WizardLang {
|
||||
id: languageView
|
||||
visible: false
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
property int minWidth: 326
|
||||
property int minHeight: 400
|
||||
MouseArea {
|
||||
@@ -1794,16 +1822,10 @@ ApplicationWindow {
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
}
|
||||
|
||||
Notifier {
|
||||
visible:false
|
||||
id: notifier
|
||||
}
|
||||
}
|
||||
|
||||
function toggleLanguageView(){
|
||||
middlePanel.visible = !middlePanel.visible;
|
||||
languageView.visible = !languageView.visible
|
||||
languageSidebar.isOpened ? languageSidebar.close() : languageSidebar.open();
|
||||
resetLanguageFields()
|
||||
// update after changing language from settings page
|
||||
if (persistentSettings.language != wizard.language_language) {
|
||||
@@ -1812,6 +1834,24 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: autosaveTimer
|
||||
interval: persistentSettings.autosaveMinutes * 60 * 1000
|
||||
repeat: true
|
||||
running: persistentSettings.autosave
|
||||
onTriggered: {
|
||||
if (currentWallet) {
|
||||
currentWallet.storeAsync(function(success) {
|
||||
if (success) {
|
||||
appWindow.showStatusMessage(qsTr("Autosaved the wallet"), 3);
|
||||
} else {
|
||||
appWindow.showStatusMessage(qsTr("Failed to autosave the wallet"), 3);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make the callback dynamic
|
||||
Timer {
|
||||
id: statusMessageTimer
|
||||
@@ -1851,9 +1891,6 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
function checkSimpleModeConnection(){
|
||||
// auto-connection mechanism for simple mode
|
||||
if(appWindow.walletMode >= 2) return;
|
||||
|
||||
const disconnectedTimeoutSec = 30;
|
||||
const firstCheckDelaySec = 2;
|
||||
|
||||
@@ -1870,15 +1907,20 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
if (appWindow.daemonRunning) {
|
||||
appWindow.stopDaemon();
|
||||
appWindow.stopDaemon(function() {
|
||||
appWindow.startDaemon("")
|
||||
});
|
||||
} else {
|
||||
appWindow.startDaemon("");
|
||||
}
|
||||
appWindow.startDaemon("");
|
||||
}
|
||||
|
||||
Timer {
|
||||
// Simple mode connection check timer
|
||||
id: simpleModeConnectionTimer
|
||||
interval: 2000; running: false; repeat: true
|
||||
interval: 2000
|
||||
running: appWindow.walletMode < 2 && currentWallet != undefined && !daemonStartStopInProgress
|
||||
repeat: true
|
||||
onTriggered: appWindow.checkSimpleModeConnection()
|
||||
}
|
||||
|
||||
@@ -1952,14 +1994,26 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
// If daemon is running - prompt user before exiting
|
||||
if(typeof daemonManager != "undefined" && daemonRunning) {
|
||||
if (appWindow.walletMode == 0) {
|
||||
stopDaemon(closeAccepted);
|
||||
} else {
|
||||
showDaemonIsRunningDialog(closeAccepted);
|
||||
}
|
||||
} else {
|
||||
if(daemonManager == undefined || persistentSettings.useRemoteNode) {
|
||||
closeAccepted();
|
||||
} else if (appWindow.walletMode == 0) {
|
||||
stopDaemon(closeAccepted);
|
||||
} else {
|
||||
showProcessingSplash(qsTr("Checking local node status..."));
|
||||
const handler = function(running) {
|
||||
hideProcessingSplash();
|
||||
if (running) {
|
||||
showDaemonIsRunningDialog(closeAccepted);
|
||||
} else {
|
||||
closeAccepted();
|
||||
}
|
||||
};
|
||||
|
||||
if (currentWallet) {
|
||||
handler(!currentWallet.disconnected);
|
||||
} else {
|
||||
daemonManager.runningAsync(persistentSettings.nettype, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1971,34 +2025,42 @@ ApplicationWindow {
|
||||
closeWallet(Qt.quit);
|
||||
}
|
||||
|
||||
function onWalletCheckUpdatesComplete(update) {
|
||||
if (update === "")
|
||||
return
|
||||
print("Update found: " + update)
|
||||
var parts = update.split("|")
|
||||
if (parts.length == 4) {
|
||||
var version = parts[0]
|
||||
var hash = parts[1]
|
||||
var user_url = parts[2]
|
||||
var msg = qsTr("New version of Monero v%1 is available.").arg(version)
|
||||
if (isMac || isWindows || isLinux) {
|
||||
msg += "<br><br>%1:<br>%2<br><br>%3:<br>%4".arg(qsTr("Download")).arg(user_url).arg(qsTr("SHA256 Hash")).arg(hash) + translationManager.emptyString
|
||||
} else {
|
||||
msg += " " + qsTr("Check out getmonero.org") + translationManager.emptyString
|
||||
}
|
||||
notifier.show(msg)
|
||||
} else {
|
||||
print("Failed to parse update spec")
|
||||
function onWalletCheckUpdatesComplete(version, downloadUrl, hash, firstSigner, secondSigner) {
|
||||
const alreadyAsked = updateDialog.url == downloadUrl && updateDialog.hash == hash;
|
||||
if (!alreadyAsked)
|
||||
{
|
||||
updateDialog.show(version, isMac || isWindows || isLinux ? downloadUrl : "", hash);
|
||||
}
|
||||
}
|
||||
|
||||
function getBuildTag() {
|
||||
if (isMac) {
|
||||
return "mac-x64";
|
||||
}
|
||||
if (isWindows) {
|
||||
return oshelper.installed ? "install-win-x64" : "win-x64";
|
||||
}
|
||||
if (isLinux) {
|
||||
return "linux-x64";
|
||||
}
|
||||
return "source";
|
||||
}
|
||||
|
||||
function checkUpdates() {
|
||||
walletManager.checkUpdatesAsync("monero-gui", "gui")
|
||||
const version = Version.GUI_VERSION.match(/\d+\.\d+\.\d+\.\d+/);
|
||||
if (version) {
|
||||
walletManager.checkUpdatesAsync("monero-gui", "gui", getBuildTag(), version[0]);
|
||||
} else {
|
||||
console.error("failed to parse version number", Version.GUI_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: updatesTimer
|
||||
interval: 3600*1000; running: true; repeat: true
|
||||
interval: 3600 * 1000
|
||||
repeat: true
|
||||
running: !disableCheckUpdatesFlag && persistentSettings.checkForUpdates
|
||||
triggeredOnStart: true
|
||||
onTriggered: checkUpdates()
|
||||
}
|
||||
|
||||
@@ -2078,7 +2140,7 @@ ApplicationWindow {
|
||||
if (mode < 2) {
|
||||
persistentSettings.useRemoteNode = false;
|
||||
|
||||
if (middlePanel.settingsView.settingsStateViewState === "Node" || middlePanel.settingsView.settingsStateViewState === "Log") {
|
||||
if (middlePanel.settingsView.settingsStateViewState === "Node") {
|
||||
middlePanel.settingsView.settingsStateViewState = "Wallet"
|
||||
}
|
||||
}
|
||||
@@ -2098,6 +2160,11 @@ ApplicationWindow {
|
||||
blackColor: "black"
|
||||
whiteColor: "white"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
}
|
||||
}
|
||||
|
||||
// borders on white theme + linux
|
||||
@@ -2165,8 +2232,12 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: QML type 'Drawer' has issues with buildbot; debug after Qt 5.9 migration
|
||||
// MoneroComponents.LanguageSidebar {
|
||||
// id: languageSidebar
|
||||
// }
|
||||
MoneroComponents.LanguageSidebar {
|
||||
id: languageSidebar
|
||||
dragMargin: 0
|
||||
}
|
||||
|
||||
WalletManager {
|
||||
id: walletManager
|
||||
}
|
||||
}
|
||||
|
||||
2
monero
@@ -53,6 +53,7 @@ HEADERS += \
|
||||
src/main/oscursor.h \
|
||||
src/libwalletqt/WalletManager.h \
|
||||
src/libwalletqt/Wallet.h \
|
||||
src/libwalletqt/PassphraseHelper.h \
|
||||
src/libwalletqt/PendingTransaction.h \
|
||||
src/libwalletqt/TransactionHistory.h \
|
||||
src/libwalletqt/TransactionInfo.h \
|
||||
@@ -76,11 +77,12 @@ HEADERS += \
|
||||
src/libwalletqt/UnsignedTransaction.h \
|
||||
src/main/Logger.h \
|
||||
src/main/MainApp.h \
|
||||
src/qt/downloader.h \
|
||||
src/qt/FutureScheduler.h \
|
||||
src/qt/ipc.h \
|
||||
src/qt/KeysFiles.h \
|
||||
src/qt/network.h \
|
||||
src/qt/utils.h \
|
||||
src/qt/prices.h \
|
||||
src/qt/macoshelper.h \
|
||||
src/qt/MoneroSettings.h \
|
||||
src/qt/TailsOS.h
|
||||
@@ -90,12 +92,15 @@ SOURCES += src/main/main.cpp \
|
||||
src/main/clipboardAdapter.cpp \
|
||||
src/main/oscursor.cpp \
|
||||
src/libwalletqt/WalletManager.cpp \
|
||||
src/libwalletqt/WalletListenerImpl.cpp \
|
||||
src/libwalletqt/Wallet.cpp \
|
||||
src/libwalletqt/PassphraseHelper.cpp \
|
||||
src/libwalletqt/PendingTransaction.cpp \
|
||||
src/libwalletqt/TransactionHistory.cpp \
|
||||
src/libwalletqt/TransactionInfo.cpp \
|
||||
src/libwalletqt/QRCodeImageProvider.cpp \
|
||||
src/main/oshelper.cpp \
|
||||
src/openpgp/openpgp.cpp \
|
||||
src/TranslationManager.cpp \
|
||||
src/model/TransactionHistoryModel.cpp \
|
||||
src/model/TransactionHistorySortFilterModel.cpp \
|
||||
@@ -112,11 +117,13 @@ SOURCES += src/main/main.cpp \
|
||||
src/libwalletqt/UnsignedTransaction.cpp \
|
||||
src/main/Logger.cpp \
|
||||
src/main/MainApp.cpp \
|
||||
src/qt/downloader.cpp \
|
||||
src/qt/FutureScheduler.cpp \
|
||||
src/qt/ipc.cpp \
|
||||
src/qt/KeysFiles.cpp \
|
||||
src/qt/network.cpp \
|
||||
src/qt/updater.cpp \
|
||||
src/qt/utils.cpp \
|
||||
src/qt/prices.cpp \
|
||||
src/qt/MoneroSettings.cpp \
|
||||
src/qt/TailsOS.cpp
|
||||
|
||||
@@ -157,6 +164,8 @@ ios:arm64 {
|
||||
}
|
||||
|
||||
LIBS_COMMON = \
|
||||
-lgcrypt \
|
||||
-lgpg-error \
|
||||
-lwallet_merged \
|
||||
-llmdb \
|
||||
-lepee \
|
||||
@@ -178,8 +187,10 @@ android {
|
||||
|
||||
|
||||
|
||||
QMAKE_CXXFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Werror -Wformat -Wformat-security
|
||||
QMAKE_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Werror -Wformat -Wformat-security
|
||||
QMAKE_CXXFLAGS += -Werror -Wformat -Wformat-security
|
||||
QMAKE_CFLAGS += -Werror -Wformat -Wformat-security
|
||||
QMAKE_CXXFLAGS_RELEASE += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2
|
||||
QMAKE_CFLAGS_RELEASE += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2
|
||||
|
||||
ios {
|
||||
message("Host is IOS")
|
||||
@@ -223,6 +234,14 @@ CONFIG(WITH_SCANNER) {
|
||||
LIBS += -lzbarjni -liconv
|
||||
} else {
|
||||
LIBS += -lzbar
|
||||
macx {
|
||||
ZBAR_DIR = $$system(brew --prefix zbar, lines, EXIT_CODE)
|
||||
equals(EXIT_CODE, 0) {
|
||||
INCLUDEPATH += $$ZBAR_DIR/include
|
||||
} else {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message("Skipping camera scanner because of Incompatible Qt Version !")
|
||||
@@ -388,6 +407,27 @@ macx {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
}
|
||||
|
||||
GCRYPT_DIR = $$system(brew --prefix libgcrypt, lines, EXIT_CODE)
|
||||
equals(EXIT_CODE, 0) {
|
||||
INCLUDEPATH += $$GCRYPT_DIR/include
|
||||
} else {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
}
|
||||
|
||||
GPGP_ERROR_DIR = $$system(brew --prefix libgpg-error, lines, EXIT_CODE)
|
||||
equals(EXIT_CODE, 0) {
|
||||
INCLUDEPATH += $$GPGP_ERROR_DIR/include
|
||||
} else {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
}
|
||||
|
||||
SODIUM_DIR = $$system(brew --prefix libsodium, lines, EXIT_CODE)
|
||||
equals(EXIT_CODE, 0) {
|
||||
INCLUDEPATH += $$SODIUM_DIR/include
|
||||
} else {
|
||||
INCLUDEPATH += /usr/local/include
|
||||
}
|
||||
|
||||
QT += macextras
|
||||
OBJECTIVE_SOURCES += src/qt/macoshelper.mm
|
||||
LIBS+= -Wl,-dead_strip
|
||||
@@ -527,7 +567,7 @@ DISTFILES += \
|
||||
|
||||
VERSION = $$cat('version.js', lines)
|
||||
VERSION = $$find(VERSION, 'GUI_VERSION')
|
||||
VERSION_LONG = $$replace(VERSION, '.*\"v(.*)\"', '\1')
|
||||
VERSION_LONG = $$replace(VERSION, '.*\"(.*)\"', '\1')
|
||||
VERSION = $$replace(VERSION, '.*(\d+\.\d+\.\d+\.\d+).*', '\1')
|
||||
|
||||
# windows application icon
|
||||
|
||||
@@ -186,7 +186,7 @@ Rectangle {
|
||||
delegate: Rectangle {
|
||||
id: tableItem2
|
||||
height: subaddressAccountListRow.subaddressAccountListItemHeight
|
||||
width: parent.width
|
||||
width: parent ? parent.width : undefined
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
|
||||
|
||||
@@ -79,13 +79,6 @@ Rectangle {
|
||||
topPadding: 0
|
||||
text: qsTr("Save your most used addresses here") + translationManager.emptyString
|
||||
width: parent.width
|
||||
|
||||
// @TODO: Legacy. Remove after Qt 5.8.
|
||||
// https://stackoverflow.com/questions/41990013
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -99,13 +92,6 @@ Rectangle {
|
||||
topPadding: 0
|
||||
text: qsTr("This makes it easier to send or receive Monero and reduces errors when typing in addresses manually.") + translationManager.emptyString
|
||||
width: parent.width
|
||||
|
||||
// @TODO: Legacy. Remove after Qt 5.8.
|
||||
// https://stackoverflow.com/questions/41990013
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
@@ -325,8 +311,6 @@ Rectangle {
|
||||
if (!parsed.error) {
|
||||
addressLine.text = parsed.address;
|
||||
descriptionLine.text = parsed.tx_description;
|
||||
} else {
|
||||
addressLine.text = clipboardText;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -569,8 +569,8 @@ Rectangle {
|
||||
delegate: Rectangle {
|
||||
id: delegate
|
||||
property bool collapsed: root.txDataCollapsed.indexOf(hash) >= 0 ? true : false
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent ? parent.left : undefined
|
||||
anchors.right: parent ? parent.right : undefined
|
||||
height: {
|
||||
if(!collapsed) return 60;
|
||||
if(isout && delegate.address !== "") return 320;
|
||||
@@ -1394,7 +1394,7 @@ Rectangle {
|
||||
txs.push(item);
|
||||
} else if(item.address !== "" && item.address.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
|
||||
txs.push(item);
|
||||
} else if(item.blockheight.toString().startsWith(root.sortSearchString)) {
|
||||
} else if(typeof item.blockheight !== "undefined" && item.blockheight.toString().startsWith(root.sortSearchString)) {
|
||||
txs.push(item);
|
||||
} else if(item.tx_note.toLowerCase().indexOf(root.sortSearchString.toLowerCase()) !== -1) {
|
||||
txs.push(item);
|
||||
@@ -1511,7 +1511,7 @@ Rectangle {
|
||||
"i": i,
|
||||
"isout": isout,
|
||||
"amount": Number(amount),
|
||||
"displayAmount": displayAmount + " XMR",
|
||||
"displayAmount": Utils.removeTrailingZeros(displayAmount.toFixed(12)) + " XMR",
|
||||
"hash": hash,
|
||||
"paymentId": paymentId,
|
||||
"address": address,
|
||||
|
||||
@@ -251,7 +251,19 @@ Rectangle {
|
||||
|
||||
function updateStatusText() {
|
||||
if (appWindow.isMining) {
|
||||
statusText.text = qsTr("Mining at %1 H/s").arg(walletManager.miningHashRate()) + translationManager.emptyString;
|
||||
var userHashRate = walletManager.miningHashRate();
|
||||
if (userHashRate === 0) {
|
||||
statusText.text = qsTr("Mining temporarily suspended.") + translationManager.emptyString;
|
||||
}
|
||||
else {
|
||||
var blockTime = 120;
|
||||
var blocksPerDay = 86400 / blockTime;
|
||||
var globalHashRate = walletManager.networkDifficulty() / blockTime;
|
||||
var probabilityFindNextBlock = userHashRate / globalHashRate;
|
||||
var probabilityFindBlockDay = 1 - Math.pow(1 - probabilityFindNextBlock, blocksPerDay);
|
||||
var chanceFindBlockDay = Math.round(1 / probabilityFindBlockDay);
|
||||
statusText.text = qsTr("Mining at %1 H/s. It gives you a 1 in %2 daily chance of finding a block.").arg(userHashRate).arg(chanceFindBlockDay) + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
else {
|
||||
statusText.text = qsTr("Not mining") + translationManager.emptyString;
|
||||
|
||||
@@ -105,7 +105,7 @@ Rectangle {
|
||||
delegate: Rectangle {
|
||||
id: tableItem2
|
||||
height: subaddressListRow.subaddressListItemHeight
|
||||
width: parent.width
|
||||
width: parent ? parent.width : undefined
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
import moneroComponents.Clipboard 1.0
|
||||
@@ -38,6 +39,7 @@ import "../components"
|
||||
import "../components" as MoneroComponents
|
||||
import "." 1.0
|
||||
import "../js/TxUtils.js" as TxUtils
|
||||
import "../js/Utils.js" as Utils
|
||||
|
||||
|
||||
Rectangle {
|
||||
@@ -54,8 +56,8 @@ Rectangle {
|
||||
property string sendButtonWarning: {
|
||||
// Currently opened wallet is not view-only
|
||||
if (appWindow.viewOnly) {
|
||||
return qsTr("Wallet is view-only and sends are not possible. Unless key images are imported, " +
|
||||
"the balance reflects only incoming but not outgoing transactions.") + translationManager.emptyString;
|
||||
return qsTr("Wallet is view-only and sends are only possible by using offline transaction signing. " +
|
||||
"Unless key images are imported, the balance reflects only incoming but not outgoing transactions.") + translationManager.emptyString;
|
||||
}
|
||||
|
||||
// There are sufficient unlocked funds available
|
||||
@@ -116,7 +118,6 @@ Rectangle {
|
||||
amountLine.text = ""
|
||||
setDescription("");
|
||||
priorityDropdown.currentIndex = 0
|
||||
updatePriorityDropdown()
|
||||
}
|
||||
|
||||
// Information dialog
|
||||
@@ -162,96 +163,6 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: appWindow.walletMode < 2 ? 1 : 2
|
||||
Layout.fillWidth: true
|
||||
columnSpacing: 32
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 200
|
||||
|
||||
// Amount input
|
||||
LineEdit {
|
||||
id: amountLine
|
||||
Layout.fillWidth: true
|
||||
inlineIcon: true
|
||||
labelText: "<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
|
||||
%1 <a href='#'>(%2)</a>".arg(qsTr("Amount")).arg(qsTr("Change account"))
|
||||
+ translationManager.emptyString
|
||||
copyButton: !isNaN(amountLine.text) && persistentSettings.fiatPriceEnabled
|
||||
copyButtonText: fiatApiCurrencySymbol() + " ~" + fiatApiConvertToFiat(amountLine.text)
|
||||
copyButtonEnabled: false
|
||||
|
||||
onLabelLinkActivated: {
|
||||
middlePanel.accountView.selectAndSend = true;
|
||||
appWindow.showPageRequest("Account")
|
||||
}
|
||||
placeholderText: "0.00"
|
||||
width: 100
|
||||
fontBold: true
|
||||
inlineButtonText: qsTr("All") + translationManager.emptyString
|
||||
inlineButton.onClicked: amountLine.text = "(all)"
|
||||
onTextChanged: {
|
||||
const match = amountLine.text.match(/^0+(\d.*)/);
|
||||
if (match) {
|
||||
const cursorPosition = amountLine.cursorPosition;
|
||||
amountLine.text = match[1];
|
||||
amountLine.cursorPosition = Math.max(cursorPosition, 1) - 1;
|
||||
} else if(amountLine.text.indexOf('.') === 0){
|
||||
amountLine.text = '0' + amountLine.text;
|
||||
if (amountLine.text.length > 2) {
|
||||
amountLine.cursorPosition = 1;
|
||||
}
|
||||
}
|
||||
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
|
||||
}
|
||||
|
||||
validator: RegExpValidator {
|
||||
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: appWindow.walletMode >= 2
|
||||
Layout.fillWidth: true
|
||||
Label {
|
||||
id: transactionPriority
|
||||
Layout.topMargin: 12
|
||||
text: qsTr("Transaction priority") + translationManager.emptyString
|
||||
fontBold: false
|
||||
fontSize: 16
|
||||
}
|
||||
// Note: workaround for translations in listElements
|
||||
// ListElement: cannot use script for property value, so
|
||||
// code like this wont work:
|
||||
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
|
||||
// For translations to work, the strings need to be listed in
|
||||
// the file components/StandardDropdown.qml too.
|
||||
|
||||
// Priorites after v5
|
||||
ListModel {
|
||||
id: priorityModelV5
|
||||
|
||||
ListElement { column1: qsTr("Automatic") ; column2: ""; priority: 0}
|
||||
ListElement { column1: qsTr("Slow (x0.2 fee)") ; column2: ""; priority: 1}
|
||||
ListElement { column1: qsTr("Normal (x1 fee)") ; column2: ""; priority: 2 }
|
||||
ListElement { column1: qsTr("Fast (x5 fee)") ; column2: ""; priority: 3 }
|
||||
ListElement { column1: qsTr("Fastest (x200 fee)") ; column2: ""; priority: 4 }
|
||||
}
|
||||
|
||||
StandardDropdown {
|
||||
Layout.fillWidth: true
|
||||
id: priorityDropdown
|
||||
Layout.topMargin: 5
|
||||
currentIndex: 0
|
||||
}
|
||||
}
|
||||
// Make sure dropdown is on top
|
||||
z: parent.z + 1
|
||||
}
|
||||
|
||||
// recipient address input
|
||||
RowLayout {
|
||||
id: addressLineRow
|
||||
@@ -260,10 +171,9 @@ Rectangle {
|
||||
LineEditMulti {
|
||||
id: addressLine
|
||||
spacing: 0
|
||||
inputPaddingRight: inlineButtonVisible && inlineButton2Visible ? 100 : 60
|
||||
fontBold: true
|
||||
labelText: qsTr("<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
|
||||
%1 <a href='#'>(%2)</a>").arg(qsTr("Address")).arg(qsTr("Address book"))
|
||||
+ translationManager.emptyString
|
||||
labelText: qsTr("Address") + translationManager.emptyString
|
||||
labelButtonText: qsTr("Resolve") + translationManager.emptyString
|
||||
placeholderText: {
|
||||
if(persistentSettings.nettype == NetworkType.MAINNET){
|
||||
@@ -276,10 +186,6 @@ Rectangle {
|
||||
}
|
||||
wrapMode: Text.WrapAnywhere
|
||||
addressValidation: true
|
||||
onInputLabelLinkActivated: {
|
||||
middlePanel.addressBookView.selectAndSend = true;
|
||||
appWindow.showPageRequest("AddressBook");
|
||||
}
|
||||
onTextChanged: {
|
||||
const parsed = walletManager.parse_uri_to_object(text);
|
||||
if (!parsed.error) {
|
||||
@@ -289,16 +195,27 @@ Rectangle {
|
||||
setDescription(parsed.tx_description);
|
||||
}
|
||||
}
|
||||
inlineButton.text: FontAwesome.qrcode
|
||||
inlineButton.text: FontAwesome.addressBook
|
||||
inlineButton.buttonHeight: 30
|
||||
inlineButton.fontPixelSize: 22
|
||||
inlineButton.fontFamily: FontAwesome.fontFamily
|
||||
inlineButton.textColor: MoneroComponents.Style.defaultFontColor
|
||||
inlineButton.buttonColor: MoneroComponents.Style.orange
|
||||
inlineButton.onClicked: {
|
||||
cameraUi.state = "Capture"
|
||||
cameraUi.qrcode_decoded.connect(updateFromQrCode)
|
||||
middlePanel.addressBookView.selectAndSend = true;
|
||||
appWindow.showPageRequest("AddressBook");
|
||||
}
|
||||
inlineButtonVisible : appWindow.qrScannerEnabled && !addressLine.text
|
||||
inlineButtonVisible: true
|
||||
|
||||
inlineButton2.text: FontAwesome.qrcode
|
||||
inlineButton2.buttonHeight: 30
|
||||
inlineButton2.fontPixelSize: 22
|
||||
inlineButton2.fontFamily: FontAwesome.fontFamily
|
||||
inlineButton2.textColor: MoneroComponents.Style.defaultFontColor
|
||||
inlineButton2.onClicked: {
|
||||
cameraUi.state = "Capture"
|
||||
cameraUi.qrcode_decoded.connect(updateFromQrCode)
|
||||
}
|
||||
inlineButton2Visible: appWindow.qrScannerEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,6 +265,143 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: appWindow.walletMode < 2 ? 1 : 2
|
||||
Layout.fillWidth: true
|
||||
columnSpacing: 32
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 200
|
||||
|
||||
// Amount input
|
||||
LineEdit {
|
||||
id: amountLine
|
||||
Layout.fillWidth: true
|
||||
inlineIcon: true
|
||||
labelText: "<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
|
||||
%1 <a href='#'>(%2)</a>".arg(qsTr("Amount")).arg(qsTr("Change account"))
|
||||
+ translationManager.emptyString
|
||||
copyButton: !isNaN(amountLine.text) && persistentSettings.fiatPriceEnabled
|
||||
copyButtonText: "~%1 %2".arg(fiatApiConvertToFiat(amountLine.text)).arg(fiatApiCurrencySymbol())
|
||||
copyButtonEnabled: false
|
||||
|
||||
onLabelLinkActivated: {
|
||||
middlePanel.accountView.selectAndSend = true;
|
||||
appWindow.showPageRequest("Account")
|
||||
}
|
||||
placeholderText: "0.00"
|
||||
width: 100
|
||||
fontBold: true
|
||||
inlineButtonText: qsTr("All") + translationManager.emptyString
|
||||
inlineButton.onClicked: amountLine.text = "(all)"
|
||||
onTextChanged: {
|
||||
amountLine.text = amountLine.text.replace(",", ".");
|
||||
const match = amountLine.text.match(/^0+(\d.*)/);
|
||||
if (match) {
|
||||
const cursorPosition = amountLine.cursorPosition;
|
||||
amountLine.text = match[1];
|
||||
amountLine.cursorPosition = Math.max(cursorPosition, 1) - 1;
|
||||
} else if(amountLine.text.indexOf('.') === 0){
|
||||
amountLine.text = '0' + amountLine.text;
|
||||
if (amountLine.text.length > 2) {
|
||||
amountLine.cursorPosition = 1;
|
||||
}
|
||||
}
|
||||
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
|
||||
}
|
||||
|
||||
validator: RegExpValidator {
|
||||
regExp: /^(\d{1,8})?([\.,]\d{1,12})?$/
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: feeLabel
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.topMargin: 12
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 14
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
property bool estimating: false
|
||||
property var estimatedFee: null
|
||||
property string estimatedFeeFiat: {
|
||||
if (!persistentSettings.fiatPriceEnabled || estimatedFee == null) {
|
||||
return "";
|
||||
}
|
||||
const fiatFee = fiatApiConvertToFiat(estimatedFee);
|
||||
return " (%1 %3)".arg(fiatFee < 0.01 ? "<0.01" : "~" + fiatFee).arg(fiatApiCurrencySymbol());
|
||||
}
|
||||
property var fee: {
|
||||
estimatedFee = null;
|
||||
estimating = sendButton.enabled;
|
||||
if (!sendButton.enabled || !currentWallet) {
|
||||
return;
|
||||
}
|
||||
currentWallet.estimateTransactionFeeAsync(
|
||||
addressLine.text,
|
||||
walletManager.amountFromString(amountLine.text),
|
||||
priorityModelV5.get(priorityDropdown.currentIndex).priority,
|
||||
function (amount) {
|
||||
estimatedFee = Utils.removeTrailingZeros(amount);
|
||||
estimating = false;
|
||||
});
|
||||
}
|
||||
text: {
|
||||
if (!sendButton.enabled || estimatedFee == null) {
|
||||
return ""
|
||||
}
|
||||
return "%1: ~%2 XMR".arg(qsTr("Fee")).arg(estimatedFee) +
|
||||
estimatedFeeFiat +
|
||||
translationManager.emptyString;
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.right: parent.right
|
||||
running: feeLabel.estimating
|
||||
height: parent.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: appWindow.walletMode >= 2
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Label {
|
||||
id: transactionPriority
|
||||
Layout.topMargin: 0
|
||||
text: qsTr("Transaction priority") + translationManager.emptyString
|
||||
fontBold: false
|
||||
fontSize: 16
|
||||
}
|
||||
// Note: workaround for translations in listElements
|
||||
// ListElement: cannot use script for property value, so
|
||||
// code like this wont work:
|
||||
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
|
||||
// For translations to work, the strings need to be listed in
|
||||
// the file components/StandardDropdown.qml too.
|
||||
|
||||
// Priorites after v5
|
||||
ListModel {
|
||||
id: priorityModelV5
|
||||
|
||||
ListElement { column1: qsTr("Automatic") ; column2: ""; priority: 0}
|
||||
ListElement { column1: qsTr("Slow (x0.2 fee)") ; column2: ""; priority: 1}
|
||||
ListElement { column1: qsTr("Normal (x1 fee)") ; column2: ""; priority: 2 }
|
||||
ListElement { column1: qsTr("Fast (x5 fee)") ; column2: ""; priority: 3 }
|
||||
ListElement { column1: qsTr("Fastest (x200 fee)") ; column2: ""; priority: 4 }
|
||||
}
|
||||
|
||||
StandardDropdown {
|
||||
Layout.preferredWidth: 200
|
||||
id: priorityDropdown
|
||||
Layout.topMargin: 5
|
||||
currentIndex: 0
|
||||
dataModel: priorityModelV5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.WarningBox {
|
||||
text: qsTr("Description field contents match long payment ID format. \
|
||||
Please don't paste long payment ID into description field, your funds might be lost.") + translationManager.emptyString;
|
||||
@@ -461,10 +515,9 @@ Rectangle {
|
||||
id: advancedLayout
|
||||
anchors.top: pageRoot.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 20
|
||||
anchors.topMargin: 32
|
||||
spacing: 26
|
||||
spacing: 10
|
||||
enabled: !viewOnly || pageRoot.enabled
|
||||
|
||||
RowLayout {
|
||||
@@ -479,84 +532,88 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
AdvancedOptionsItem {
|
||||
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||
columns: 6
|
||||
|
||||
StandardButton {
|
||||
id: sweepUnmixableButton
|
||||
text: qsTr("Sweep Unmixable") + translationManager.emptyString
|
||||
enabled : pageRoot.enabled
|
||||
small: true
|
||||
onClicked: {
|
||||
console.log("Transfer: sweepUnmixableClicked")
|
||||
root.sweepUnmixableClicked()
|
||||
}
|
||||
title: qsTr("Key images") + translationManager.emptyString
|
||||
button1.text: qsTr("Export") + translationManager.emptyString
|
||||
button1.enabled: !appWindow.viewOnly
|
||||
button1.onClicked: {
|
||||
console.log("Transfer: export key images clicked")
|
||||
exportKeyImagesDialog.open();
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: saveTxButton
|
||||
text: qsTr("Create tx file") + translationManager.emptyString
|
||||
visible: appWindow.viewOnly
|
||||
enabled: pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)
|
||||
small: true
|
||||
onClicked: {
|
||||
console.log("Transfer: saveTx Clicked")
|
||||
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
|
||||
console.log("priority: " + priority)
|
||||
console.log("amount: " + amountLine.text)
|
||||
addressLine.text = addressLine.text.trim()
|
||||
setPaymentId(paymentIdLine.text.trim());
|
||||
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
|
||||
|
||||
}
|
||||
button2.text: qsTr("Import") + translationManager.emptyString
|
||||
button2.enabled: appWindow.viewOnly && appWindow.isTrustedDaemon()
|
||||
button2.onClicked: {
|
||||
console.log("Transfer: import key images clicked")
|
||||
importKeyImagesDialog.open();
|
||||
}
|
||||
helpTextLarge.text: qsTr("Required for view-only wallets to display the real balance") + translationManager.emptyString
|
||||
helpTextSmall.text: {
|
||||
var errorMessage = "";
|
||||
if (appWindow.viewOnly && !appWindow.isTrustedDaemon()){
|
||||
errorMessage = "<p class='orange'>" + qsTr("* To import, you must connect to a local node or a trusted remote node") + "</p>";
|
||||
}
|
||||
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:#ffffff;} p.orange{color:#ff9323;}</style>" +
|
||||
"<p>" + qsTr("1. Using cold wallet, export the key images into a file") + "</p>" +
|
||||
"<p>" + qsTr("2. Using view-only wallet, import the key images file") + "</p>" +
|
||||
errorMessage + translationManager.emptyString
|
||||
}
|
||||
helpTextSmall.themeTransition: false
|
||||
}
|
||||
|
||||
AdvancedOptionsItem {
|
||||
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||
title: qsTr("Offline transaction signing") + translationManager.emptyString
|
||||
button1.text: qsTr("Create") + translationManager.emptyString
|
||||
button1.enabled: appWindow.viewOnly && pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)
|
||||
button1.onClicked: {
|
||||
console.log("Transfer: saveTx Clicked")
|
||||
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
|
||||
console.log("priority: " + priority)
|
||||
console.log("amount: " + amountLine.text)
|
||||
addressLine.text = addressLine.text.trim()
|
||||
setPaymentId(paymentIdLine.text.trim());
|
||||
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
|
||||
}
|
||||
button2.text: qsTr("Sign (offline)") + translationManager.emptyString
|
||||
button2.enabled: !appWindow.viewOnly
|
||||
button2.onClicked: {
|
||||
console.log("Transfer: sign tx clicked")
|
||||
signTxDialog.open();
|
||||
}
|
||||
button3.text: qsTr("Submit") + translationManager.emptyString
|
||||
button3.enabled: appWindow.viewOnly
|
||||
button3.onClicked: {
|
||||
console.log("Transfer: submit tx clicked")
|
||||
submitTxDialog.open();
|
||||
}
|
||||
helpTextLarge.text: qsTr("Spend XMR from a cold (offline) wallet") + translationManager.emptyString
|
||||
helpTextSmall.text: {
|
||||
var errorMessage = "";
|
||||
if (appWindow.viewOnly && !pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)){
|
||||
errorMessage = "<p class='orange'>" + qsTr("* To create a transaction file, please enter address and amount above") + "</p>";
|
||||
}
|
||||
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:#ffffff;} p.orange{color:#ff9323;}</style>" +
|
||||
"<p>" + qsTr("1. Using view-only wallet, export the outputs into a file") + "</p>" +
|
||||
"<p>" + qsTr("2. Using cold wallet, import the outputs file and export the key images") + "</p>" +
|
||||
"<p>" + qsTr("3. Using view-only wallet, import the key images file and create a transaction file") + "</p>" +
|
||||
errorMessage +
|
||||
"<p>" + qsTr("4. Using cold wallet, sign your transaction file") + "</p>" +
|
||||
"<p>" + qsTr("5. Using view-only wallet, submit your signed transaction") + "</p>" + translationManager.emptyString
|
||||
}
|
||||
helpTextSmall.themeTransition: false
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: signTxButton
|
||||
text: qsTr("Sign tx file") + translationManager.emptyString
|
||||
small: true
|
||||
visible: !appWindow.viewOnly
|
||||
onClicked: {
|
||||
console.log("Transfer: sign tx clicked")
|
||||
signTxDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: submitTxButton
|
||||
text: qsTr("Submit tx file") + translationManager.emptyString
|
||||
small: true
|
||||
visible: appWindow.viewOnly
|
||||
enabled: pageRoot.enabled
|
||||
onClicked: {
|
||||
console.log("Transfer: submit tx clicked")
|
||||
submitTxDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: exportKeyImagesButton
|
||||
text: qsTr("Export key images") + translationManager.emptyString
|
||||
small: true
|
||||
visible: !appWindow.viewOnly
|
||||
enabled: pageRoot.enabled
|
||||
onClicked: {
|
||||
console.log("Transfer: export key images clicked")
|
||||
exportKeyImagesDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: importKeyImagesButton
|
||||
text: qsTr("Import key images") + translationManager.emptyString
|
||||
small: true
|
||||
enabled: appWindow.viewOnly && appWindow.isTrustedDaemon()
|
||||
onClicked: {
|
||||
console.log("Transfer: import key images clicked")
|
||||
importKeyImagesDialog.open();
|
||||
}
|
||||
AdvancedOptionsItem {
|
||||
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||
title: qsTr("Unmixable outputs") + translationManager.emptyString
|
||||
button1.text: qsTr("Sweep") + translationManager.emptyString
|
||||
button1.enabled : pageRoot.enabled
|
||||
button1.onClicked: {
|
||||
console.log("Transfer: sweepUnmixableClicked")
|
||||
root.sweepUnmixableClicked()
|
||||
}
|
||||
helpTextLarge.text: qsTr("Create a transaction that spends old unmovable outputs") + translationManager.emptyString
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,12 +746,6 @@ Rectangle {
|
||||
function onPageCompleted() {
|
||||
console.log("transfer page loaded")
|
||||
updateStatus();
|
||||
updatePriorityDropdown()
|
||||
}
|
||||
|
||||
function updatePriorityDropdown() {
|
||||
priorityDropdown.dataModel = priorityModelV5;
|
||||
priorityDropdown.update()
|
||||
}
|
||||
|
||||
//TODO: Add daemon sync status
|
||||
@@ -717,6 +768,8 @@ Rectangle {
|
||||
|
||||
switch (currentWallet.connected()) {
|
||||
case Wallet.ConnectionStatus_Connecting:
|
||||
root.warningContent = qsTr("Wallet is connecting to daemon.")
|
||||
break
|
||||
case Wallet.ConnectionStatus_Disconnected:
|
||||
root.warningContent = messageNotConnected;
|
||||
break
|
||||
|
||||
@@ -25,6 +25,7 @@ Item {
|
||||
anchors.margins: 0
|
||||
|
||||
property int minWidth: 900
|
||||
property int minHeight: 600
|
||||
property int qrCodeSize: 220
|
||||
property bool enableTracking: false
|
||||
property string trackingError: "" // setting this will show a message @ tracking table
|
||||
@@ -33,6 +34,9 @@ Item {
|
||||
property var hiddenAmounts: []
|
||||
|
||||
function onPageCompleted() {
|
||||
if (appWindow.currentWallet) {
|
||||
appWindow.current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0)
|
||||
}
|
||||
// prepare tracking
|
||||
trackingCheckbox.checked = root.enableTracking
|
||||
root.update();
|
||||
@@ -67,7 +71,7 @@ Item {
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
visible: parent.width >= root.minWidth
|
||||
visible: parent.width >= root.minWidth && appWindow.height >= root.minHeight
|
||||
spacing: 0
|
||||
|
||||
// emulates max-width + center for container
|
||||
@@ -544,7 +548,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: appWindow.showPageRequest("Receive")
|
||||
onClicked: appWindow.showPageRequest("Settings")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -553,7 +557,7 @@ Item {
|
||||
|
||||
Rectangle {
|
||||
// Shows when the window is too small
|
||||
visible: parent.width < root.minWidth
|
||||
visible: parent.width < root.minWidth || appWindow.height < root.minHeight
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 100;
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -570,6 +574,13 @@ Item {
|
||||
text: qsTr("The merchant page requires a larger window") + translationManager.emptyString
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: appWindow.showPageRequest("Settings")
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
@@ -589,9 +600,7 @@ Item {
|
||||
|
||||
var model = appWindow.currentWallet.historyModel
|
||||
var count = model.rowCount()
|
||||
var totalAmount = 0
|
||||
var nTransactions = 0
|
||||
var blockchainHeight = null
|
||||
var txs = []
|
||||
|
||||
// Currently selected subaddress as per Receive page
|
||||
@@ -607,8 +616,6 @@ Item {
|
||||
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
|
||||
|
||||
if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == current_subaddress_table_index) {
|
||||
var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole);
|
||||
totalAmount = walletManager.addi(totalAmount, amount)
|
||||
nTransactions += 1
|
||||
|
||||
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
|
||||
@@ -616,21 +623,17 @@ Item {
|
||||
|
||||
var in_txpool = false;
|
||||
var confirmations = 0;
|
||||
var displayAmount = 0;
|
||||
var displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
|
||||
|
||||
if (blockHeight == 0) {
|
||||
if (blockHeight === undefined) {
|
||||
in_txpool = true;
|
||||
} else {
|
||||
if (blockchainHeight == null)
|
||||
blockchainHeight = walletManager.blockchainHeight()
|
||||
confirmations = blockchainHeight - blockHeight - 1
|
||||
displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
|
||||
confirmations = model.data(idx, TransactionHistoryModel.TransactionConfirmationsRole);
|
||||
}
|
||||
|
||||
txs.push({
|
||||
"amount": displayAmount,
|
||||
"confirmations": confirmations,
|
||||
"blockheight": blockHeight,
|
||||
"in_txpool": in_txpool,
|
||||
"txid": txid,
|
||||
"time_epoch": timeEpoch,
|
||||
@@ -650,9 +653,7 @@ Item {
|
||||
txs.forEach(function(tx){
|
||||
trackingModel.append({
|
||||
"amount": tx.amount,
|
||||
"blockheight": tx.blockheight,
|
||||
"confirmations": tx.confirmations,
|
||||
"blockheight": tx.blockHeight,
|
||||
"in_txpool": tx.in_txpool,
|
||||
"txid": tx.txid,
|
||||
"time_epoch": tx.time_epoch,
|
||||
|
||||
@@ -125,7 +125,7 @@ ListView {
|
||||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
color: hide_amount ? "#707070" : "#009F1E"
|
||||
text: hide_amount ? '-' : '+' + amount
|
||||
text: hide_amount ? '-' : '+' + amount + (in_txpool ? ' (%1)'.arg(qsTr('unconfirmed')) : '')
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
selectByMouse: true
|
||||
readOnly: true
|
||||
|
||||
@@ -273,7 +273,6 @@ Rectangle {
|
||||
// LOG
|
||||
id: navLog
|
||||
property bool isActive: settingsStateView.state === "Log"
|
||||
visible: appWindow.walletMode >= 2
|
||||
Layout.preferredWidth: navLogText.width + grid.textMargin
|
||||
Layout.preferredHeight: 32
|
||||
Layout.minimumWidth: 72
|
||||
|
||||
@@ -103,7 +103,7 @@ Rectangle {
|
||||
MoneroComponents.TextBlock {
|
||||
font.pixelSize: 14
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: Version.GUI_MONERO_VERSION + translationManager.emptyString
|
||||
text: moneroVersion
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -131,10 +131,11 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.TextBlock {
|
||||
id: walletLocation
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
font.pixelSize: 14
|
||||
property string walletPath: (isIOS ? moneroAccountsDir : "") + appWindow.walletPath()
|
||||
property string walletPath: (isIOS ? moneroAccountsDir : "") + persistentSettings.wallet_path
|
||||
text: "\
|
||||
<style type='text/css'>\
|
||||
a {cursor:pointer;text-decoration: none; color: #FF6C3C}\
|
||||
@@ -212,7 +213,6 @@ Rectangle {
|
||||
+ "The old wallet cache file will be renamed and can be restored later.\n"
|
||||
);
|
||||
confirmationDialog.icon = StandardIcon.Question
|
||||
confirmationDialog.cancelText = qsTr("Cancel")
|
||||
confirmationDialog.onAcceptedCallback = function() {
|
||||
appWindow.closeWallet(function() {
|
||||
walletManager.clearWalletCache(persistentSettings.wallet_path);
|
||||
@@ -230,7 +230,7 @@ Rectangle {
|
||||
appWindow.showStatusMessage(qsTr("Invalid restore height specified. Must be a number or a date formatted YYYY-MM-DD"),3);
|
||||
}
|
||||
inputDialog.onRejectedCallback = null;
|
||||
inputDialog.open(currentWallet ? currentWallet.walletCreationHeight : "0")
|
||||
inputDialog.open(currentWallet ? currentWallet.walletCreationHeight.toFixed(0) : "0")
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -381,32 +381,38 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy info to clipboard
|
||||
MoneroComponents.StandardButton {
|
||||
small: true
|
||||
text: qsTr("Copy to clipboard") + translationManager.emptyString
|
||||
onClicked: {
|
||||
var data = "";
|
||||
data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")";
|
||||
data += "\nEmbedded Monero version: " + Version.GUI_MONERO_VERSION;
|
||||
data += "\nWallet path: ";
|
||||
RowLayout {
|
||||
spacing: 20;
|
||||
|
||||
var wallet_path = walletPath();
|
||||
if(isIOS)
|
||||
wallet_path = moneroAccountsDir + wallet_path;
|
||||
data += wallet_path;
|
||||
MoneroComponents.StandardButton {
|
||||
small: true
|
||||
text: qsTr("Copy to clipboard") + translationManager.emptyString
|
||||
onClicked: {
|
||||
var data = "";
|
||||
data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")";
|
||||
data += "\nEmbedded Monero version: " + moneroVersion;
|
||||
data += "\nWallet path: " + walletLocation.walletPath;
|
||||
|
||||
data += "\nWallet creation height: ";
|
||||
if(currentWallet)
|
||||
data += currentWallet.walletCreationHeight;
|
||||
data += "\nWallet creation height: ";
|
||||
if(currentWallet)
|
||||
data += currentWallet.walletCreationHeight;
|
||||
|
||||
data += "\nWallet log path: " + walletLogPath;
|
||||
data += "\nWallet mode: " + walletModeString;
|
||||
data += "\nGraphics: " + isOpenGL ? "OpenGL" : "Low graphics mode";
|
||||
data += "\nWallet log path: " + walletLogPath;
|
||||
data += "\nWallet mode: " + walletModeString;
|
||||
data += "\nGraphics: " + isOpenGL ? "OpenGL" : "Low graphics mode";
|
||||
|
||||
console.log("Copied to clipboard");
|
||||
clipboard.setText(data);
|
||||
appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3);
|
||||
console.log("Copied to clipboard");
|
||||
clipboard.setText(data);
|
||||
appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3);
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
small: true
|
||||
text: qsTr("Donate to Monero") + translationManager.emptyString
|
||||
onClicked: {
|
||||
middlePanel.sendTo("888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", "", "Donation to Monero Core Team");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,14 @@ Rectangle {
|
||||
text: qsTr("Custom decorations") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: checkForUpdatesCheckBox
|
||||
enabled: !disableCheckUpdatesFlag
|
||||
checked: persistentSettings.checkForUpdates && !disableCheckUpdatesFlag
|
||||
onClicked: persistentSettings.checkForUpdates = !persistentSettings.checkForUpdates
|
||||
text: qsTr("Check for updates periodically") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: hideBalanceCheckBox
|
||||
checked: persistentSettings.hideBalance
|
||||
@@ -78,6 +86,46 @@ Rectangle {
|
||||
persistentSettings.blackTheme = MoneroComponents.Style.blackTheme;
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
checked: persistentSettings.askPasswordBeforeSending
|
||||
text: qsTr("Ask for password before sending a transaction") + translationManager.emptyString
|
||||
toggleOnClick: false
|
||||
onClicked: {
|
||||
if (persistentSettings.askPasswordBeforeSending) {
|
||||
passwordDialog.onAcceptedCallback = function() {
|
||||
if (appWindow.walletPassword === passwordDialog.password){
|
||||
persistentSettings.askPasswordBeforeSending = false;
|
||||
} else {
|
||||
passwordDialog.showError(qsTr("Wrong password"));
|
||||
}
|
||||
}
|
||||
passwordDialog.onRejectedCallback = null;
|
||||
passwordDialog.open()
|
||||
} else {
|
||||
persistentSettings.askPasswordBeforeSending = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
checked: persistentSettings.autosave
|
||||
onClicked: persistentSettings.autosave = !persistentSettings.autosave
|
||||
text: qsTr("Autosave") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.Slider {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 35
|
||||
Layout.topMargin: 6
|
||||
visible: persistentSettings.autosave
|
||||
from: 1
|
||||
stepSize: 1
|
||||
to: 60
|
||||
value: persistentSettings.autosaveMinutes
|
||||
text: "%1 %2 %3".arg(qsTr("Every")).arg(value).arg(qsTr("minute(s)")) + translationManager.emptyString
|
||||
onMoved: persistentSettings.autosaveMinutes = value
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: userInActivityCheckbox
|
||||
@@ -86,70 +134,20 @@ Rectangle {
|
||||
text: qsTr("Lock wallet on inactivity") + translationManager.emptyString
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
MoneroComponents.Slider {
|
||||
visible: userInActivityCheckbox.checked
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 6
|
||||
Layout.leftMargin: 42
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.pixelSize: 14
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
var val = userInactivitySlider.value;
|
||||
var minutes = val > 1 ? qsTr("minutes") : qsTr("minute");
|
||||
|
||||
qsTr("After ") + val + " " + minutes + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
|
||||
Slider {
|
||||
id: userInactivitySlider
|
||||
from: 1
|
||||
value: persistentSettings.lockOnUserInActivityInterval
|
||||
to: 60
|
||||
leftPadding: 0
|
||||
stepSize: 1
|
||||
snapMode: Slider.SnapAlways
|
||||
|
||||
background: Rectangle {
|
||||
x: parent.leftPadding
|
||||
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||
implicitWidth: 200
|
||||
implicitHeight: 4
|
||||
width: parent.availableWidth
|
||||
height: implicitHeight
|
||||
radius: 2
|
||||
color: MoneroComponents.Style.progressBarBackgroundColor
|
||||
|
||||
Rectangle {
|
||||
width: parent.visualPosition * parent.width
|
||||
height: parent.height
|
||||
color: MoneroComponents.Style.green
|
||||
radius: 2
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
|
||||
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||
implicitWidth: 18
|
||||
implicitHeight: 18
|
||||
radius: 8
|
||||
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
|
||||
border.color: MoneroComponents.Style.grey
|
||||
}
|
||||
|
||||
onMoved: persistentSettings.lockOnUserInActivityInterval = userInactivitySlider.value;
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
Layout.leftMargin: 35
|
||||
from: 1
|
||||
stepSize: 1
|
||||
to: 60
|
||||
value: persistentSettings.lockOnUserInActivityInterval
|
||||
text: {
|
||||
var minutes = value > 1 ? qsTr("minutes") : qsTr("minute");
|
||||
return qsTr("After ") + value + " " + minutes + translationManager.emptyString;
|
||||
}
|
||||
onMoved: persistentSettings.lockOnUserInActivityInterval = value
|
||||
}
|
||||
|
||||
//! Manage pricing
|
||||
@@ -212,6 +210,7 @@ Rectangle {
|
||||
MoneroComponents.StandardDropdown {
|
||||
id: fiatPriceCurrencyDropdown
|
||||
Layout.fillWidth: true
|
||||
currentIndex: persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1
|
||||
dataModel: fiatPriceCurrencyModel
|
||||
onChanged: {
|
||||
var obj = dataModel.get(currentIndex);
|
||||
@@ -297,10 +296,6 @@ Rectangle {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
fiatPriceProviderDropDown.update();
|
||||
fiatPriceCurrencyDropdown.currentIndex = persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1;
|
||||
fiatPriceCurrencyDropdown.update();
|
||||
|
||||
console.log('SettingsLayout loaded');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ Rectangle {
|
||||
textFormat: TextEdit.RichText
|
||||
selectByMouse: true
|
||||
selectByKeyboard: true
|
||||
font.family: MoneroComponents.Style.defaultFontColor
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 14
|
||||
wrapMode: TextEdit.Wrap
|
||||
readOnly: true
|
||||
@@ -213,9 +213,27 @@ Rectangle {
|
||||
MoneroComponents.LineEdit {
|
||||
id: sendCommandText
|
||||
Layout.fillWidth: true
|
||||
property var lastCommands: []
|
||||
property int currentCommandIndex
|
||||
fontBold: false
|
||||
placeholderText: qsTr("command + enter (e.g 'help' or 'status')") + translationManager.emptyString
|
||||
placeholderFontSize: 16
|
||||
Keys.onUpPressed: {
|
||||
if (currentCommandIndex != 0) {
|
||||
sendCommandText.text = lastCommands[currentCommandIndex - 1]
|
||||
currentCommandIndex = currentCommandIndex - 1
|
||||
}
|
||||
}
|
||||
Keys.onDownPressed: {
|
||||
if (currentCommandIndex == lastCommands.length - 1) {
|
||||
currentCommandIndex = lastCommands.length;
|
||||
return text = "";
|
||||
}
|
||||
if (currentCommandIndex != lastCommands.length) {
|
||||
sendCommandText.text = lastCommands[currentCommandIndex + 1]
|
||||
currentCommandIndex = currentCommandIndex + 1
|
||||
}
|
||||
}
|
||||
onAccepted: {
|
||||
if(text.length > 0) {
|
||||
consoleArea.logCommand(">>> " + text)
|
||||
@@ -225,15 +243,14 @@ Rectangle {
|
||||
}
|
||||
});
|
||||
}
|
||||
lastCommands.push(text);
|
||||
currentCommandIndex = lastCommands.length;
|
||||
text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
logLevelDropdown.currentIndex = appWindow.persistentSettings.logLevel;
|
||||
logLevelDropdown.update();
|
||||
|
||||
if(typeof daemonManager != "undefined")
|
||||
daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated)
|
||||
}
|
||||
|
||||
@@ -130,13 +130,6 @@ Rectangle{
|
||||
topPadding: 0
|
||||
text: qsTr("The blockchain is downloaded to your computer. Provides higher security and requires more local storage.") + translationManager.emptyString
|
||||
width: parent.width - (localNodeIcon.width + localNodeIcon.anchors.leftMargin + anchors.leftMargin)
|
||||
|
||||
// @TODO: Legacy. Remove after Qt 5.8.
|
||||
// https://stackoverflow.com/questions/41990013
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,13 +222,6 @@ Rectangle{
|
||||
topPadding: 0
|
||||
text: qsTr("Uses a third-party server to connect to the Monero network. Less secure, but easier on your computer.") + translationManager.emptyString
|
||||
width: parent.width - (remoteNodeIcon.width + remoteNodeIcon.anchors.leftMargin + anchors.leftMargin)
|
||||
|
||||
// @TODO: Legacy. Remove after Qt 5.8.
|
||||
// https://stackoverflow.com/questions/41990013
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -279,12 +265,10 @@ Rectangle{
|
||||
Layout.minimumWidth: 100
|
||||
placeholderFontSize: 15
|
||||
|
||||
daemonAddrLabelText: qsTr("Address")
|
||||
daemonPortLabelText: qsTr("Port")
|
||||
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
||||
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
||||
|
||||
property var rna: persistentSettings.remoteNodeAddress
|
||||
daemonAddrText: rna.search(":") != -1 ? rna.split(":")[0].trim() : ""
|
||||
daemonPortText: rna.search(":") != -1 ? (rna.split(":")[1].trim() == "") ? appWindow.getDefaultDaemonRpcPort(persistentSettings.nettype) : rna.split(":")[1] : ""
|
||||
initialAddress: persistentSettings.remoteNodeAddress
|
||||
onEditingFinished: {
|
||||
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
|
||||
console.log("setting remote node to " + persistentSettings.remoteNodeAddress);
|
||||
@@ -318,7 +302,7 @@ Rectangle{
|
||||
labelText: qsTr("Daemon password") + translationManager.emptyString
|
||||
text: persistentSettings.daemonPassword
|
||||
placeholderText: qsTr("Password") + translationManager.emptyString
|
||||
echoMode: TextInput.Password
|
||||
password: true
|
||||
placeholderFontSize: 15
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
@@ -382,14 +366,12 @@ Rectangle{
|
||||
labelFontSize: 14
|
||||
property string style: "<style type='text/css'>a {cursor:pointer;text-decoration: none; color: #FF6C3C}</style>"
|
||||
labelText: qsTr("Blockchain location") + style + " <a href='#'> (%1)</a>".arg(qsTr("Change")) + translationManager.emptyString
|
||||
labelButtonText: qsTr("Reset") + translationManager.emptyString
|
||||
labelButtonVisible: text
|
||||
placeholderText: qsTr("(default)") + translationManager.emptyString
|
||||
placeholderFontSize: 15
|
||||
readOnly: true
|
||||
text: {
|
||||
if(persistentSettings.blockchainDataDir.length > 0){
|
||||
return persistentSettings.blockchainDataDir;
|
||||
} else { return "" }
|
||||
}
|
||||
text: persistentSettings.blockchainDataDir
|
||||
addressValidation: false
|
||||
onInputLabelLinkActivated: {
|
||||
//mouse.accepted = false
|
||||
@@ -399,6 +381,7 @@ Rectangle{
|
||||
blockchainFileDialog.open();
|
||||
blockchainFolder.focus = true;
|
||||
}
|
||||
onLabelButtonClicked: persistentSettings.blockchainDataDir = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +396,12 @@ Rectangle{
|
||||
placeholderFontSize: 15
|
||||
text: persistentSettings.daemonFlags
|
||||
addressValidation: false
|
||||
onEditingFinished: persistentSettings.daemonFlags = daemonFlags.text;
|
||||
error: text.match(/(^|\s)--(data-dir|bootstrap-daemon-address)/)
|
||||
onEditingFinished: {
|
||||
if (!daemonFlags.error) {
|
||||
persistentSettings.daemonFlags = daemonFlags.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -427,17 +415,9 @@ Rectangle{
|
||||
Layout.minimumWidth: 100
|
||||
Layout.bottomMargin: 20
|
||||
|
||||
daemonAddrLabelText: qsTr("Bootstrap Address")
|
||||
daemonPortLabelText: qsTr("Bootstrap Port")
|
||||
daemonAddrText: persistentSettings.bootstrapNodeAddress.split(":")[0].trim()
|
||||
daemonPortText: {
|
||||
var node_split = persistentSettings.bootstrapNodeAddress.split(":");
|
||||
if(node_split.length == 2){
|
||||
(node_split[1].trim() == "") ? appWindow.getDefaultDaemonRpcPort(persistentSettings.nettype) : node_split[1];
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
daemonAddrLabelText: qsTr("Bootstrap Address") + translationManager.emptyString
|
||||
daemonPortLabelText: qsTr("Bootstrap Port") + translationManager.emptyString
|
||||
initialAddress: persistentSettings.bootstrapNodeAddress
|
||||
onEditingFinished: {
|
||||
if (daemonAddrText == "auto") {
|
||||
persistentSettings.bootstrapNodeAddress = daemonAddrText;
|
||||
|
||||
@@ -30,6 +30,7 @@ import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Dialogs 1.2
|
||||
import FontAwesome 1.0
|
||||
|
||||
import "../../js/Utils.js" as Utils
|
||||
import "../../components" as MoneroComponents
|
||||
@@ -47,10 +48,10 @@ Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 20
|
||||
anchors.topMargin: 0
|
||||
spacing: 8
|
||||
spacing: 0
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
buttonText: qsTr("Close wallet") + translationManager.emptyString
|
||||
iconText: FontAwesome.signOutAlt
|
||||
description: qsTr("Logs out of this wallet.") + translationManager.emptyString
|
||||
title: qsTr("Close this wallet") + translationManager.emptyString
|
||||
|
||||
@@ -58,7 +59,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
buttonText: qsTr("Create wallet") + translationManager.emptyString
|
||||
iconText: FontAwesome.eye
|
||||
description: qsTr("Creates a new wallet that can only view and initiate transactions, but requires a spendable wallet to sign transactions before sending.") + translationManager.emptyString
|
||||
title: qsTr("Create a view-only wallet") + translationManager.emptyString
|
||||
visible: !appWindow.viewOnly
|
||||
@@ -80,7 +81,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
buttonText: qsTr("Show seed") + translationManager.emptyString
|
||||
iconText: FontAwesome.key
|
||||
description: qsTr("Store this information safely to recover your wallet in the future.") + translationManager.emptyString
|
||||
title: qsTr("Show seed & keys") + translationManager.emptyString
|
||||
|
||||
@@ -90,7 +91,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
buttonText: qsTr("Rescan") + translationManager.emptyString
|
||||
iconText: FontAwesome.repeat
|
||||
description: qsTr("Use this feature if you think the shown balance is not accurate.") + translationManager.emptyString
|
||||
title: qsTr("Rescan wallet balance") + translationManager.emptyString
|
||||
visible: appWindow.walletMode >= 2
|
||||
@@ -114,7 +115,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
buttonText: qsTr("Change password") + translationManager.emptyString
|
||||
iconText: FontAwesome.ellipsisH
|
||||
description: qsTr("Change the password of your wallet.") + translationManager.emptyString
|
||||
title: qsTr("Change wallet password") + translationManager.emptyString
|
||||
|
||||
@@ -135,6 +136,19 @@ Rectangle {
|
||||
passwordDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.SettingsListItem {
|
||||
iconText: FontAwesome.cashRegister
|
||||
isLast: true
|
||||
description: qsTr("Receive Monero for your business, easily.") + translationManager.emptyString
|
||||
title: qsTr("Enter merchant mode") + translationManager.emptyString
|
||||
|
||||
onClicked: {
|
||||
middlePanel.state = "Merchant";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
updateBalance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
20
qml.qrc
@@ -5,10 +5,14 @@
|
||||
<file>MiddlePanel.qml</file>
|
||||
<file>components/Label.qml</file>
|
||||
<file>components/SettingsListItem.qml</file>
|
||||
<file>components/Slider.qml</file>
|
||||
<file>components/UpdateDialog.qml</file>
|
||||
<file>images/whatIsIcon.png</file>
|
||||
<file>images/whatIsIcon@2x.png</file>
|
||||
<file>images/lockIcon.png</file>
|
||||
<file>components/MenuButton.qml</file>
|
||||
<file>monero/utils/gpg_keys/binaryfate.asc</file>
|
||||
<file>monero/utils/gpg_keys/fluffypony.asc</file>
|
||||
<file>monero/utils/gpg_keys/luigi1111.asc</file>
|
||||
<file>pages/Account.qml</file>
|
||||
<file>pages/Transfer.qml</file>
|
||||
<file>pages/History.qml</file>
|
||||
@@ -23,7 +27,7 @@
|
||||
<file>components/TipItem.qml</file>
|
||||
<file>images/tip.png</file>
|
||||
<file>components/MenuButtonDivider.qml</file>
|
||||
<file>images/moneroIcon.png</file>
|
||||
<file>images/monero-vector.svg</file>
|
||||
<file>components/StandardDropdown.qml</file>
|
||||
<file>images/whiteDropIndicator.png</file>
|
||||
<file>images/whiteDropIndicator@2x.png</file>
|
||||
@@ -34,7 +38,6 @@
|
||||
<file>images/prevMonth.png</file>
|
||||
<file>images/prevMonth@2x.png</file>
|
||||
<file>components/TitleBar.qml</file>
|
||||
<file>images/moneroLogo2.png</file>
|
||||
<file>images/resize.png</file>
|
||||
<file>images/resize@2x.png</file>
|
||||
<file>images/resizeHovered.png</file>
|
||||
@@ -96,11 +99,11 @@
|
||||
<file>components/ProcessingSplash.qml</file>
|
||||
<file>components/ProgressBar.qml</file>
|
||||
<file>components/StandardDialog.qml</file>
|
||||
<file>components/DevicePassphraseDialog.qml</file>
|
||||
<file>pages/Sign.qml</file>
|
||||
<file>components/DaemonManagerDialog.qml</file>
|
||||
<file>version.js</file>
|
||||
<file>components/QRCodeScanner.qml</file>
|
||||
<file>components/Notifier.qml</file>
|
||||
<file>components/TextBlock.qml</file>
|
||||
<file>components/RemoteNodeEdit.qml</file>
|
||||
<file>pages/Keys.qml</file>
|
||||
@@ -110,8 +113,6 @@
|
||||
<file>images/card-background-white.png</file>
|
||||
<file>images/card-background-white@2x.png</file>
|
||||
<file>images/moneroLogo_white.png</file>
|
||||
<file>images/question.png</file>
|
||||
<file>images/question@2x.png</file>
|
||||
<file>images/titlebarLogo.png</file>
|
||||
<file>images/titlebarLogo@2x.png</file>
|
||||
<file>pages/merchant/MerchantTitlebar.qml</file>
|
||||
@@ -188,7 +189,6 @@
|
||||
<file>wizard/WizardHeader.qml</file>
|
||||
<file>wizard/WizardHome.qml</file>
|
||||
<file>wizard/WizardLanguage.qml</file>
|
||||
<file>wizard/WizardLang.qml</file>
|
||||
<file>wizard/WizardNav.qml</file>
|
||||
<file>wizard/WizardWalletInput.qml</file>
|
||||
<file>wizard/WizardRestoreWallet1.qml</file>
|
||||
@@ -204,7 +204,6 @@
|
||||
<file>js/Wizard.js</file>
|
||||
<file>components/LanguageSidebar.qml</file>
|
||||
<file>images/world-flags-globe.png</file>
|
||||
<file>images/langFlagGrey.png</file>
|
||||
<file>images/restore-wallet-from-hardware@2x.png</file>
|
||||
<file>images/restore-wallet-from-hardware.png</file>
|
||||
<file>images/open-wallet-from-file@2x.png</file>
|
||||
@@ -219,7 +218,6 @@
|
||||
<file>images/local-node@2x.png</file>
|
||||
<file>images/local-node-full.png</file>
|
||||
<file>images/local-node-full@2x.png</file>
|
||||
<file>wizard/WizardNavProgressDot.qml</file>
|
||||
<file>wizard/WizardOpenWallet1.qml</file>
|
||||
<file>images/arrow-right-in-circle.png</file>
|
||||
<file>images/arrow-right-in-circle@2x.png</file>
|
||||
@@ -233,7 +231,6 @@
|
||||
<file>images/themes/white/close.svg</file>
|
||||
<file>images/themes/white/fullscreen.svg</file>
|
||||
<file>images/themes/white/minimize.svg</file>
|
||||
<file>images/themes/white/question.svg</file>
|
||||
<file>components/effects/ColorTransition.qml</file>
|
||||
<file>components/effects/GradientBackground.qml</file>
|
||||
<file>images/check-white.svg</file>
|
||||
@@ -241,5 +238,8 @@
|
||||
<file>images/edit.svg</file>
|
||||
<file>images/arrow-right-in-circle-outline-medium-white.svg</file>
|
||||
<file>images/tails-grey.png</file>
|
||||
<file>components/AdvancedOptionsItem.qml</file>
|
||||
<file>images/busy-indicator.png</file>
|
||||
<file>images/busy-indicator@2x.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -3,6 +3,7 @@ add_subdirectory(QR-Code-scanner)
|
||||
add_subdirectory(daemon)
|
||||
add_subdirectory(libwalletqt)
|
||||
add_subdirectory(model)
|
||||
add_subdirectory(openpgp)
|
||||
add_subdirectory(zxcvbn-c)
|
||||
|
||||
qt5_add_resources(RESOURCES ../qml.qrc)
|
||||
@@ -14,7 +15,9 @@ file(GLOB SOURCE_FILES
|
||||
"main/*.h"
|
||||
"main/*.cpp"
|
||||
"libwalletqt/WalletManager.cpp"
|
||||
"libwalletqt/WalletListenerImpl.cpp"
|
||||
"libwalletqt/Wallet.cpp"
|
||||
"libwalletqt/PassphraseHelper.cpp"
|
||||
"libwalletqt/PendingTransaction.cpp"
|
||||
"libwalletqt/TransactionHistory.cpp"
|
||||
"libwalletqt/TransactionInfo.cpp"
|
||||
@@ -28,6 +31,7 @@ file(GLOB SOURCE_FILES
|
||||
"libwalletqt/UnsignedTransaction.cpp"
|
||||
"libwalletqt/WalletManager.h"
|
||||
"libwalletqt/Wallet.h"
|
||||
"libwalletqt/PassphraseHelper.h"
|
||||
"libwalletqt/PendingTransaction.h"
|
||||
"libwalletqt/TransactionHistory.h"
|
||||
"libwalletqt/TransactionInfo.h"
|
||||
@@ -40,13 +44,16 @@ file(GLOB SOURCE_FILES
|
||||
"libwalletqt/Subaddress.h"
|
||||
"libwalletqt/SubaddressAccount.h"
|
||||
"libwalletqt/UnsignedTransaction.h"
|
||||
"daemon/*.h"
|
||||
"daemon/*.cpp"
|
||||
"model/*.h"
|
||||
"model/*.cpp"
|
||||
"qt/*.h"
|
||||
"daemon/*.h"
|
||||
"daemon/*.cpp"
|
||||
"model/*.h"
|
||||
"model/*.cpp"
|
||||
"qt/*.h"
|
||||
"qt/*.cpp"
|
||||
)
|
||||
if(APPLE)
|
||||
list(APPEND SOURCE_FILES "qt/macoshelper.mm")
|
||||
endif()
|
||||
|
||||
if(ENABLE_PASS_STRENGTH_METER)
|
||||
file(GLOB PASS_STRENGTH_FILES
|
||||
@@ -76,25 +83,36 @@ if(MINGW)
|
||||
list(APPEND RESOURCES ${ICON_RES})
|
||||
endif()
|
||||
|
||||
add_executable(monero-gui ${EXECUTABLE_FLAG} main/main.cpp
|
||||
if(APPLE)
|
||||
set(ICON ${PROJECT_SOURCE_DIR}/images/appicon.icns)
|
||||
set_source_files_properties(${ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
|
||||
list(APPEND RESOURCES ${ICON})
|
||||
endif()
|
||||
|
||||
add_executable(monero-wallet-gui ${EXECUTABLE_FLAG} main/main.cpp
|
||||
${SOURCE_FILES}
|
||||
${PASS_STRENGTH_FILES}
|
||||
${QR_CODE_FILES}
|
||||
${RESOURCES}
|
||||
)
|
||||
set_property(TARGET monero-gui PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
set_target_properties(monero-wallet-gui PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/share/Info.plist"
|
||||
)
|
||||
|
||||
# OpenGL
|
||||
target_include_directories(monero-gui PUBLIC ${OPENGL_INCLUDE_DIR})
|
||||
target_include_directories(monero-wallet-gui PUBLIC ${OPENGL_INCLUDE_DIR})
|
||||
message(STATUS "OpenGL: include dir at ${OPENGL_INCLUDE_DIR}")
|
||||
message(STATUS "OpenGL: libraries at ${OPENGL_LIBRARIES}")
|
||||
|
||||
target_include_directories(monero-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
target_include_directories(monero-wallet-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
file(GLOB_RECURSE SRC_SOURCES *.cpp)
|
||||
file(GLOB_RECURSE SRC_HEADERS *.h)
|
||||
|
||||
target_include_directories(monero-gui PUBLIC
|
||||
target_include_directories(monero-wallet-gui PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/monero/include
|
||||
${CMAKE_SOURCE_DIR}/monero/src
|
||||
${CMAKE_SOURCE_DIR}/monero/external/easylogging++
|
||||
@@ -114,33 +132,24 @@ target_include_directories(monero-gui PUBLIC
|
||||
${ZBAR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_compile_definitions(monero-gui
|
||||
target_compile_definitions(monero-wallet-gui
|
||||
PUBLIC
|
||||
${Qt5Widgets_DEFINITIONS}
|
||||
${Qt5Qml_DEFINITIONS}
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
|
||||
if(X11_FOUND)
|
||||
target_link_libraries(monero-gui ${X11_LIBRARIES} pthread dl Xt xcb X11)
|
||||
endif()
|
||||
|
||||
if(DEVICE_TREZOR_READY)
|
||||
target_link_libraries(monero-gui ${TREZOR_DEP_LIBS})
|
||||
endif()
|
||||
|
||||
target_link_libraries(monero-gui
|
||||
${CMAKE_BINARY_DIR}/lib/libwallet_merged.a
|
||||
target_link_libraries(monero-wallet-gui
|
||||
wallet_merged
|
||||
${LMDB_LIBRARY}
|
||||
${CMAKE_BINARY_DIR}/monero/contrib/epee/src/libepee.a
|
||||
${CMAKE_BINARY_DIR}/monero/external/unbound/libunbound.a
|
||||
epee
|
||||
${UNBOUND_LIBRARY}
|
||||
${SODIUM_LIBRARY}
|
||||
${CMAKE_BINARY_DIR}/monero/external/easylogging++/libeasylogging.a
|
||||
${CMAKE_BINARY_DIR}/monero/src/blockchain_db/libblockchain_db.a
|
||||
${CMAKE_BINARY_DIR}/monero/external/randomx/librandomx.a
|
||||
${CMAKE_BINARY_DIR}/monero/src/hardforks/libhardforks.a
|
||||
easylogging
|
||||
blockchain_db
|
||||
randomx
|
||||
hardforks
|
||||
${Boost_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
@@ -149,10 +158,20 @@ target_link_libraries(monero-gui
|
||||
${QT5_LIBRARIES}
|
||||
${EXTRA_LIBRARIES}
|
||||
${ICU_LIBRARIES}
|
||||
openpgp
|
||||
translations
|
||||
)
|
||||
|
||||
if(DEVICE_TREZOR_READY)
|
||||
target_link_libraries(monero-wallet-gui ${TREZOR_DEP_LIBS})
|
||||
endif()
|
||||
|
||||
if(X11_FOUND)
|
||||
target_link_libraries(monero-wallet-gui ${X11_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(WITH_SCANNER)
|
||||
target_link_libraries(monero-gui
|
||||
target_link_libraries(monero-wallet-gui
|
||||
${ZBAR_LIBRARIES}
|
||||
jpeg
|
||||
v4l2
|
||||
@@ -161,6 +180,6 @@ if(WITH_SCANNER)
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS monero-gui
|
||||
install(TARGETS monero-wallet-gui
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ QrCodeScanner::QrCodeScanner(QObject *parent)
|
||||
m_probe = new QVideoProbe(this);
|
||||
m_thread = new QrScanThread(this);
|
||||
m_thread->start();
|
||||
QObject::connect(m_thread, SIGNAL(decoded(int,QString)), this, SLOT(processCode(int,QString)));
|
||||
QObject::connect(m_thread, SIGNAL(decoded(int, QString)), this, SIGNAL(decoded(int, QString)));
|
||||
QObject::connect(m_thread, SIGNAL(notifyError(const QString &, bool)), this, SIGNAL(notifyError(const QString &, bool)));
|
||||
connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame)));
|
||||
}
|
||||
@@ -48,37 +48,6 @@ void QrCodeScanner::setSource(QCamera *camera)
|
||||
{
|
||||
m_probe->setSource(camera);
|
||||
}
|
||||
void QrCodeScanner::processCode(int type, const QString &data)
|
||||
{
|
||||
if (! m_enabled) return;
|
||||
qDebug() << "decoded - type: " << type << " data: " << data;
|
||||
QString address, payment_id, tx_description, recipient_name, error;
|
||||
QVector<QString> unknown_parameters;
|
||||
uint64_t amount(0);
|
||||
if( ! WalletManager::instance()->parse_uri(data, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error) )
|
||||
{
|
||||
qDebug() << "Failed to parse_uri : " << error;
|
||||
emit notifyError(error);
|
||||
return;
|
||||
}
|
||||
QVariantMap parsed_unknown_parameters;
|
||||
if(unknown_parameters.size() > 0)
|
||||
{
|
||||
qDebug() << "unknown parameters " << unknown_parameters;
|
||||
foreach(const QString &item, unknown_parameters )
|
||||
{
|
||||
QStringList parsed_item = item.split("=");
|
||||
if(parsed_item.size() == 2) {
|
||||
parsed_unknown_parameters.insert(parsed_item[0], parsed_item[1]);
|
||||
}
|
||||
}
|
||||
emit notifyError(error, true);
|
||||
}
|
||||
qDebug() << "Parsed URI : " << address << " " << payment_id << " " << amount << " " << tx_description << " " << recipient_name << " " << error;
|
||||
QString s_amount = WalletManager::instance()->displayAmount(amount);
|
||||
qDebug() << "Amount passed " << s_amount ;
|
||||
emit decoded(address, payment_id, s_amount, tx_description, recipient_name, parsed_unknown_parameters);
|
||||
}
|
||||
void QrCodeScanner::processFrame(QVideoFrame frame)
|
||||
{
|
||||
if(frame.isValid()){
|
||||
|
||||
@@ -51,13 +51,12 @@ public:
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
public Q_SLOTS:
|
||||
void processCode(int type, const QString &data);
|
||||
void processFrame(QVideoFrame);
|
||||
|
||||
Q_SIGNALS:
|
||||
void enabledChanged();
|
||||
|
||||
void decoded(const QString &address, const QString &payment_id, const QString &amount, const QString &tx_description, const QString &recipient_name, const QVariantMap &extra_parameters);
|
||||
void decoded(int type, const QString &data);
|
||||
void decode(int type, const QString &data);
|
||||
void notifyError(const QString &error, bool warning = false);
|
||||
|
||||
|
||||
@@ -30,13 +30,8 @@
|
||||
#include <QtGlobal>
|
||||
#include <QDebug>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
extern QImage qt_imageFromVideoFrame(const QVideoFrame &f);
|
||||
#else
|
||||
QImage qt_imageFromVideoFrame(const QVideoFrame &f){
|
||||
Q_ASSERT_X(0 != 0, "qt_imageFromVideoFrame", "Should have been managed in .pro");
|
||||
return QImage();
|
||||
}
|
||||
#endif
|
||||
|
||||
QrScanThread::QrScanThread(QObject *parent)
|
||||
@@ -80,7 +75,11 @@ bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
|
||||
unsigned int bpl( qimg.bytesPerLine() ), width( bpl / 4), height( qimg.height());
|
||||
dst.set_size(width, height);
|
||||
dst.set_format("BGR4");
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
unsigned long datalen = qimg.sizeInBytes();
|
||||
#else
|
||||
unsigned long datalen = qimg.byteCount();
|
||||
#endif
|
||||
dst.set_data(qimg.bits(), datalen);
|
||||
if((width * 4 != bpl) || (width * height * 4 > datalen)){
|
||||
emit notifyError(QString("QImage to Zbar::Image failed !"));
|
||||
@@ -104,7 +103,11 @@ void QrScanThread::processQImage(const QImage &qimg)
|
||||
|
||||
void QrScanThread::processVideoFrame(const QVideoFrame &frame)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
processQImage( qt_imageFromVideoFrame(frame) );
|
||||
#else
|
||||
processQImage(frame.image());
|
||||
#endif
|
||||
}
|
||||
|
||||
void QrScanThread::stop()
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "DaemonManager.h"
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QMutexLocker>
|
||||
#include <QThread>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
@@ -113,8 +114,8 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
|
||||
|
||||
arguments << "--check-updates" << "disabled";
|
||||
|
||||
// --max-concurrency based on threads available. max: 6
|
||||
int32_t concurrency = qBound(1, QThread::idealThreadCount() / 2, 6);
|
||||
// --max-concurrency based on threads available.
|
||||
int32_t concurrency = qMax(1, QThread::idealThreadCount() / 2);
|
||||
|
||||
if(!flags.contains("--max-concurrency", Qt::CaseSensitive)){
|
||||
arguments << "--max-concurrency" << QString::number(concurrency);
|
||||
@@ -123,18 +124,19 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
|
||||
qDebug() << "starting monerod " + m_monerod;
|
||||
qDebug() << "With command line arguments " << arguments;
|
||||
|
||||
m_daemon = new QProcess();
|
||||
initialized = true;
|
||||
QMutexLocker locker(&m_daemonMutex);
|
||||
|
||||
m_daemon.reset(new QProcess());
|
||||
|
||||
// Connect output slots
|
||||
connect (m_daemon, SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
|
||||
connect (m_daemon, SIGNAL(readyReadStandardError()), this, SLOT(printError()));
|
||||
connect(m_daemon.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
|
||||
connect(m_daemon.get(), SIGNAL(readyReadStandardError()), this, SLOT(printError()));
|
||||
|
||||
// Start monerod
|
||||
bool started = m_daemon->startDetached(m_monerod, arguments);
|
||||
|
||||
// add state changed listener
|
||||
connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState)));
|
||||
connect(m_daemon.get(), SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(stateChanged(QProcess::ProcessState)));
|
||||
|
||||
if (!started) {
|
||||
qDebug() << "Daemon start error: " + m_daemon->errorString();
|
||||
@@ -200,9 +202,9 @@ bool DaemonManager::stopWatcher(NetworkType::Type nettype) const
|
||||
if(counter >= 5) {
|
||||
qDebug() << "Killing it! ";
|
||||
#ifdef Q_OS_WIN
|
||||
QProcess::execute("taskkill /F /IM monerod.exe");
|
||||
QProcess::execute("taskkill", {"/F", "/IM", "monerod.exe"});
|
||||
#else
|
||||
QProcess::execute("pkill monerod");
|
||||
QProcess::execute("pkill", {"monerod"});
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -223,7 +225,10 @@ void DaemonManager::stateChanged(QProcess::ProcessState state)
|
||||
|
||||
void DaemonManager::printOutput()
|
||||
{
|
||||
QByteArray byteArray = m_daemon->readAllStandardOutput();
|
||||
QByteArray byteArray = [this]() {
|
||||
QMutexLocker locker(&m_daemonMutex);
|
||||
return m_daemon->readAllStandardOutput();
|
||||
}();
|
||||
QStringList strLines = QString(byteArray).split("\n");
|
||||
|
||||
foreach (QString line, strLines) {
|
||||
@@ -234,7 +239,10 @@ void DaemonManager::printOutput()
|
||||
|
||||
void DaemonManager::printError()
|
||||
{
|
||||
QByteArray byteArray = m_daemon->readAllStandardError();
|
||||
QByteArray byteArray = [this]() {
|
||||
QMutexLocker locker(&m_daemonMutex);
|
||||
return m_daemon->readAllStandardError();
|
||||
}();
|
||||
QStringList strLines = QString(byteArray).split("\n");
|
||||
|
||||
foreach (QString line, strLines) {
|
||||
@@ -351,7 +359,6 @@ DaemonManager::DaemonManager(QObject *parent)
|
||||
|
||||
if (m_monerod.length() == 0) {
|
||||
qCritical() << "no daemon binary defined for current platform";
|
||||
m_has_daemon = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#ifndef DAEMONMANAGER_H
|
||||
#define DAEMONMANAGER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <QProcess>
|
||||
@@ -78,10 +81,9 @@ private:
|
||||
|
||||
static DaemonManager * m_instance;
|
||||
static QStringList m_clArgs;
|
||||
QProcess *m_daemon;
|
||||
bool initialized = false;
|
||||
std::unique_ptr<QProcess> m_daemon;
|
||||
QMutex m_daemonMutex;
|
||||
QString m_monerod;
|
||||
bool m_has_daemon = true;
|
||||
bool m_app_exit = false;
|
||||
bool m_noSync = false;
|
||||
|
||||
|
||||
70
src/libwalletqt/PassphraseHelper.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "PassphraseHelper.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QDebug>
|
||||
|
||||
Monero::optional<std::string> PassphraseHelper::onDevicePassphraseRequest(bool & on_device)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
QMutexLocker locker(&m_mutex_pass);
|
||||
m_passphrase_on_device = true;
|
||||
m_passphrase_abort = false;
|
||||
|
||||
if (m_prompter != nullptr){
|
||||
m_prompter->onWalletPassphraseNeeded(on_device);
|
||||
}
|
||||
|
||||
m_cond_pass.wait(&m_mutex_pass);
|
||||
|
||||
if (m_passphrase_abort)
|
||||
{
|
||||
throw std::runtime_error("Passphrase entry abort");
|
||||
}
|
||||
|
||||
on_device = m_passphrase_on_device;
|
||||
if (!on_device) {
|
||||
auto tmpPass = m_passphrase.toStdString();
|
||||
m_passphrase = QString();
|
||||
return Monero::optional<std::string>(tmpPass);
|
||||
} else {
|
||||
return Monero::optional<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
void PassphraseHelper::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
QMutexLocker locker(&m_mutex_pass);
|
||||
m_passphrase = passphrase;
|
||||
m_passphrase_abort = entry_abort;
|
||||
m_passphrase_on_device = enter_on_device;
|
||||
|
||||
m_cond_pass.wakeAll();
|
||||
}
|
||||
74
src/libwalletqt/PassphraseHelper.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef MONERO_GUI_PASSPHRASEHELPER_H
|
||||
#define MONERO_GUI_PASSPHRASEHELPER_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <wallet/api/wallet2_api.h>
|
||||
#include <QMutex>
|
||||
#include <QPointer>
|
||||
#include <QWaitCondition>
|
||||
#include <QMutex>
|
||||
|
||||
/**
|
||||
* Implements component responsible for showing entry prompt to the user,
|
||||
* typically Wallet / Wallet manager.
|
||||
*/
|
||||
class PassprasePrompter {
|
||||
public:
|
||||
virtual void onWalletPassphraseNeeded(bool onDevice) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements receiver of the passphrase responsible for passing it back to the wallet,
|
||||
* typically wallet listener.
|
||||
*/
|
||||
class PassphraseReceiver {
|
||||
public:
|
||||
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) = 0;
|
||||
};
|
||||
|
||||
class PassphraseHelper {
|
||||
public:
|
||||
PassphraseHelper(PassprasePrompter * prompter=nullptr): m_prompter(prompter) {};
|
||||
PassphraseHelper(const PassphraseHelper & h): PassphraseHelper(h.m_prompter) {};
|
||||
Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device);
|
||||
void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort);
|
||||
|
||||
private:
|
||||
PassprasePrompter * m_prompter;
|
||||
QWaitCondition m_cond_pass;
|
||||
QMutex m_mutex_pass;
|
||||
QString m_passphrase;
|
||||
bool m_passphrase_abort;
|
||||
bool m_passphrase_on_device;
|
||||
|
||||
};
|
||||
|
||||
#endif //MONERO_GUI_PASSPHRASEHELPER_H
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <QDebug>
|
||||
#include <QReadLocker>
|
||||
#include <QWriteLocker>
|
||||
#include <QtGlobal>
|
||||
|
||||
|
||||
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
|
||||
@@ -58,7 +59,11 @@ bool TransactionHistory::transaction(int index, std::function<void (TransactionI
|
||||
|
||||
void TransactionHistory::refresh(quint32 accountIndex)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay();
|
||||
#else
|
||||
QDateTime firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
||||
#endif
|
||||
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
||||
|
||||
emit refreshStarted();
|
||||
@@ -143,7 +148,11 @@ bool TransactionHistory::TransactionHistory::locked() const
|
||||
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent)
|
||||
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false)
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
m_firstDateTime = QDate(2014, 4, 18).startOfDay();
|
||||
#else
|
||||
m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
||||
#endif
|
||||
m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,14 +61,14 @@ quint64 TransactionInfo::atomicAmount() const
|
||||
|
||||
QString TransactionInfo::displayAmount() const
|
||||
{
|
||||
return WalletManager::instance()->displayAmount(m_amount);
|
||||
return WalletManager::displayAmount(m_amount);
|
||||
}
|
||||
|
||||
QString TransactionInfo::fee() const
|
||||
{
|
||||
if(m_fee == 0)
|
||||
return "";
|
||||
return WalletManager::instance()->displayAmount(m_fee);
|
||||
return WalletManager::displayAmount(m_fee);
|
||||
}
|
||||
|
||||
quint64 TransactionInfo::blockHeight() const
|
||||
@@ -132,7 +132,7 @@ QString TransactionInfo::destinations_formatted() const
|
||||
for (auto const& t: m_transfers) {
|
||||
if (!destinations.isEmpty())
|
||||
destinations += "<br> ";
|
||||
destinations += WalletManager::instance()->displayAmount(t->amount()) + ": " + t->address();
|
||||
destinations += WalletManager::displayAmount(t->amount()) + ": " + t->address();
|
||||
}
|
||||
return destinations;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
|
||||
namespace {
|
||||
@@ -59,66 +58,6 @@ namespace {
|
||||
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
|
||||
}
|
||||
|
||||
class WalletListenerImpl : public Monero::WalletListener
|
||||
{
|
||||
public:
|
||||
WalletListenerImpl(Wallet * w)
|
||||
: m_wallet(w)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void moneySpent(const std::string &txId, uint64_t amount) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
|
||||
}
|
||||
|
||||
|
||||
virtual void moneyReceived(const std::string &txId, uint64_t amount) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
|
||||
}
|
||||
|
||||
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
|
||||
}
|
||||
|
||||
virtual void newBlock(uint64_t height) override
|
||||
{
|
||||
// qDebug() << __FUNCTION__;
|
||||
emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
|
||||
}
|
||||
|
||||
virtual void updated() override
|
||||
{
|
||||
emit m_wallet->updated();
|
||||
}
|
||||
|
||||
// called when wallet refreshed by background thread or explicitly
|
||||
virtual void refreshed() override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->refreshed();
|
||||
}
|
||||
|
||||
virtual void onDeviceButtonRequest(uint64_t code) override
|
||||
{
|
||||
emit m_wallet->deviceButtonRequest(code);
|
||||
}
|
||||
|
||||
virtual void onDeviceButtonPressed() override
|
||||
{
|
||||
emit m_wallet->deviceButtonPressed();
|
||||
}
|
||||
|
||||
private:
|
||||
Wallet * m_wallet;
|
||||
};
|
||||
|
||||
Wallet::Wallet(QObject * parent)
|
||||
: Wallet(nullptr, parent)
|
||||
{
|
||||
@@ -227,12 +166,22 @@ QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const
|
||||
|
||||
QString Wallet::path() const
|
||||
{
|
||||
return QString::fromStdString(m_walletImpl->path());
|
||||
return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->path()));
|
||||
}
|
||||
|
||||
bool Wallet::store(const QString &path)
|
||||
void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */)
|
||||
{
|
||||
return m_walletImpl->store(path.toStdString());
|
||||
const auto future = m_scheduler.run(
|
||||
[this, path] {
|
||||
QMutexLocker locker(&m_storeMutex);
|
||||
|
||||
return QJSValueList({m_walletImpl->store(path.toStdString())});
|
||||
},
|
||||
callback);
|
||||
if (!future.first)
|
||||
{
|
||||
QJSValue(callback).call(QJSValueList({false}));
|
||||
}
|
||||
}
|
||||
|
||||
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
|
||||
@@ -290,6 +239,11 @@ bool Wallet::isLedger() const
|
||||
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Ledger;
|
||||
}
|
||||
|
||||
bool Wallet::isTrezor() const
|
||||
{
|
||||
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Trezor;
|
||||
}
|
||||
|
||||
//! create a view only wallet
|
||||
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
||||
{
|
||||
@@ -600,6 +554,19 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
|
||||
delete t;
|
||||
}
|
||||
|
||||
void Wallet::estimateTransactionFeeAsync(const QString &destination,
|
||||
quint64 amount,
|
||||
PendingTransaction::Priority priority,
|
||||
const QJSValue &callback)
|
||||
{
|
||||
m_scheduler.run([this, destination, amount, priority] {
|
||||
const uint64_t fee = m_walletImpl->estimateTransactionFee(
|
||||
{std::make_pair(destination.toStdString(), amount)},
|
||||
static_cast<Monero::PendingTransaction::Priority>(priority));
|
||||
return QJSValueList({QString::fromStdString(Monero::Wallet::displayAmount(fee))});
|
||||
}, callback);
|
||||
}
|
||||
|
||||
TransactionHistory *Wallet::history() const
|
||||
{
|
||||
return m_history;
|
||||
@@ -999,6 +966,19 @@ void Wallet::keyReuseMitigation2(bool mitigation)
|
||||
m_walletImpl->keyReuseMitigation2(mitigation);
|
||||
}
|
||||
|
||||
void Wallet::onWalletPassphraseNeeded(bool on_device)
|
||||
{
|
||||
emit this->walletPassphraseNeeded(on_device);
|
||||
}
|
||||
|
||||
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
if (m_walletListener != nullptr)
|
||||
{
|
||||
m_walletListener->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||
}
|
||||
}
|
||||
|
||||
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_walletImpl(w)
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here;
|
||||
#include "UnsignedTransaction.h"
|
||||
#include "NetworkType.h"
|
||||
#include "PassphraseHelper.h"
|
||||
#include "WalletListenerImpl.h"
|
||||
|
||||
namespace Monero {
|
||||
struct Wallet; // forward declaration
|
||||
@@ -57,7 +59,7 @@ class SubaddressModel;
|
||||
class SubaddressAccount;
|
||||
class SubaddressAccountModel;
|
||||
|
||||
class Wallet : public QObject
|
||||
class Wallet : public QObject, public PassprasePrompter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
|
||||
@@ -144,7 +146,7 @@ public:
|
||||
|
||||
//! saves wallet to the file by given path
|
||||
//! empty path stores in current location
|
||||
Q_INVOKABLE bool store(const QString &path = "");
|
||||
Q_INVOKABLE void storeAsync(const QJSValue &callback, const QString &path = "");
|
||||
|
||||
//! initializes wallet asynchronously
|
||||
Q_INVOKABLE void initAsync(const QString &daemonAddress, bool trustedDaemon = false, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0);
|
||||
@@ -185,6 +187,7 @@ public:
|
||||
//! hw-device backed wallets
|
||||
Q_INVOKABLE bool isHwBacked() const;
|
||||
Q_INVOKABLE bool isLedger() const;
|
||||
Q_INVOKABLE bool isTrezor() const;
|
||||
|
||||
//! returns if view only wallet
|
||||
Q_INVOKABLE bool viewOnly() const;
|
||||
@@ -250,6 +253,11 @@ public:
|
||||
//! deletes unsigned transaction and frees memory
|
||||
Q_INVOKABLE void disposeTransaction(UnsignedTransaction * t);
|
||||
|
||||
Q_INVOKABLE void estimateTransactionFeeAsync(const QString &destination,
|
||||
quint64 amount,
|
||||
PendingTransaction::Priority priority,
|
||||
const QJSValue &callback);
|
||||
|
||||
//! returns transaction history
|
||||
TransactionHistory * history() const;
|
||||
|
||||
@@ -343,6 +351,10 @@ public:
|
||||
Q_INVOKABLE void segregationHeight(quint64 height);
|
||||
Q_INVOKABLE void keyReuseMitigation2(bool mitigation);
|
||||
|
||||
// Passphrase entry for hardware wallets
|
||||
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
||||
virtual void onWalletPassphraseNeeded(bool on_device) override;
|
||||
|
||||
// TODO: setListenter() when it implemented in API
|
||||
signals:
|
||||
// emitted on every event happened with wallet
|
||||
@@ -362,6 +374,7 @@ signals:
|
||||
void walletCreationHeightChanged();
|
||||
void deviceButtonRequest(quint64 buttonCode);
|
||||
void deviceButtonPressed();
|
||||
void walletPassphraseNeeded(bool onDevice);
|
||||
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
||||
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
||||
void deviceShowAddressShowed();
|
||||
@@ -427,8 +440,9 @@ private:
|
||||
bool m_connectionStatusRunning;
|
||||
QString m_daemonUsername;
|
||||
QString m_daemonPassword;
|
||||
Monero::WalletListener *m_walletListener;
|
||||
WalletListenerImpl *m_walletListener;
|
||||
FutureScheduler m_scheduler;
|
||||
QMutex m_storeMutex;
|
||||
};
|
||||
|
||||
|
||||
|
||||
97
src/libwalletqt/WalletListenerImpl.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "WalletListenerImpl.h"
|
||||
#include "Wallet.h"
|
||||
|
||||
WalletListenerImpl::WalletListenerImpl(Wallet * w)
|
||||
: m_wallet(w)
|
||||
, m_phelper(w)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void WalletListenerImpl::moneySpent(const std::string &txId, uint64_t amount)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
|
||||
}
|
||||
|
||||
void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
|
||||
}
|
||||
|
||||
void WalletListenerImpl::unconfirmedMoneyReceived(const std::string &txId, uint64_t amount)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
|
||||
}
|
||||
|
||||
void WalletListenerImpl::newBlock(uint64_t height)
|
||||
{
|
||||
// qDebug() << __FUNCTION__;
|
||||
emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
|
||||
}
|
||||
|
||||
void WalletListenerImpl::updated()
|
||||
{
|
||||
emit m_wallet->updated();
|
||||
}
|
||||
|
||||
// called when wallet refreshed by background thread or explicitly
|
||||
void WalletListenerImpl::refreshed()
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->refreshed();
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->deviceButtonRequest(code);
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onDeviceButtonPressed()
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_wallet->deviceButtonPressed();
|
||||
}
|
||||
|
||||
void WalletListenerImpl::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||
}
|
||||
|
||||
Monero::optional<std::string> WalletListenerImpl::onDevicePassphraseRequest(bool & on_device)
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
return m_phelper.onDevicePassphraseRequest(on_device);
|
||||
}
|
||||
68
src/libwalletqt/WalletListenerImpl.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2014-2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef MONERO_GUI_WALLETLISTENERIMPL_H
|
||||
#define MONERO_GUI_WALLETLISTENERIMPL_H
|
||||
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "PassphraseHelper.h"
|
||||
|
||||
class Wallet;
|
||||
|
||||
class WalletListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
||||
{
|
||||
public:
|
||||
WalletListenerImpl(Wallet * w);
|
||||
|
||||
virtual void moneySpent(const std::string &txId, uint64_t amount) override;
|
||||
|
||||
virtual void moneyReceived(const std::string &txId, uint64_t amount) override;
|
||||
|
||||
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override;
|
||||
|
||||
virtual void newBlock(uint64_t height) override;
|
||||
|
||||
virtual void updated() override;
|
||||
|
||||
// called when wallet refreshed by background thread or explicitly
|
||||
virtual void refreshed() override;
|
||||
|
||||
virtual void onDeviceButtonRequest(uint64_t code) override;
|
||||
|
||||
virtual void onDeviceButtonPressed() override;
|
||||
|
||||
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override;
|
||||
|
||||
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override;
|
||||
|
||||
private:
|
||||
Wallet * m_wallet;
|
||||
PassphraseHelper m_phelper;
|
||||
};
|
||||
|
||||
#endif //MONERO_GUI_WALLETLISTENERIMPL_H
|
||||
@@ -41,10 +41,13 @@
|
||||
#include <QMutexLocker>
|
||||
#include <QString>
|
||||
|
||||
class WalletPassphraseListenerImpl : public Monero::WalletListener
|
||||
#include "qt/updater.h"
|
||||
#include "qt/ScopeGuard.h"
|
||||
|
||||
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
||||
{
|
||||
public:
|
||||
WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_wallet(nullptr) {}
|
||||
WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_phelper(mgr) {}
|
||||
|
||||
virtual void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
||||
virtual void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
||||
@@ -53,56 +56,35 @@ public:
|
||||
virtual void updated() override {};
|
||||
virtual void refreshed() override {};
|
||||
|
||||
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool on_device) override
|
||||
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
if (on_device) return Monero::optional<std::string>();
|
||||
m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||
}
|
||||
|
||||
m_mgr->onWalletPassphraseNeeded(m_wallet);
|
||||
|
||||
if (m_mgr->m_passphrase_abort)
|
||||
{
|
||||
throw std::runtime_error("Passphrase entry abort");
|
||||
}
|
||||
|
||||
auto tmpPass = m_mgr->m_passphrase.toStdString();
|
||||
m_mgr->m_passphrase = QString();
|
||||
|
||||
return Monero::optional<std::string>(tmpPass);
|
||||
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
return m_phelper.onDevicePassphraseRequest(on_device);
|
||||
}
|
||||
|
||||
virtual void onDeviceButtonRequest(uint64_t code) override
|
||||
{
|
||||
emit m_mgr->deviceButtonRequest(code);
|
||||
qDebug() << __FUNCTION__;
|
||||
emit m_mgr->deviceButtonRequest(code);
|
||||
}
|
||||
|
||||
virtual void onDeviceButtonPressed() override
|
||||
{
|
||||
emit m_mgr->deviceButtonPressed();
|
||||
}
|
||||
|
||||
virtual void onSetWallet(Monero::Wallet * wallet) override
|
||||
{
|
||||
qDebug() << __FUNCTION__;
|
||||
m_wallet = wallet;
|
||||
emit m_mgr->deviceButtonPressed();
|
||||
}
|
||||
|
||||
private:
|
||||
WalletManager * m_mgr;
|
||||
Monero::Wallet * m_wallet;
|
||||
PassphraseHelper m_phelper;
|
||||
};
|
||||
|
||||
WalletManager * WalletManager::m_instance = nullptr;
|
||||
|
||||
WalletManager *WalletManager::instance()
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new WalletManager;
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
Wallet *WalletManager::createWallet(const QString &path, const QString &password,
|
||||
const QString &language, NetworkType::Type nettype, quint64 kdfRounds)
|
||||
{
|
||||
@@ -121,6 +103,13 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
WalletPassphraseListenerImpl tmpListener(this);
|
||||
m_mutex_passphraseReceiver.lock();
|
||||
m_passphraseReceiver = &tmpListener;
|
||||
m_mutex_passphraseReceiver.unlock();
|
||||
const auto cleanup = sg::make_scope_guard([this]() noexcept {
|
||||
QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
|
||||
this->m_passphraseReceiver = nullptr;
|
||||
});
|
||||
|
||||
if (m_currentWallet) {
|
||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||
@@ -151,14 +140,14 @@ void WalletManager::openWalletAsync(const QString &path, const QString &password
|
||||
}
|
||||
|
||||
|
||||
Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
|
||||
Wallet *WalletManager::recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (m_currentWallet) {
|
||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||
delete m_currentWallet;
|
||||
}
|
||||
Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", memo.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds);
|
||||
Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", seed.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds, seed_offset.toStdString());
|
||||
m_currentWallet = new Wallet(w);
|
||||
return m_currentWallet;
|
||||
}
|
||||
@@ -184,6 +173,13 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
WalletPassphraseListenerImpl tmpListener(this);
|
||||
m_mutex_passphraseReceiver.lock();
|
||||
m_passphraseReceiver = &tmpListener;
|
||||
m_mutex_passphraseReceiver.unlock();
|
||||
const auto cleanup = sg::make_scope_guard([this]() noexcept {
|
||||
QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
|
||||
this->m_passphraseReceiver = nullptr;
|
||||
});
|
||||
|
||||
if (m_currentWallet) {
|
||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||
@@ -265,7 +261,7 @@ QString WalletManager::maximumAllowedAmountAsString() const
|
||||
return WalletManager::displayAmount(WalletManager::maximumAllowedAmount());
|
||||
}
|
||||
|
||||
QString WalletManager::displayAmount(quint64 amount) const
|
||||
QString WalletManager::displayAmount(quint64 amount)
|
||||
{
|
||||
return QString::fromStdString(Monero::Wallet::displayAmount(amount));
|
||||
}
|
||||
@@ -408,13 +404,27 @@ QVariantMap WalletManager::parse_uri_to_object(const QString &uri) const
|
||||
if (this->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error)) {
|
||||
result.insert("address", address);
|
||||
result.insert("payment_id", payment_id);
|
||||
result.insert("amount", amount > 0 ? this->displayAmount(amount) : "");
|
||||
result.insert("amount", amount > 0 ? displayAmount(amount) : "");
|
||||
result.insert("tx_description", tx_description);
|
||||
result.insert("recipient_name", recipient_name);
|
||||
|
||||
QVariantMap extra_parameters;
|
||||
if (unknown_parameters.size() > 0)
|
||||
{
|
||||
for (const QString &item : unknown_parameters)
|
||||
{
|
||||
const auto parsed_item = item.splitRef("=");
|
||||
if (parsed_item.size() == 2)
|
||||
{
|
||||
extra_parameters.insert(parsed_item[0].toString(), parsed_item[1].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
result.insert("extra_parameters", extra_parameters);
|
||||
} else {
|
||||
result.insert("error", error);
|
||||
result.insert("error", !error.isEmpty() ? error : tr("Unknown error"));
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -461,15 +471,43 @@ bool WalletManager::saveQrCode(const QString &code, const QString &path) const
|
||||
return QRCodeImageProvider::genQrImage(code, &size).scaled(size.expandedTo(QSize(240, 240)), Qt::KeepAspectRatio).save(path, "PNG", 100);
|
||||
}
|
||||
|
||||
void WalletManager::checkUpdatesAsync(const QString &software, const QString &subdir)
|
||||
void WalletManager::checkUpdatesAsync(
|
||||
const QString &software,
|
||||
const QString &subdir,
|
||||
const QString &buildTag,
|
||||
const QString &version)
|
||||
{
|
||||
m_scheduler.run([this, software, subdir] {
|
||||
emit checkUpdatesComplete(checkUpdates(software, subdir));
|
||||
m_scheduler.run([this, software, subdir, buildTag, version] {
|
||||
const auto updateInfo = Monero::WalletManager::checkUpdates(
|
||||
software.toStdString(),
|
||||
subdir.toStdString(),
|
||||
buildTag.toStdString().c_str(),
|
||||
version.toStdString().c_str());
|
||||
if (!std::get<0>(updateInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QString version = QString::fromStdString(std::get<1>(updateInfo));
|
||||
const QByteArray hashFromDns = QByteArray::fromHex(QString::fromStdString(std::get<2>(updateInfo)).toUtf8());
|
||||
const QString downloadUrl = QString::fromStdString(std::get<4>(updateInfo));
|
||||
|
||||
try
|
||||
{
|
||||
const QString binaryFilename = QUrl(downloadUrl).fileName();
|
||||
QPair<QString, QString> signers;
|
||||
const QString signedHash = Updater().fetchSignedHash(binaryFilename, hashFromDns, signers).toHex();
|
||||
|
||||
qInfo() << "Update found" << version << downloadUrl << "hash" << signedHash << "signed by" << signers;
|
||||
emit checkUpdatesComplete(version, downloadUrl, signedHash, signers.first, signers.second);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
qCritical() << "Failed to fetch and verify signed hash:" << e.what();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString WalletManager::checkUpdates(const QString &software, const QString &subdir) const
|
||||
{
|
||||
qDebug() << "Checking for updates";
|
||||
@@ -499,6 +537,7 @@ bool WalletManager::clearWalletCache(const QString &wallet_path) const
|
||||
|
||||
WalletManager::WalletManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_passphraseReceiver(nullptr)
|
||||
, m_scheduler(this)
|
||||
{
|
||||
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
|
||||
@@ -509,22 +548,16 @@ WalletManager::~WalletManager()
|
||||
m_scheduler.shutdownWaitForFinished();
|
||||
}
|
||||
|
||||
void WalletManager::onWalletPassphraseNeeded(Monero::Wallet *)
|
||||
void WalletManager::onWalletPassphraseNeeded(bool on_device)
|
||||
{
|
||||
m_mutex_pass.lock();
|
||||
m_passphrase_abort = false;
|
||||
emit this->walletPassphraseNeeded();
|
||||
|
||||
m_cond_pass.wait(&m_mutex_pass);
|
||||
m_mutex_pass.unlock();
|
||||
emit this->walletPassphraseNeeded(on_device);
|
||||
}
|
||||
|
||||
void WalletManager::onPassphraseEntered(const QString &passphrase, bool entry_abort)
|
||||
void WalletManager::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||
{
|
||||
m_mutex_pass.lock();
|
||||
m_passphrase = passphrase;
|
||||
m_passphrase_abort = entry_abort;
|
||||
|
||||
m_cond_pass.wakeAll();
|
||||
m_mutex_pass.unlock();
|
||||
QMutexLocker locker(&m_mutex_passphraseReceiver);
|
||||
if (m_passphraseReceiver != nullptr)
|
||||
{
|
||||
m_passphraseReceiver->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,21 +36,24 @@
|
||||
#include <QMutex>
|
||||
#include <QPointer>
|
||||
#include <QWaitCondition>
|
||||
#include <QMutex>
|
||||
#include "qt/FutureScheduler.h"
|
||||
#include "NetworkType.h"
|
||||
#include "PassphraseHelper.h"
|
||||
|
||||
class Wallet;
|
||||
namespace Monero {
|
||||
struct WalletManager;
|
||||
}
|
||||
|
||||
class WalletManager : public QObject
|
||||
class WalletManager : public QObject, public PassprasePrompter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool connected READ connected)
|
||||
|
||||
public:
|
||||
explicit WalletManager(QObject *parent = 0);
|
||||
~WalletManager();
|
||||
|
||||
enum LogLevel {
|
||||
LogLevel_Silent = Monero::WalletManagerFactory::LogLevel_Silent,
|
||||
LogLevel_0 = Monero::WalletManagerFactory::LogLevel_0,
|
||||
@@ -62,7 +65,6 @@ public:
|
||||
LogLevel_Max = Monero::WalletManagerFactory::LogLevel_Max,
|
||||
};
|
||||
|
||||
static WalletManager * instance();
|
||||
// wizard: createWallet path;
|
||||
Q_INVOKABLE Wallet * createWallet(const QString &path, const QString &password,
|
||||
const QString &language, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
|
||||
@@ -83,7 +85,7 @@ public:
|
||||
Q_INVOKABLE void openWalletAsync(const QString &path, const QString &password, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
|
||||
|
||||
// wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = ""
|
||||
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &memo,
|
||||
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset,
|
||||
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
|
||||
|
||||
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
|
||||
@@ -129,7 +131,7 @@ public:
|
||||
Q_INVOKABLE QString errorString() const;
|
||||
|
||||
//! since we can't call static method from QML, move it to this class
|
||||
Q_INVOKABLE QString displayAmount(quint64 amount) const;
|
||||
Q_INVOKABLE static QString displayAmount(quint64 amount);
|
||||
Q_INVOKABLE quint64 amountFromString(const QString &amount) const;
|
||||
Q_INVOKABLE quint64 amountFromDouble(double amount) const;
|
||||
Q_INVOKABLE quint64 maximumAllowedAmount() const;
|
||||
@@ -176,44 +178,46 @@ public:
|
||||
Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error) const;
|
||||
Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const;
|
||||
Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const;
|
||||
Q_INVOKABLE void checkUpdatesAsync(const QString &software, const QString &subdir);
|
||||
Q_INVOKABLE void checkUpdatesAsync(
|
||||
const QString &software,
|
||||
const QString &subdir,
|
||||
const QString &buildTag,
|
||||
const QString &version);
|
||||
Q_INVOKABLE QString checkUpdates(const QString &software, const QString &subdir) const;
|
||||
|
||||
// clear/rename wallet cache
|
||||
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
|
||||
|
||||
Q_INVOKABLE void onWalletPassphraseNeeded(Monero::Wallet * wallet);
|
||||
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool entry_abort=false);
|
||||
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
||||
virtual void onWalletPassphraseNeeded(bool on_device) override;
|
||||
|
||||
signals:
|
||||
|
||||
void walletOpened(Wallet * wallet);
|
||||
void walletCreated(Wallet * wallet);
|
||||
void walletPassphraseNeeded();
|
||||
void walletPassphraseNeeded(bool onDevice);
|
||||
void deviceButtonRequest(quint64 buttonCode);
|
||||
void deviceButtonPressed();
|
||||
void checkUpdatesComplete(const QString &result) const;
|
||||
void checkUpdatesComplete(
|
||||
const QString &version,
|
||||
const QString &downloadUrl,
|
||||
const QString &hash,
|
||||
const QString &firstSigner,
|
||||
const QString &secondSigner) const;
|
||||
void miningStatus(bool isMining) const;
|
||||
|
||||
public slots:
|
||||
private:
|
||||
friend class WalletPassphraseListenerImpl;
|
||||
|
||||
explicit WalletManager(QObject *parent = 0);
|
||||
~WalletManager();
|
||||
|
||||
bool isMining() const;
|
||||
|
||||
static WalletManager * m_instance;
|
||||
Monero::WalletManager * m_pimpl;
|
||||
mutable QMutex m_mutex;
|
||||
QPointer<Wallet> m_currentWallet;
|
||||
|
||||
QWaitCondition m_cond_pass;
|
||||
QMutex m_mutex_pass;
|
||||
QString m_passphrase;
|
||||
bool m_passphrase_abort;
|
||||
|
||||
PassphraseReceiver * m_passphraseReceiver;
|
||||
QMutex m_mutex_passphraseReceiver;
|
||||
FutureScheduler m_scheduler;
|
||||
};
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
#include <QScreen>
|
||||
#include <QRegExp>
|
||||
#include <QThread>
|
||||
|
||||
#include <version.h>
|
||||
|
||||
#include "clipboardAdapter.h"
|
||||
#include "filter.h"
|
||||
#include "oscursor.h"
|
||||
@@ -61,18 +64,25 @@
|
||||
#include "wallet/api/wallet2_api.h"
|
||||
#include "Logger.h"
|
||||
#include "MainApp.h"
|
||||
#include "qt/downloader.h"
|
||||
#include "qt/ipc.h"
|
||||
#include "qt/network.h"
|
||||
#include "qt/updater.h"
|
||||
#include "qt/utils.h"
|
||||
#include "qt/TailsOS.h"
|
||||
#include "qt/KeysFiles.h"
|
||||
#include "qt/MoneroSettings.h"
|
||||
#include "qt/prices.h"
|
||||
#include "qt/NetworkAccessBlockingFactory.h"
|
||||
|
||||
// IOS exclusions
|
||||
#ifndef Q_OS_IOS
|
||||
#include "daemon/DaemonManager.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <QOpenGLContext>
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SCANNER
|
||||
#include "QR-Code-scanner/QrCodeScanner.h"
|
||||
#endif
|
||||
@@ -86,13 +96,12 @@
|
||||
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
|
||||
#elif defined(Q_OS_LINUX)
|
||||
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
|
||||
Q_IMPORT_PLUGIN(QXcbGlxIntegrationPlugin);
|
||||
#endif
|
||||
Q_IMPORT_PLUGIN(QSvgIconPlugin)
|
||||
Q_IMPORT_PLUGIN(QGifPlugin)
|
||||
Q_IMPORT_PLUGIN(QICNSPlugin)
|
||||
Q_IMPORT_PLUGIN(QICOPlugin)
|
||||
Q_IMPORT_PLUGIN(QJpegPlugin)
|
||||
Q_IMPORT_PLUGIN(QMngPlugin)
|
||||
Q_IMPORT_PLUGIN(QSvgPlugin)
|
||||
Q_IMPORT_PLUGIN(QTgaPlugin)
|
||||
Q_IMPORT_PLUGIN(QTiffPlugin)
|
||||
@@ -124,7 +133,9 @@ Q_IMPORT_PLUGIN(QtQuick2PrivateWidgetsPlugin)
|
||||
Q_IMPORT_PLUGIN(QtQuickControls2Plugin)
|
||||
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin)
|
||||
Q_IMPORT_PLUGIN(QmlXmlListModelPlugin)
|
||||
#ifdef WITH_SCANNER
|
||||
Q_IMPORT_PLUGIN(QMultimediaDeclarativeModule)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -139,6 +150,8 @@ bool isOpenGL = true;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE(translations);
|
||||
|
||||
// platform dependant settings
|
||||
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
|
||||
bool isDesktop = true;
|
||||
@@ -177,6 +190,17 @@ int main(int argc, char *argv[])
|
||||
|
||||
MainApp app(argc, argv);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
if (isOpenGL)
|
||||
{
|
||||
QOpenGLContext ctx;
|
||||
isOpenGL = ctx.create() && ctx.format().version() >= qMakePair(2, 1);
|
||||
if (!isOpenGL) {
|
||||
qputenv("QMLSCENE_DEVICE", "softwarecontext");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
app.setApplicationName("monero-core");
|
||||
app.setOrganizationDomain("getmonero.org");
|
||||
app.setOrganizationName("monero-project");
|
||||
@@ -208,6 +232,7 @@ int main(int argc, char *argv[])
|
||||
qCritical() << "Error: accounts root directory could not be set";
|
||||
return 1;
|
||||
}
|
||||
moneroAccountsDir = QDir::toNativeSeparators(moneroAccountsDir);
|
||||
|
||||
#if defined(Q_OS_LINUX)
|
||||
if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico"));
|
||||
@@ -221,6 +246,16 @@ int main(int argc, char *argv[])
|
||||
QCoreApplication::translate("main", "Log to specified file"),
|
||||
QCoreApplication::translate("main", "file"));
|
||||
|
||||
QCommandLineOption verifyUpdateOption("verify-update", "\
|
||||
Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by two maintainers.\n\
|
||||
* Requires 'hashes.txt' - signed 'shasum' output \
|
||||
(i.e. 'gpg -o hashes.txt --clear-sign <shasum_output>') generated by a maintainer.\n\
|
||||
* Requires 'hashes.txt.sig' - detached signature of 'hashes.txt' \
|
||||
(i.e. 'gpg -b hashes.txt') generated by another maintainer.", "update-binary");
|
||||
parser.addOption(verifyUpdateOption);
|
||||
|
||||
QCommandLineOption disableCheckUpdatesOption("disable-check-updates", "Disable automatic check for updates.");
|
||||
parser.addOption(disableCheckUpdatesOption);
|
||||
QCommandLineOption testQmlOption("test-qml");
|
||||
testQmlOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||
parser.addOption(logPathOption);
|
||||
@@ -231,7 +266,7 @@ int main(int argc, char *argv[])
|
||||
Monero::Utils::onStartup();
|
||||
|
||||
// Log settings
|
||||
const QString logPath = getLogPath(parser.value(logPathOption));
|
||||
const QString logPath = QDir::toNativeSeparators(getLogPath(parser.value(logPathOption)));
|
||||
Monero::Wallet::init(argv[0], "monero-wallet-gui", logPath.toStdString().c_str(), true);
|
||||
qInstallMessageHandler(messageHandler);
|
||||
|
||||
@@ -244,6 +279,32 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
qWarning().noquote() << "app startd" << "(log: " + logPath + ")";
|
||||
|
||||
if (parser.isSet(verifyUpdateOption))
|
||||
{
|
||||
const QString updateBinaryFullPath = parser.value(verifyUpdateOption);
|
||||
const QFileInfo updateBinaryInfo(updateBinaryFullPath);
|
||||
const QString updateBinaryDir = QDir::toNativeSeparators(updateBinaryInfo.absolutePath()) + QDir::separator();
|
||||
const QString hashesTxt = updateBinaryDir + "hashes.txt";
|
||||
const QString hashesTxtSig = hashesTxt + ".sig";
|
||||
try
|
||||
{
|
||||
const QByteArray updateBinaryContents = fileGetContents(updateBinaryFullPath);
|
||||
const QPair<QString, QString> signers = Updater().verifySignaturesAndHashSum(
|
||||
fileGetContents(hashesTxt),
|
||||
fileGetContents(hashesTxtSig),
|
||||
updateBinaryInfo.fileName(),
|
||||
updateBinaryContents.data(),
|
||||
updateBinaryContents.size());
|
||||
qCritical() << "successfully verified, signed by" << signers.first << "and" << signers.second;
|
||||
return 0;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
qCritical() << e.what();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Desktop entry
|
||||
#ifdef Q_OS_LINUX
|
||||
registerXdgMime(app);
|
||||
@@ -295,6 +356,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
// registering types for QML
|
||||
qmlRegisterType<clipboardAdapter>("moneroComponents.Clipboard", 1, 0, "Clipboard");
|
||||
qmlRegisterType<Downloader>("moneroComponents.Downloader", 1, 0, "Downloader");
|
||||
qmlRegisterType<WalletKeysFilesModel>("moneroComponents.WalletKeysFilesModel", 1, 0, "WalletKeysFilesModel");
|
||||
qmlRegisterType<WalletManager>("moneroComponents.WalletManager", 1, 0, "WalletManager");
|
||||
|
||||
// Temporary Qt.labs.settings replacement
|
||||
qmlRegisterType<MoneroSettings>("moneroComponents.Settings", 1, 0, "MoneroSettings");
|
||||
@@ -308,15 +372,9 @@ int main(int argc, char *argv[])
|
||||
qmlRegisterUncreatableType<UnsignedTransaction>("moneroComponents.UnsignedTransaction", 1, 0, "UnsignedTransaction",
|
||||
"UnsignedTransaction can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<WalletManager>("moneroComponents.WalletManager", 1, 0, "WalletManager",
|
||||
"WalletManager can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<TranslationManager>("moneroComponents.TranslationManager", 1, 0, "TranslationManager",
|
||||
"TranslationManager can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<WalletKeysFilesModel>("moneroComponents.walletKeysFilesModel", 1, 0, "WalletKeysFilesModel",
|
||||
"walletKeysFilesModel can't be instantiated directly");
|
||||
|
||||
qmlRegisterUncreatableType<TransactionHistoryModel>("moneroComponents.TransactionHistoryModel", 1, 0, "TransactionHistoryModel",
|
||||
"TransactionHistoryModel can't be instantiated directly");
|
||||
|
||||
@@ -363,6 +421,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||
engine.setNetworkAccessManagerFactory(new NetworkAccessBlockingFactory);
|
||||
#endif
|
||||
OSCursor cursor;
|
||||
engine.rootContext()->setContextProperty("globalCursor", &cursor);
|
||||
OSHelper osHelper;
|
||||
@@ -372,10 +433,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
engine.rootContext()->setContextProperty("moneroAccountsDir", moneroAccountsDir);
|
||||
|
||||
WalletManager *walletManager = WalletManager::instance();
|
||||
|
||||
engine.rootContext()->setContextProperty("walletManager", walletManager);
|
||||
|
||||
engine.rootContext()->setContextProperty("translationManager", TranslationManager::instance());
|
||||
|
||||
engine.addImageProvider(QLatin1String("qrcode"), new QRCodeImageProvider());
|
||||
@@ -413,11 +470,6 @@ int main(int argc, char *argv[])
|
||||
engine.rootContext()->setContextProperty("desktopFolder", desktopFolder);
|
||||
#endif
|
||||
|
||||
// Wallet .keys files model (wizard -> open wallet)
|
||||
WalletKeysFilesModel walletKeysFilesModel(walletManager);
|
||||
engine.rootContext()->setContextProperty("walletKeysFilesModel", &walletKeysFilesModel);
|
||||
engine.rootContext()->setContextProperty("walletKeysFilesModelProxy", &walletKeysFilesModel.proxyModel());
|
||||
|
||||
// Get default account name
|
||||
QString accountName = qgetenv("USER"); // mac/linux
|
||||
if (accountName.isEmpty())
|
||||
@@ -429,6 +481,7 @@ int main(int argc, char *argv[])
|
||||
engine.rootContext()->setContextProperty("homePath", QDir::homePath());
|
||||
engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
|
||||
engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount());
|
||||
engine.rootContext()->setContextProperty("disableCheckUpdatesFlag", parser.isSet(disableCheckUpdatesOption));
|
||||
|
||||
bool builtWithScanner = false;
|
||||
#ifdef WITH_SCANNER
|
||||
@@ -436,8 +489,10 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner);
|
||||
|
||||
Prices prices;
|
||||
engine.rootContext()->setContextProperty("Prices", &prices);
|
||||
engine.rootContext()->setContextProperty("moneroVersion", MONERO_VERSION_FULL);
|
||||
|
||||
Network network;
|
||||
engine.rootContext()->setContextProperty("Network", &network);
|
||||
|
||||
// Load main window (context properties needs to be defined obove this line)
|
||||
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "oshelper.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryFile>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
@@ -82,6 +85,11 @@ OSHelper::OSHelper(QObject *parent) : QObject(parent)
|
||||
|
||||
}
|
||||
|
||||
QString OSHelper::downloadLocation() const
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
}
|
||||
|
||||
bool OSHelper::openContainingFolder(const QString &filePath) const
|
||||
{
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -105,6 +113,12 @@ bool OSHelper::openContainingFolder(const QString &filePath) const
|
||||
return QDesktopServices::openUrl(url);
|
||||
}
|
||||
|
||||
QString OSHelper::openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const
|
||||
{
|
||||
const QString hint = (folder.isEmpty() ? "" : folder + QDir::separator()) + filename;
|
||||
return QFileDialog::getSaveFileName(nullptr, title, hint);
|
||||
}
|
||||
|
||||
QString OSHelper::temporaryFilename() const
|
||||
{
|
||||
QString tempFileName;
|
||||
@@ -151,3 +165,47 @@ QString OSHelper::temporaryPath() const
|
||||
{
|
||||
return QDir::tempPath();
|
||||
}
|
||||
|
||||
bool OSHelper::installed() const
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
static constexpr const wchar_t installKey[] =
|
||||
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Monero GUI Wallet_is1";
|
||||
static constexpr const wchar_t installValue[] = L"InstallLocation";
|
||||
|
||||
DWORD size;
|
||||
LSTATUS status =
|
||||
::RegGetValueW(HKEY_LOCAL_MACHINE, installKey, installValue, RRF_RT_REG_SZ, nullptr, nullptr, &size);
|
||||
if (status == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
qCritical() << "RegGetValueW failed (get size)" << status;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring installLocation;
|
||||
installLocation.resize(size / sizeof(std::wstring::value_type));
|
||||
size = installLocation.size() * sizeof(std::wstring::value_type);
|
||||
status = ::RegGetValueW(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
installKey,
|
||||
installValue,
|
||||
RRF_RT_REG_SZ,
|
||||
nullptr,
|
||||
&installLocation[0],
|
||||
&size);
|
||||
if (status != ERROR_SUCCESS)
|
||||
{
|
||||
qCritical() << "RegGetValueW Failed (read)" << status;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QDir installDir(QString(reinterpret_cast<const QChar *>(&installLocation[0])));
|
||||
return installDir == QDir(QCoreApplication::applicationDirPath());
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -36,15 +36,22 @@
|
||||
class OSHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool installed READ installed CONSTANT);
|
||||
|
||||
public:
|
||||
explicit OSHelper(QObject *parent = 0);
|
||||
|
||||
Q_INVOKABLE QString downloadLocation() const;
|
||||
Q_INVOKABLE bool openContainingFolder(const QString &filePath) const;
|
||||
Q_INVOKABLE QString openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const;
|
||||
Q_INVOKABLE QString temporaryFilename() const;
|
||||
Q_INVOKABLE QString temporaryPath() const;
|
||||
Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const;
|
||||
Q_INVOKABLE bool isCapsLock() const;
|
||||
|
||||
private:
|
||||
bool installed() const;
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "TransactionHistoryModel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace {
|
||||
/**
|
||||
@@ -204,9 +205,14 @@ bool TransactionHistorySortFilterModel::filterAcceptsRow(int source_row, const Q
|
||||
break;
|
||||
case TransactionHistoryModel::TransactionTimeStampRole:
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
QDateTime from = dateFromFilter().startOfDay();
|
||||
QDateTime to = dateToFilter().endOfDay();
|
||||
#else
|
||||
QDateTime from = QDateTime(dateFromFilter());
|
||||
QDateTime to = QDateTime(dateToFilter());
|
||||
to = to.addDays(1); // including upperbound
|
||||
#endif
|
||||
QDateTime timestamp = data.toDateTime();
|
||||
bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from;
|
||||
bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to;
|
||||
|
||||
18
src/openpgp/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
file(GLOB_RECURSE SOURCES *.cpp)
|
||||
file(GLOB_RECURSE HEADERS *.h)
|
||||
|
||||
find_library(GCRYPT_LIBRARY gcrypt)
|
||||
find_library(GPG_ERROR_LIBRARY gpg-error)
|
||||
|
||||
add_library(openpgp
|
||||
${SOURCES}
|
||||
${HEADERS})
|
||||
|
||||
target_include_directories(openpgp
|
||||
PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include)
|
||||
|
||||
target_link_libraries(openpgp
|
||||
PUBLIC
|
||||
${GCRYPT_LIBRARY}
|
||||
${GPG_ERROR_LIBRARY})
|
||||
107
src/openpgp/hash.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gcrypt.h>
|
||||
#include <span.h>
|
||||
|
||||
namespace openpgp
|
||||
{
|
||||
|
||||
class hash
|
||||
{
|
||||
public:
|
||||
enum algorithm : uint8_t
|
||||
{
|
||||
sha256 = 8,
|
||||
};
|
||||
|
||||
hash(const hash &) = delete;
|
||||
hash &operator=(const hash &) = delete;
|
||||
|
||||
hash(uint8_t algorithm)
|
||||
: algo(algorithm)
|
||||
, consumed(0)
|
||||
{
|
||||
if (gcry_md_open(&md, algo, 0) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("failed to create message digest object");
|
||||
}
|
||||
}
|
||||
|
||||
~hash()
|
||||
{
|
||||
gcry_md_close(md);
|
||||
}
|
||||
|
||||
hash &operator<<(uint8_t byte)
|
||||
{
|
||||
gcry_md_putc(md, byte);
|
||||
++consumed;
|
||||
return *this;
|
||||
}
|
||||
|
||||
hash &operator<<(const epee::span<const uint8_t> &bytes)
|
||||
{
|
||||
gcry_md_write(md, &bytes[0], bytes.size());
|
||||
consumed += bytes.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
hash &operator<<(const std::vector<uint8_t> &bytes)
|
||||
{
|
||||
return *this << epee::to_span(bytes);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> finish() const
|
||||
{
|
||||
std::vector<uint8_t> result(gcry_md_get_algo_dlen(algo));
|
||||
const void *digest = gcry_md_read(md, algo);
|
||||
if (digest == nullptr)
|
||||
{
|
||||
throw std::runtime_error("failed to read the digest");
|
||||
}
|
||||
memcpy(&result[0], digest, result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t consumed_bytes() const
|
||||
{
|
||||
return consumed;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t algo;
|
||||
gcry_md_hd_t md;
|
||||
size_t consumed;
|
||||
};
|
||||
|
||||
}
|
||||
78
src/openpgp/mpi.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
namespace openpgp
|
||||
{
|
||||
|
||||
class mpi
|
||||
{
|
||||
public:
|
||||
mpi(const mpi &) = delete;
|
||||
mpi &operator=(const mpi &) = delete;
|
||||
|
||||
mpi(mpi &&other)
|
||||
: data(other.data)
|
||||
{
|
||||
other.data = nullptr;
|
||||
}
|
||||
|
||||
template <
|
||||
typename byte_container,
|
||||
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
||||
mpi(const byte_container &buffer, gcry_mpi_format format = GCRYMPI_FMT_USG)
|
||||
: mpi(&buffer[0], buffer.size(), format)
|
||||
{
|
||||
}
|
||||
|
||||
mpi(const void *buffer, size_t size, gcry_mpi_format format = GCRYMPI_FMT_USG)
|
||||
{
|
||||
if (gcry_mpi_scan(&data, format, buffer, size, nullptr) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("failed to read mpi from buffer");
|
||||
}
|
||||
}
|
||||
|
||||
~mpi()
|
||||
{
|
||||
gcry_mpi_release(data);
|
||||
}
|
||||
|
||||
const gcry_mpi_t &get() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
gcry_mpi_t data;
|
||||
};
|
||||
|
||||
} // namespace openpgp
|
||||
382
src/openpgp/openpgp.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "openpgp.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <vector>
|
||||
|
||||
#include <string_coding.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "mpi.h"
|
||||
#include "packet_stream.h"
|
||||
#include "s_expression.h"
|
||||
#include "serialization.h"
|
||||
|
||||
namespace openpgp
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string::const_iterator find_next_line(std::string::const_iterator begin, const std::string::const_iterator &end)
|
||||
{
|
||||
begin = std::find(begin, end, '\n');
|
||||
return begin != end ? ++begin : end;
|
||||
}
|
||||
|
||||
std::string::const_iterator find_line_starting_with(
|
||||
std::string::const_iterator it,
|
||||
const std::string::const_iterator &end,
|
||||
const std::string &starts_with)
|
||||
{
|
||||
for (std::string::const_iterator next_line; it != end; it = next_line)
|
||||
{
|
||||
next_line = find_next_line(it, end);
|
||||
const size_t line_length = static_cast<size_t>(std::distance(it, next_line));
|
||||
if (line_length >= starts_with.size() && std::equal(starts_with.begin(), starts_with.end(), it))
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
std::string::const_iterator find_empty_line(std::string::const_iterator it, const std::string::const_iterator &end)
|
||||
{
|
||||
for (; it != end && *it != '\r' && *it != '\n'; it = find_next_line(it, end))
|
||||
{
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
std::string get_armored_block_contents(const std::string &text, const std::string &block_name)
|
||||
{
|
||||
static constexpr const char dashes[] = "-----";
|
||||
const std::string armor_header = dashes + block_name + dashes;
|
||||
auto block_start = find_line_starting_with(text.begin(), text.end(), armor_header);
|
||||
auto block_headers = find_next_line(block_start, text.end());
|
||||
auto block_end = find_line_starting_with(block_headers, text.end(), dashes);
|
||||
auto contents_begin = find_next_line(find_empty_line(block_headers, block_end), block_end);
|
||||
if (contents_begin == block_end)
|
||||
{
|
||||
throw std::runtime_error("armored block not found");
|
||||
}
|
||||
return std::string(contents_begin, block_end);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
public_key_rsa::public_key_rsa(s_expression expression, size_t bits)
|
||||
: m_expression(std::move(expression))
|
||||
, m_bits(bits)
|
||||
{
|
||||
}
|
||||
|
||||
const gcry_sexp_t &public_key_rsa::get() const
|
||||
{
|
||||
return m_expression.get();
|
||||
}
|
||||
|
||||
size_t public_key_rsa::bits() const
|
||||
{
|
||||
return m_bits;
|
||||
}
|
||||
|
||||
public_key_block::public_key_block(const std::string &armored)
|
||||
: public_key_block(epee::to_byte_span(epee::to_span(epee::string_encoding::base64_decode(
|
||||
strip_line_breaks(get_armored_block_contents(armored, "BEGIN PGP PUBLIC KEY BLOCK"))))))
|
||||
{
|
||||
}
|
||||
|
||||
// TODO: Public-Key expiration, User ID and Public-Key certification, Subkey binding checks
|
||||
public_key_block::public_key_block(const epee::span<const uint8_t> buffer)
|
||||
{
|
||||
packet_stream packets(buffer);
|
||||
|
||||
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::user_id);
|
||||
if (data == nullptr)
|
||||
{
|
||||
throw std::runtime_error("user id is missing");
|
||||
}
|
||||
m_user_id.assign(data->begin(), data->end());
|
||||
|
||||
const auto append_public_key = [this](const std::vector<uint8_t> &data) {
|
||||
deserializer<std::vector<uint8_t>> serialized(data);
|
||||
|
||||
const auto version = serialized.read_big_endian<uint8_t>();
|
||||
if (version != 4)
|
||||
{
|
||||
throw std::runtime_error("unsupported public key version");
|
||||
}
|
||||
|
||||
/* const auto timestamp = */ serialized.read_big_endian<uint32_t>();
|
||||
|
||||
const auto algorithm = serialized.read_big_endian<uint8_t>();
|
||||
if (algorithm != openpgp::algorithm::rsa)
|
||||
{
|
||||
throw std::runtime_error("unsupported public key algorithm");
|
||||
}
|
||||
|
||||
{
|
||||
const mpi public_key_n = serialized.read_mpi();
|
||||
const mpi public_key_e = serialized.read_mpi();
|
||||
|
||||
emplace_back(
|
||||
s_expression("(public-key (rsa (n %m) (e %m)))", public_key_n.get(), public_key_e.get()),
|
||||
gcry_mpi_get_nbits(public_key_n.get()));
|
||||
}
|
||||
};
|
||||
|
||||
data = packets.find_first(packet_tag::type::public_key);
|
||||
if (data == nullptr)
|
||||
{
|
||||
throw std::runtime_error("public key is missing");
|
||||
}
|
||||
append_public_key(*data);
|
||||
|
||||
packets.for_each(packet_tag::type::public_subkey, append_public_key);
|
||||
}
|
||||
|
||||
std::string public_key_block::user_id() const
|
||||
{
|
||||
return m_user_id;
|
||||
}
|
||||
|
||||
// TODO: Signature expiration check
|
||||
signature_rsa::signature_rsa(
|
||||
uint8_t algorithm,
|
||||
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
|
||||
uint8_t hash_algorithm,
|
||||
const std::vector<uint8_t> &hashed_data,
|
||||
type type,
|
||||
s_expression signature,
|
||||
uint8_t version)
|
||||
: m_hash_algorithm(hash_algorithm)
|
||||
, m_hash_leftmost_bytes(hash_leftmost_bytes)
|
||||
, m_hashed_appendix(format_hashed_appendix(algorithm, hash_algorithm, hashed_data, type, version))
|
||||
, m_signature(std::move(signature))
|
||||
, m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
signature_rsa signature_rsa::from_armored(const std::string &armored_signed_message)
|
||||
{
|
||||
return from_base64(get_armored_block_contents(armored_signed_message, "BEGIN PGP SIGNATURE"));
|
||||
}
|
||||
|
||||
signature_rsa signature_rsa::from_base64(const std::string &base64)
|
||||
{
|
||||
std::string decoded = epee::string_encoding::base64_decode(strip_line_breaks(base64));
|
||||
epee::span<const uint8_t> buffer(reinterpret_cast<const uint8_t *>(&decoded[0]), decoded.size());
|
||||
return from_buffer(buffer);
|
||||
}
|
||||
|
||||
signature_rsa signature_rsa::from_buffer(const epee::span<const uint8_t> input)
|
||||
{
|
||||
packet_stream packets(input);
|
||||
|
||||
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::signature);
|
||||
if (data == nullptr)
|
||||
{
|
||||
throw std::runtime_error("signature is missing");
|
||||
}
|
||||
|
||||
deserializer<std::vector<uint8_t>> buffer(*data);
|
||||
|
||||
const auto version = buffer.read_big_endian<uint8_t>();
|
||||
if (version != 4)
|
||||
{
|
||||
throw std::runtime_error("unsupported signature version");
|
||||
}
|
||||
|
||||
const auto signature_type = static_cast<type>(buffer.read_big_endian<uint8_t>());
|
||||
|
||||
const auto algorithm = buffer.read_big_endian<uint8_t>();
|
||||
if (algorithm != openpgp::algorithm::rsa)
|
||||
{
|
||||
throw std::runtime_error("unsupported signature algorithm");
|
||||
}
|
||||
|
||||
const auto hash_algorithm = buffer.read_big_endian<uint8_t>();
|
||||
|
||||
const auto hashed_data_length = buffer.read_big_endian<uint16_t>();
|
||||
std::vector<uint8_t> hashed_data = buffer.read(hashed_data_length);
|
||||
|
||||
const auto unhashed_data_length = buffer.read_big_endian<uint16_t>();
|
||||
buffer.read_span(unhashed_data_length);
|
||||
|
||||
std::pair<uint8_t, uint8_t> hash_leftmost_bytes{buffer.read_big_endian<uint8_t>(), buffer.read_big_endian<uint8_t>()};
|
||||
|
||||
const mpi signature = buffer.read_mpi();
|
||||
|
||||
return signature_rsa(
|
||||
algorithm,
|
||||
std::move(hash_leftmost_bytes),
|
||||
hash_algorithm,
|
||||
hashed_data,
|
||||
signature_type,
|
||||
s_expression("(sig-val (rsa (s %m)))", signature.get()),
|
||||
version);
|
||||
}
|
||||
|
||||
bool signature_rsa::verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const
|
||||
{
|
||||
const s_expression signed_data = hash_message(message, public_key.bits());
|
||||
return gcry_pk_verify(m_signature.get(), signed_data.get(), public_key.get()) == 0;
|
||||
}
|
||||
|
||||
s_expression signature_rsa::hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case type::binary_document:
|
||||
return hash_bytes(message, public_key_bits);
|
||||
case type::canonical_text_document:
|
||||
{
|
||||
std::vector<uint8_t> crlf_formatted;
|
||||
crlf_formatted.reserve(message.size());
|
||||
const size_t message_size = message.size();
|
||||
for (size_t offset = 0; offset < message_size; ++offset)
|
||||
{
|
||||
const auto &character = message[offset];
|
||||
if (character == '\r')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (character == '\n')
|
||||
{
|
||||
const bool skip_last_crlf = offset + 1 == message_size;
|
||||
if (skip_last_crlf)
|
||||
{
|
||||
break;
|
||||
}
|
||||
crlf_formatted.push_back('\r');
|
||||
}
|
||||
crlf_formatted.push_back(character);
|
||||
}
|
||||
return hash_bytes(epee::to_span(crlf_formatted), public_key_bits);
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("unsupported signature type");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> signature_rsa::hash_asn_object_id() const
|
||||
{
|
||||
size_t size;
|
||||
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, nullptr, &size) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID) size");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> asn_object_id(size);
|
||||
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, &asn_object_id[0], &size) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID)");
|
||||
}
|
||||
|
||||
return asn_object_id;
|
||||
}
|
||||
|
||||
s_expression signature_rsa::hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const
|
||||
{
|
||||
const std::vector<uint8_t> plain_hash = (hash(m_hash_algorithm) << message << m_hashed_appendix).finish();
|
||||
if (plain_hash.size() < 2)
|
||||
{
|
||||
throw std::runtime_error("insufficient message hash size");
|
||||
}
|
||||
if (plain_hash[0] != m_hash_leftmost_bytes.first || plain_hash[1] != m_hash_leftmost_bytes.second)
|
||||
{
|
||||
throw std::runtime_error("signature checksum doesn't match the expected value");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> asn_object_id = hash_asn_object_id();
|
||||
|
||||
const size_t public_key_bytes = bits_to_bytes(public_key_bits);
|
||||
if (public_key_bytes < plain_hash.size() + asn_object_id.size() + 11)
|
||||
{
|
||||
throw std::runtime_error("insufficient public key bit length");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> emsa_pkcs1_v1_5_encoded;
|
||||
emsa_pkcs1_v1_5_encoded.reserve(public_key_bytes);
|
||||
emsa_pkcs1_v1_5_encoded.push_back(0);
|
||||
emsa_pkcs1_v1_5_encoded.push_back(1);
|
||||
const size_t ps_size = public_key_bytes - plain_hash.size() - asn_object_id.size() - 3;
|
||||
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), ps_size, 0xff);
|
||||
emsa_pkcs1_v1_5_encoded.push_back(0);
|
||||
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), asn_object_id.begin(), asn_object_id.end());
|
||||
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), plain_hash.begin(), plain_hash.end());
|
||||
|
||||
mpi value(emsa_pkcs1_v1_5_encoded);
|
||||
return s_expression("(data (flags raw) (value %m))", value.get());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> signature_rsa::format_hashed_appendix(
|
||||
uint8_t algorithm,
|
||||
uint8_t hash_algorithm,
|
||||
const std::vector<uint8_t> &hashed_data,
|
||||
uint8_t type,
|
||||
uint8_t version)
|
||||
{
|
||||
const uint16_t hashed_data_size = static_cast<uint16_t>(hashed_data.size());
|
||||
const uint32_t hashed_pefix_size = sizeof(version) + sizeof(type) + sizeof(algorithm) + sizeof(hash_algorithm) +
|
||||
sizeof(hashed_data_size) + hashed_data.size();
|
||||
|
||||
std::vector<uint8_t> appendix;
|
||||
appendix.reserve(hashed_pefix_size + sizeof(version) + sizeof(uint8_t) + sizeof(hashed_pefix_size));
|
||||
appendix.push_back(version);
|
||||
appendix.push_back(type);
|
||||
appendix.push_back(algorithm);
|
||||
appendix.push_back(hash_algorithm);
|
||||
appendix.push_back(static_cast<uint8_t>(hashed_data_size >> 8));
|
||||
appendix.push_back(static_cast<uint8_t>(hashed_data_size));
|
||||
appendix.insert(appendix.end(), hashed_data.begin(), hashed_data.end());
|
||||
appendix.push_back(version);
|
||||
appendix.push_back(0xff);
|
||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 24));
|
||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 16));
|
||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 8));
|
||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size));
|
||||
|
||||
return appendix;
|
||||
}
|
||||
|
||||
message_armored::message_armored(const std::string &message_armored)
|
||||
: m_message(get_armored_block_contents(message_armored, "BEGIN PGP SIGNED MESSAGE"))
|
||||
{
|
||||
}
|
||||
|
||||
message_armored::operator epee::span<const uint8_t>() const
|
||||
{
|
||||
return epee::to_byte_span(epee::to_span(m_message));
|
||||
}
|
||||
|
||||
} // namespace openpgp
|
||||
127
src/openpgp/openpgp.h
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include <span.h>
|
||||
|
||||
#include "s_expression.h"
|
||||
|
||||
namespace openpgp
|
||||
{
|
||||
|
||||
enum algorithm : uint8_t
|
||||
{
|
||||
rsa = 1,
|
||||
};
|
||||
|
||||
class public_key_rsa
|
||||
{
|
||||
public:
|
||||
public_key_rsa(s_expression expression, size_t bits);
|
||||
|
||||
size_t bits() const;
|
||||
const gcry_sexp_t &get() const;
|
||||
|
||||
private:
|
||||
s_expression m_expression;
|
||||
size_t m_bits;
|
||||
};
|
||||
|
||||
class public_key_block : public std::vector<public_key_rsa>
|
||||
{
|
||||
public:
|
||||
public_key_block(const std::string &armored);
|
||||
public_key_block(const epee::span<const uint8_t> buffer);
|
||||
|
||||
std::string user_id() const;
|
||||
|
||||
private:
|
||||
std::string m_user_id;
|
||||
};
|
||||
|
||||
class signature_rsa
|
||||
{
|
||||
public:
|
||||
enum type : uint8_t
|
||||
{
|
||||
binary_document = 0,
|
||||
canonical_text_document = 1,
|
||||
};
|
||||
|
||||
signature_rsa(
|
||||
uint8_t algorithm,
|
||||
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
|
||||
uint8_t hash_algorithm,
|
||||
const std::vector<uint8_t> &hashed_data,
|
||||
type type,
|
||||
s_expression signature,
|
||||
uint8_t version);
|
||||
|
||||
static signature_rsa from_armored(const std::string &armored_signed_message);
|
||||
static signature_rsa from_base64(const std::string &base64);
|
||||
static signature_rsa from_buffer(const epee::span<const uint8_t> input);
|
||||
|
||||
bool verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const;
|
||||
|
||||
private:
|
||||
s_expression hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const;
|
||||
std::vector<uint8_t> hash_asn_object_id() const;
|
||||
s_expression hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const;
|
||||
|
||||
static std::vector<uint8_t> format_hashed_appendix(
|
||||
uint8_t algorithm,
|
||||
uint8_t hash_algorithm,
|
||||
const std::vector<uint8_t> &hashed_data,
|
||||
uint8_t type,
|
||||
uint8_t version);
|
||||
|
||||
private:
|
||||
uint8_t m_hash_algorithm;
|
||||
std::pair<uint8_t, uint8_t> m_hash_leftmost_bytes;
|
||||
std::vector<uint8_t> m_hashed_appendix;
|
||||
s_expression m_signature;
|
||||
type m_type;
|
||||
};
|
||||
|
||||
class message_armored
|
||||
{
|
||||
public:
|
||||
message_armored(const std::string &message_armored);
|
||||
|
||||
operator epee::span<const uint8_t>() const;
|
||||
|
||||
private:
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
} // namespace openpgp
|
||||
@@ -1,21 +1,21 @@
|
||||
// Copyright (c) 2017-2018, The Monero Project
|
||||
//
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
@@ -26,60 +26,63 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 1.4
|
||||
import moneroComponents.Wallet 1.0
|
||||
import "." as MoneroComponents
|
||||
#pragma once
|
||||
|
||||
Item {
|
||||
id: item
|
||||
property string message: ""
|
||||
property bool active: false
|
||||
height: 180
|
||||
width: 320
|
||||
property int margin: 15
|
||||
x: parent.width - width - margin
|
||||
y: parent.height - height * scale.yScale - margin * scale.yScale
|
||||
#include <vector>
|
||||
|
||||
Rectangle {
|
||||
color: "#FF6C3C"
|
||||
border.color: "black"
|
||||
anchors.fill: parent
|
||||
#include <span.h>
|
||||
|
||||
TextArea {
|
||||
id:versionText
|
||||
readOnly: true
|
||||
backgroundVisible: false
|
||||
textFormat: TextEdit.AutoText
|
||||
anchors.fill: parent
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 12
|
||||
textMargin: 20
|
||||
textColor: "white"
|
||||
text: item.message
|
||||
wrapMode: Text.WrapAnywhere
|
||||
}
|
||||
#include "serialization.h"
|
||||
|
||||
namespace openpgp
|
||||
{
|
||||
|
||||
class packet_stream
|
||||
{
|
||||
public:
|
||||
packet_stream(const epee::span<const uint8_t> buffer)
|
||||
: packet_stream(deserializer<epee::span<const uint8_t>>(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename byte_container,
|
||||
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
||||
packet_stream(deserializer<byte_container> buffer)
|
||||
{
|
||||
while (!buffer.empty())
|
||||
{
|
||||
packet_tag tag = buffer.read_packet_tag();
|
||||
packets.push_back({std::move(tag), buffer.read(tag.length)});
|
||||
}
|
||||
}
|
||||
|
||||
transform: Scale {
|
||||
id: scale
|
||||
yScale: item.active ? 1 : 0
|
||||
|
||||
Behavior on yScale {
|
||||
NumberAnimation { duration: 500; easing.type: Easing.InOutCubic }
|
||||
}
|
||||
const std::vector<uint8_t> *find_first(packet_tag::type type) const
|
||||
{
|
||||
for (const auto &packet : packets)
|
||||
{
|
||||
if (packet.first.packet_type == type)
|
||||
{
|
||||
return &packet.second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hider
|
||||
interval: 30000; running: false; repeat: false
|
||||
onTriggered: { item.active = false }
|
||||
template <typename Callback>
|
||||
void for_each(packet_tag::type type, Callback &callback) const
|
||||
{
|
||||
for (const auto &packet : packets)
|
||||
{
|
||||
if (packet.first.packet_type == type)
|
||||
{
|
||||
callback(packet.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function show(message) {
|
||||
item.visible = true
|
||||
item.message = message
|
||||
item.active = true
|
||||
hider.running = true
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<std::pair<packet_tag, std::vector<uint8_t>>> packets;
|
||||
};
|
||||
|
||||
} // namespace openpgp
|
||||
@@ -1,21 +1,21 @@
|
||||
// Copyright (c) 2014-2019, The Monero Project
|
||||
//
|
||||
// Copyright (c) 2020, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
@@ -26,26 +26,53 @@
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Controls 2.0
|
||||
#pragma once
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
Rectangle {
|
||||
property bool active: false
|
||||
Layout.preferredWidth: 30
|
||||
Layout.fillHeight: true
|
||||
property string activeColor: MoneroComponents.Style.defaultFontColor
|
||||
property string inactiveColor: MoneroComponents.Style.progressBarBackgroundColor
|
||||
color: "transparent"
|
||||
#include <gcrypt.h>
|
||||
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 10
|
||||
height: 10
|
||||
radius: 10
|
||||
color: parent.active ? parent.activeColor : parent.inactiveColor
|
||||
namespace openpgp
|
||||
{
|
||||
|
||||
class s_expression
|
||||
{
|
||||
public:
|
||||
s_expression(const s_expression &) = delete;
|
||||
s_expression &operator=(const s_expression &) = delete;
|
||||
|
||||
template <typename... Args>
|
||||
s_expression(Args... args)
|
||||
{
|
||||
if (gcry_sexp_build(&data, nullptr, args...) != GPG_ERR_NO_ERROR)
|
||||
{
|
||||
throw std::runtime_error("failed to build S-expression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_expression(s_expression &&other)
|
||||
{
|
||||
std::swap(data, other.data);
|
||||
}
|
||||
|
||||
s_expression(gcry_sexp_t data)
|
||||
: data(data)
|
||||
{
|
||||
}
|
||||
|
||||
~s_expression()
|
||||
{
|
||||
gcry_sexp_release(data);
|
||||
}
|
||||
|
||||
const gcry_sexp_t &get() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
gcry_sexp_t data = nullptr;
|
||||
};
|
||||
|
||||
} // namespace openpgp
|
||||