mirror of
https://github.com/monero-project/monero-gui.git
synced 2026-04-04 19:47:26 -04:00
Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8444a9563e | ||
|
|
2f41a6aecf | ||
|
|
61bb6d359f | ||
|
|
938a4fada4 | ||
|
|
142c6bc19f | ||
|
|
f53af12e02 | ||
|
|
990e92ba00 | ||
|
|
816eeb4647 | ||
|
|
d5b4f43f48 | ||
|
|
4597b4d94d | ||
|
|
691fdac7a4 | ||
|
|
3a37364741 | ||
|
|
c789efbe8a | ||
|
|
ddec66b2ad | ||
|
|
325e99d202 | ||
|
|
76b0b6013a | ||
|
|
183585653f | ||
|
|
6bc9627046 | ||
|
|
ba7eeb12c5 | ||
|
|
2354c615d1 | ||
|
|
3aa530aa84 | ||
|
|
6b0cb8dadb | ||
|
|
ecc8b8cc99 | ||
|
|
74e1483d0b | ||
|
|
b3dace6b45 | ||
|
|
a17b88d80a | ||
|
|
1ee06fb78c | ||
|
|
ac2e9d370b | ||
|
|
a0a9d9e31e | ||
|
|
1e4c0d2e0d | ||
|
|
7c881d0100 | ||
|
|
d83f14799e | ||
|
|
34df4e74d4 | ||
|
|
9593a16cb0 | ||
|
|
3e8bc1dcd3 | ||
|
|
8dd06bba5c | ||
|
|
244d622818 | ||
|
|
34d3f6575a | ||
|
|
e94ac7c75d | ||
|
|
ad19dbb440 | ||
|
|
c18614f96f | ||
|
|
16fdedc4d0 | ||
|
|
057de959b9 | ||
|
|
88d26dbecf | ||
|
|
059af2bb49 | ||
|
|
4403387fa4 | ||
|
|
92a2ae1a11 | ||
|
|
40adc6bbbf | ||
|
|
c073867657 | ||
|
|
a7f5b44488 | ||
|
|
8b8948954d | ||
|
|
2c763f5014 | ||
|
|
5d566d1c02 | ||
|
|
41520b3a71 | ||
|
|
5039de8327 | ||
|
|
2bb1092472 | ||
|
|
16fd2b4447 | ||
|
|
3c27c570d4 | ||
|
|
e25e44b45f | ||
|
|
b20b956e15 | ||
|
|
4f10683c2c | ||
|
|
f40b10ea0b | ||
|
|
38f21a3e89 | ||
|
|
1f0f21a8e5 | ||
|
|
c948c9dd7c | ||
|
|
04d5fa51cf | ||
|
|
c1573c2c2a | ||
|
|
42fba21c6b | ||
|
|
04e3ac9200 | ||
|
|
f9f319d571 | ||
|
|
090dca7848 | ||
|
|
9cf7c7f03c | ||
|
|
d3d26e37d0 | ||
|
|
b79f1b8ff4 | ||
|
|
2a44f95f16 | ||
|
|
dc0ce27963 | ||
|
|
378da8093d | ||
|
|
5261f79e9f | ||
|
|
1659c7fd1a | ||
|
|
ec7bc577d6 | ||
|
|
3f4de99be4 | ||
|
|
1a11f2b192 | ||
|
|
e21d7be725 | ||
|
|
4098352faf | ||
|
|
a25b164cd5 | ||
|
|
45b5150487 | ||
|
|
bd21914b9b | ||
|
|
5848aee1c3 | ||
|
|
a214003559 | ||
|
|
83bb7a9297 | ||
|
|
d3102b1cc5 | ||
|
|
93b22311e3 | ||
|
|
55baa8b695 | ||
|
|
18f16d9ebd | ||
|
|
e9b894da16 | ||
|
|
f3a24d92a4 | ||
|
|
07a9b0e6f7 | ||
|
|
3ca5f10fa8 | ||
|
|
f7b817972f | ||
|
|
9399839d96 | ||
|
|
51a4d1f629 | ||
|
|
bbe3716542 | ||
|
|
7c32fe6b5c | ||
|
|
72ab846be5 | ||
|
|
8e6a2cde0f | ||
|
|
c34d4ee97c | ||
|
|
0194cf8f22 | ||
|
|
ad06fcc79e | ||
|
|
7d4b82c691 | ||
|
|
69f989d617 | ||
|
|
0f3df860e3 | ||
|
|
5662841d22 | ||
|
|
3f0bbfb6aa | ||
|
|
ba4d6993b7 | ||
|
|
8d4cda030e | ||
|
|
772b828b67 | ||
|
|
78f5360af2 | ||
|
|
a1fdffcabe | ||
|
|
fed00a5662 | ||
|
|
79f2843b09 | ||
|
|
14a477748e | ||
|
|
cebb78979c | ||
|
|
df771470c2 | ||
|
|
e359c60f00 | ||
|
|
53335a8487 | ||
|
|
3f64312283 | ||
|
|
897946af13 | ||
|
|
e90626e05a | ||
|
|
90e9968dcb | ||
|
|
841d0e01dc | ||
|
|
2feee9e956 | ||
|
|
486ba05526 | ||
|
|
ae8394e5f8 | ||
|
|
fa79e609e1 | ||
|
|
cc352e4913 | ||
|
|
903539bd30 | ||
|
|
af0b3142a0 | ||
|
|
6fe41e6f55 | ||
|
|
2a6ad67f77 | ||
|
|
5652284572 | ||
|
|
2eeeadfd10 | ||
|
|
0d5d2dbf5e | ||
|
|
606dbed4a0 | ||
|
|
301b20d19c | ||
|
|
f6196d48ab | ||
|
|
110b09efba | ||
|
|
0fdf81bc92 | ||
|
|
ea1fee2f5f | ||
|
|
ef54a32de0 | ||
|
|
96f9c11320 | ||
|
|
4a5b191f7f | ||
|
|
148d487988 | ||
|
|
7b137f7682 | ||
|
|
2e81ea2c09 | ||
|
|
5150945414 | ||
|
|
ec8cd137cc | ||
|
|
d5365298d2 | ||
|
|
30bf63b4b8 | ||
|
|
e8ee55a502 | ||
|
|
212fa083e2 | ||
|
|
3daf16e65d | ||
|
|
abfaac9772 | ||
|
|
28e6558a48 | ||
|
|
2d20bfd7ac | ||
|
|
45bfcfd2e9 | ||
|
|
46cea8db6b | ||
|
|
b4c0cb65de | ||
|
|
56e611480a | ||
|
|
ff201af778 | ||
|
|
ef2be82c21 | ||
|
|
6fce5c7a84 | ||
|
|
157166269b | ||
|
|
caa273afea | ||
|
|
d58ce3f599 | ||
|
|
19a6f399f3 | ||
|
|
18c964afca | ||
|
|
cedfa5aabb | ||
|
|
cd3a0f85a6 | ||
|
|
1373e709d6 | ||
|
|
baa0ffa5f9 | ||
|
|
56722e4747 |
67
.github/workflows/build.yml
vendored
67
.github/workflows/build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: continuous-integration/gh-actions/gui
|
||||
name: ci/gh-actions/gui
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5 pkg-config
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi zmq libpgm libsodium miniupnpc ldns expat libunwind-headers protobuf qt5 pkg-config
|
||||
- name: build
|
||||
run: DEV_MODE=ON make release -j3
|
||||
- name: test qml
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- 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 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 libgcrypt20-dev xvfb
|
||||
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtqml-models2 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: DEV_MODE=ON make release -j3
|
||||
- name: test qml
|
||||
@@ -58,22 +58,59 @@ jobs:
|
||||
- name: test qml
|
||||
run: build/release/bin/monero-wallet-gui --test-qml
|
||||
|
||||
macos-bundle:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: install dependencies
|
||||
run: HOMEBREW_NO_AUTO_UPDATE=1 brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf pkg-config python3 p7zip
|
||||
- name: install dependencies
|
||||
run: pip3 install requests semantic_version lxml py7zr
|
||||
- name: download qt
|
||||
run: |
|
||||
curl -O https://raw.githubusercontent.com/engnr/qt-downloader/master/qt-downloader
|
||||
chmod +x qt-downloader
|
||||
./qt-downloader macos desktop 5.15.2 clang_64
|
||||
working-directory: ../
|
||||
- name: build
|
||||
run: CMAKE_PREFIX_PATH=/Users/runner/work/monero-gui/5.15.2/clang_64 make release -j3
|
||||
- name: deploy
|
||||
run: make deploy
|
||||
working-directory: build/release
|
||||
- name: test qml
|
||||
run: build/release/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml
|
||||
- name: create .tar
|
||||
run: tar -cf monero-wallet-gui.tar monero-wallet-gui.app
|
||||
working-directory: build/release/bin
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.job }}
|
||||
path: build/release/bin/monero-wallet-gui.tar
|
||||
|
||||
docker-linux-static:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.8
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.10
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: docker-linux-static-{hash}
|
||||
restore-keys: |
|
||||
docker-linux-static-
|
||||
- name: install dependencies
|
||||
run: sudo apt -y install xvfb libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xkb1 libxkbcommon-x11-0
|
||||
- name: preprare build enviroment
|
||||
run: docker build --tag monero:build-env-linux --build-arg THREADS=3 --file Dockerfile.linux .
|
||||
- name: build
|
||||
run: docker run --rm -v /home/runner/work/monero-gui/monero-gui:/monero-gui -w /monero-gui monero:build-env-linux sh -c 'make release-static -j3'
|
||||
- name: sha256sum
|
||||
run: shasum -a256 /home/runner/work/monero-gui/monero-gui/build/release/bin/monero-wallet-gui
|
||||
- name: test qml
|
||||
run: xvfb-run -a /home/runner/work/monero-gui/monero-gui/build/release/bin/monero-wallet-gui --test-qml
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.job }}
|
||||
@@ -87,7 +124,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.8
|
||||
- uses: satackey/action-docker-layer-caching@v0.0.10
|
||||
continue-on-error: true
|
||||
with:
|
||||
key: docker-windows-static-{hash}
|
||||
@@ -97,6 +134,8 @@ jobs:
|
||||
run: docker build --tag monero:build-env-windows --build-arg THREADS=3 --file Dockerfile.windows .
|
||||
- name: build
|
||||
run: docker run --rm -v /home/runner/work/monero-gui/monero-gui:/monero-gui -w /monero-gui monero:build-env-windows sh -c 'make depends root=/depends target=x86_64-w64-mingw32 tag=win-x64 -j3'
|
||||
- name: sha256sum
|
||||
run: shasum -a256 /home/runner/work/monero-gui/monero-gui/build/x86_64-w64-mingw32/release/bin/monero-wallet-gui.exe
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ github.job }}
|
||||
@@ -118,3 +157,21 @@ jobs:
|
||||
with:
|
||||
name: ${{ github.job }}
|
||||
path: /home/runner/work/monero-gui/monero-gui/build/Android/release/android-build/monero-gui.apk
|
||||
|
||||
source-archive:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: archive
|
||||
run: |
|
||||
pip install git-archive-all
|
||||
export VERSION="monero-gui-$(git describe)"
|
||||
export OUTPUT="$VERSION.tar"
|
||||
echo "OUTPUT=$OUTPUT" >> $GITHUB_ENV
|
||||
/home/runner/.local/bin/git-archive-all --prefix "$VERSION/" --force-submodules "$OUTPUT"
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ env.OUTPUT }}
|
||||
path: /home/runner/work/monero-gui/monero-gui/${{ env.OUTPUT }}
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -2,3 +2,6 @@
|
||||
path = monero
|
||||
url = https://github.com/monero-project/monero
|
||||
ignore = all
|
||||
[submodule "external/quirc"]
|
||||
path = external/quirc
|
||||
url = https://github.com/dlbeer/quirc/
|
||||
|
||||
@@ -4,8 +4,8 @@ project(monero-gui)
|
||||
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
|
||||
|
||||
set(VERSION_MAJOR "17")
|
||||
set(VERSION_MINOR "1")
|
||||
set(VERSION_REVISION "3")
|
||||
set(VERSION_MINOR "2")
|
||||
set(VERSION_REVISION "1")
|
||||
set(VERSION "0.${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||
|
||||
option(STATIC "Link libraries statically, requires static Qt")
|
||||
@@ -48,8 +48,9 @@ if(NOT MANUAL_SUBMODULES)
|
||||
else()
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} fetch WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/monero RESULT_VARIABLE GIT_FETCH_RESULT)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} checkout -f origin/master WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/monero RESULT_VARIABLE GIT_CHECKOUT_MASTER_RESULT)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule sync --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/monero RESULT_VARIABLE GIT_SUBMODULE_SYNC_RESULT)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --force --recursive WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/monero RESULT_VARIABLE GIT_SUBMODULE_UPDATE_RESULT)
|
||||
if(NOT GIT_FETCH_RESULT EQUAL "0" OR NOT GIT_CHECKOUT_MASTER_RESULT EQUAL "0" OR NOT GIT_SUBMODULE_UPDATE_RESULT EQUAL "0")
|
||||
if(NOT GIT_FETCH_RESULT EQUAL "0" OR NOT GIT_CHECKOUT_MASTER_RESULT EQUAL "0" OR NOT GIT_SUBMODULE_SYNC_RESULT EQUAL "0" OR NOT GIT_SUBMODULE_UPDATE_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "Updating git submodule to master (-DDEV_MODE=ON) failed")
|
||||
endif()
|
||||
endif()
|
||||
@@ -131,12 +132,8 @@ message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
|
||||
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
|
||||
message(STATUS "OpenSSL: libraries at ${OPENSSL_LIBRARIES} ${OPENSSL_SSL_LIBRARIES}")
|
||||
|
||||
# Zbar (for QR scanner)
|
||||
if(WITH_SCANNER)
|
||||
add_definitions(-DWITH_SCANNER)
|
||||
find_package(ZBar0 REQUIRED)
|
||||
message(STATUS "libzbar: include dir at ${ZBAR_INCLUDE_DIR}")
|
||||
message(STATUS "libzbar: libraries at ${ZBAR_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
# Sodium
|
||||
@@ -178,7 +175,10 @@ find_package(Boost 1.58 REQUIRED COMPONENTS
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
set(CMAKE_SKIP_RPATH ON)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES_PREV ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
|
||||
find_package(X11 REQUIRED)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_PREV})
|
||||
message(STATUS "X11_FOUND = ${X11_FOUND}")
|
||||
message(STATUS "X11_INCLUDE_DIR = ${X11_INCLUDE_DIR}")
|
||||
message(STATUS "X11_LIBRARIES = ${X11_LIBRARIES}")
|
||||
@@ -229,11 +229,6 @@ if(UNIX)
|
||||
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)
|
||||
set(CMAKE_BUILD_RPATH "${CMAKE_PREFIX_PATH}/lib")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
@@ -265,6 +260,13 @@ else()
|
||||
pkg_check_modules(QT5_PKG_CONFIG REQUIRED ${QT5_LIBRARIES_ABI})
|
||||
endif()
|
||||
|
||||
get_target_property(QMAKE_IMPORTED_LOCATION Qt5::qmake IMPORTED_LOCATION)
|
||||
get_filename_component(QT_INSTALL_PREFIX "${QMAKE_IMPORTED_LOCATION}/../.." ABSOLUTE)
|
||||
|
||||
if(APPLE AND NOT STATIC)
|
||||
set(CMAKE_BUILD_RPATH "${QT_INSTALL_PREFIX}/lib")
|
||||
endif()
|
||||
|
||||
if(QT5_PKG_CONFIG_FOUND)
|
||||
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
|
||||
if(STATIC)
|
||||
@@ -276,10 +278,10 @@ if(QT5_PKG_CONFIG_FOUND)
|
||||
list(JOIN ${QT5_PKG_CONFIG}_LDFLAGS_OTHER " " ${QT5_PKG_CONFIG}_LDFLAGS_OTHER)
|
||||
endif()
|
||||
# temporal workaround for https://bugreports.qt.io/browse/QTBUG-80922
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${CMAKE_PREFIX_PATH}" ${QT5_PKG_CONFIG}_LDFLAGS_OTHER "${${QT5_PKG_CONFIG}_LDFLAGS_OTHER}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${CMAKE_PREFIX_PATH}" ${QT5_PKG_CONFIG}_LIBRARIES "${${QT5_PKG_CONFIG}_LIBRARIES}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${CMAKE_PREFIX_PATH}" ${QT5_PKG_CONFIG}_INCLUDE_DIRS "${${QT5_PKG_CONFIG}_INCLUDE_DIRS}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${CMAKE_PREFIX_PATH}" ${QT5_PKG_CONFIG}_LIBRARY_DIRS "${${QT5_PKG_CONFIG}_LIBRARY_DIRS}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_LDFLAGS_OTHER "${${QT5_PKG_CONFIG}_LDFLAGS_OTHER}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_LIBRARIES "${${QT5_PKG_CONFIG}_LIBRARIES}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_INCLUDE_DIRS "${${QT5_PKG_CONFIG}_INCLUDE_DIRS}")
|
||||
STRING(REPLACE "${QT5_PKG_CONFIG_Qt5Core_PREFIX}" "${QT_INSTALL_PREFIX}" ${QT5_PKG_CONFIG}_LIBRARY_DIRS "${${QT5_PKG_CONFIG}_LIBRARY_DIRS}")
|
||||
endif()
|
||||
|
||||
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
|
||||
@@ -301,6 +303,7 @@ if(STATIC)
|
||||
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/QtQml)
|
||||
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQml/Models.2)
|
||||
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)
|
||||
@@ -323,6 +326,7 @@ if(STATIC)
|
||||
qmlsettingsplugin
|
||||
qmlxmllistmodelplugin
|
||||
qquicklayoutsplugin
|
||||
modelsplugin
|
||||
)
|
||||
|
||||
if(WITH_SCANNER)
|
||||
@@ -385,8 +389,10 @@ if(STATIC)
|
||||
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})
|
||||
pkg_check_modules(X11XCB_XCBGLX REQUIRED x11-xcb xcb-glx)
|
||||
list(APPEND QT5_LIBRARIES ${X11XCB_XCBGLX_LIBRARIES})
|
||||
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
|
||||
list(APPEND QT5_LIBRARIES ${FONTCONFIG_STATIC_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -413,7 +419,7 @@ message(STATUS "Using Boost libraries at ${Boost_LIBRARIES}")
|
||||
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
|
||||
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt;winmm)
|
||||
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
|
||||
if(DEPENDS)
|
||||
set(ICU_LIBRARIES icuio icui18n icuuc icudata icutu iconv)
|
||||
else()
|
||||
@@ -556,6 +562,6 @@ if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(external)
|
||||
add_subdirectory(translations)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
FROM debian:unstable
|
||||
FROM debian:stretch
|
||||
|
||||
ARG THREADS=1
|
||||
ARG ANDROID_NDK_REVISION=21d
|
||||
ARG ANDROID_NDK_HASH=bcf4023eb8cb6976a4c7cff0a8a8f145f162bf4d
|
||||
ARG ANDROID_SDK_REVISION=4333796
|
||||
ARG ANDROID_SDK_HASH=92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9
|
||||
ARG QT_VERSION=5.15
|
||||
ARG QT_VERSION=5.15.2
|
||||
|
||||
WORKDIR /opt/android
|
||||
ENV WORKDIR=/opt/android
|
||||
@@ -19,12 +19,12 @@ ENV ANDROID_SDK_ROOT=${WORKDIR}/tools
|
||||
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
|
||||
ENV PATH=${JAVA_HOME}/bin:${PATH}
|
||||
ENV PREFIX=${WORKDIR}/prefix
|
||||
ENV QT_PREFIX=${WORKDIR}/Qt-${QT_VERSION}
|
||||
ENV TOOLCHAIN_DIR=${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ant automake build-essential ca-certificates-java cmake file gettext git libc6 libncurses5 \
|
||||
libstdc++6 libtinfo5 libtool libz1 openjdk-8-jdk-headless openjdk-8-jre-headless pkg-config python3 unzip wget
|
||||
&& apt-get install -y ant automake build-essential ca-certificates-java file gettext git libc6 libncurses5 \
|
||||
libssl-dev libstdc++6 libtinfo5 libtool libz1 openjdk-8-jdk-headless openjdk-8-jre-headless pkg-config python3 \
|
||||
unzip wget
|
||||
|
||||
RUN wget -q https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \
|
||||
&& unzip -q sdk-tools-linux-${ANDROID_SDK_REVISION}.zip \
|
||||
@@ -66,7 +66,7 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 \
|
||||
-no-sql-mysql \
|
||||
-opensource -confirm-license \
|
||||
-android-arch arm64-v8a \
|
||||
-prefix ${QT_PREFIX} \
|
||||
-prefix ${PREFIX} \
|
||||
-nomake tools -nomake tests -nomake examples \
|
||||
-skip qtwebengine \
|
||||
-skip qtserialport \
|
||||
@@ -78,11 +78,11 @@ RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 \
|
||||
&& sed -i '213,215d' qtbase/src/3rdparty/pcre2/src/sljit/sljitConfigInternal.h \
|
||||
&& PATH=${HOST_PATH} make -j${THREADS} \
|
||||
&& PATH=${HOST_PATH} make -j${THREADS} install \
|
||||
&& cd qttools/src/linguist/lrelease && \
|
||||
../../../../qtbase/bin/qmake && \
|
||||
PATH=${HOST_PATH} make -j$THREADS install && \
|
||||
cd ../../../.. && \
|
||||
rm -rf $(pwd)
|
||||
&& cd qttools/src/linguist/lrelease \
|
||||
&& ../../../../qtbase/bin/qmake \
|
||||
&& PATH=${HOST_PATH} make -j${THREADS} install \
|
||||
&& cd ../../../.. \
|
||||
&& rm -rf $(pwd)
|
||||
|
||||
ARG ICONV_VERSION=1.16
|
||||
ARG ICONV_HASH=e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04
|
||||
@@ -152,25 +152,6 @@ RUN set -ex \
|
||||
&& make -j${THREADS} install \
|
||||
&& rm -rf $(pwd)
|
||||
|
||||
RUN git clone https://github.com/ZBar/ZBar.git --depth 1 \
|
||||
&& cd ZBar \
|
||||
&& git reset --hard 854a5d97059e395807091ac4d80c53f7968abb8f \
|
||||
&& sed -i 's/SHARED/STATIC/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CPP_FEATURES := exceptions rtti features\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS := -Wno-multichar\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -D_ANDROID\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DLIBDIR="\\".\\""\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DBUILDING_LIBICONV\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DBUILDING_LIBCHARSET\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -DIN_LIBRARY\n\0/' android/jni/Android.mk \
|
||||
&& sed -i -E 's/(.*BUILD_STATIC_LIBRARY.*)/LOCAL_CFLAGS += -fno-stack-protector\n\0/' android/jni/Android.mk \
|
||||
&& echo "APP_ABI := arm64-v8a \nAPP_STL := c++_shared \nTARGET_PLATFORM := ${ANDROID_API} \nTARGET_ARCH_ABI := arm64-v8a \nAPP_CFLAGS += -target aarch64-none-linux-android -fexceptions -fstack-protector-strong -fno-limit-debug-info -mfloat-abi=softfp -fno-builtin-memmove -fno-omit-frame-pointer -fno-stack-protector\n" \
|
||||
>> android/jni/Application.mk \
|
||||
&& cd android \
|
||||
&& CC=${ANDROID_CLANG} CXX=${ANDROID_CLANGPP} ${ANDROID_NDK_ROOT}/ndk-build ICONV_SRC=${WORKDIR}/libiconv-${ICONV_VERSION} -B V=1 NDK_APPLICATION_MK=jni/Application.mk \
|
||||
&& cp obj/local/arm64-v8a/lib* ${PREFIX}/lib \
|
||||
&& cp -r ../include/* ${PREFIX}/include
|
||||
|
||||
RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git \
|
||||
&& cd libgpg-error \
|
||||
&& git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 \
|
||||
@@ -195,21 +176,29 @@ RUN cd tools \
|
||||
&& rm -f tools_r25.2.5-linux.zip \
|
||||
&& echo y | ${ANDROID_SDK_ROOT}/tools/android update sdk --no-ui --all --filter build-tools-28.0.3
|
||||
|
||||
RUN git clone -b v3.19.7 --depth 1 https://github.com/Kitware/CMake \
|
||||
&& cd CMake \
|
||||
&& git reset --hard 22612dd53a46c7f9b4c3f4b7dbe5c78f9afd9581 \
|
||||
&& PATH=${HOST_PATH} ./bootstrap \
|
||||
&& PATH=${HOST_PATH} make -j${THREADS} \
|
||||
&& PATH=${HOST_PATH} make -j${THREADS} install \
|
||||
&& rm -rf $(pwd)
|
||||
|
||||
CMD set -ex \
|
||||
&& cd /monero-gui \
|
||||
&& mkdir -p build/Android/release \
|
||||
&& cd build/Android/release \
|
||||
&& cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" \
|
||||
-DCMAKE_PREFIX_PATH="${PREFIX};${QT_PREFIX}" \
|
||||
-DCMAKE_FIND_ROOT_PATH="${PREFIX};${QT_PREFIX}" \
|
||||
-DCMAKE_PREFIX_PATH="${PREFIX}" \
|
||||
-DCMAKE_FIND_ROOT_PATH="${PREFIX}" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DARCH="armv8-a" \
|
||||
-DANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL} \
|
||||
-DANDROID_ABI="arm64-v8a" \
|
||||
-DANDROID_TOOLCHAIN=clang \
|
||||
-DBoost_USE_STATIC_RUNTIME=ON \
|
||||
-DLRELEASE_PATH="${QT_PREFIX}/bin" \
|
||||
-DLRELEASE_PATH="${PREFIX}/bin" \
|
||||
-DQT_ANDROID_APPLICATION_BINARY="monero-wallet-gui" \
|
||||
-DWITH_SCANNER=ON \
|
||||
../../.. \
|
||||
|
||||
@@ -4,6 +4,7 @@ ARG THREADS=1
|
||||
ARG QT_VERSION=5.15.2
|
||||
|
||||
ENV CFLAGS="-fPIC"
|
||||
ENV CPPFLAGS="-fPIC"
|
||||
ENV CXXFLAGS="-fPIC"
|
||||
ENV SOURCE_DATE_EPOCH=1397818193
|
||||
|
||||
@@ -30,7 +31,7 @@ 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 && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -39,9 +40,14 @@ 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 && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
make -j$THREADS clean && \
|
||||
rm /usr/local/lib/libxcb-xinerama.so && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
cp src/.libs/libxcb-xinerama.a /usr/local/lib/ && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-util && \
|
||||
@@ -50,7 +56,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
|
||||
git submodule init && \
|
||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -61,7 +67,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
|
||||
git submodule init && \
|
||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -72,7 +78,7 @@ RUN git clone -b 0.4.0 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
|
||||
git submodule init && \
|
||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -83,7 +89,7 @@ RUN git clone -b 0.3.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
|
||||
git submodule init && \
|
||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -94,16 +100,16 @@ RUN git clone -b 0.4.1 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb-
|
||||
git submodule init && \
|
||||
git clone --depth 1 https://gitlab.freedesktop.org/xorg/util/xcb-util-m4 m4 && \
|
||||
git -C m4 reset --hard f662e3a93ebdec3d1c9374382dcc070093a42fed && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
./autogen.sh --enable-shared --disable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN apt install -y bison &&\
|
||||
RUN apt install -y bison && \
|
||||
git clone -b xkbcommon-0.5.0 --depth 1 https://github.com/xkbcommon/libxkbcommon && \
|
||||
cd libxkbcommon && \
|
||||
git reset --hard c43c3c866eb9d52cd8f61e75cbef1c30d07f3a28 && \
|
||||
./autogen.sh --prefix=/usr --disable-shared --enable-static --enable-x11 --disable-docs && \
|
||||
./autogen.sh --prefix=/usr --enable-shared --disable-static --enable-x11 --disable-docs && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
@@ -172,6 +178,9 @@ RUN wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN apt install -y libgl1-mesa-dev libglib2.0-dev mesa-common-dev && \
|
||||
rm /usr/lib/x86_64-linux-gnu/libX11.a && \
|
||||
rm /usr/lib/x86_64-linux-gnu/libXext.a && \
|
||||
rm /usr/lib/x86_64-linux-gnu/libX11-xcb.a && \
|
||||
git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
|
||||
cd qt5 && \
|
||||
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
|
||||
@@ -227,22 +236,6 @@ RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN git clone -b libX11-1.6.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libx11 && \
|
||||
cd libx11 && \
|
||||
git reset --hard db7cca17ad7807e92a928da9d4c68a00f4836da2 && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN git clone -b libXext-1.3.4 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxext && \
|
||||
cd libxext && \
|
||||
git reset --hard ebb167f34a3514783966775fb12573c4ed209625 && \
|
||||
./autogen.sh --disable-shared --enable-static && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
rm -rf $(pwd)
|
||||
|
||||
RUN apt install -y libsodium-dev && \
|
||||
git clone -b v4.3.2 --depth 1 https://github.com/zeromq/libzmq && \
|
||||
cd libzmq && \
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
ARG THREADS=1
|
||||
ARG QT_VERSION=5.15.2
|
||||
ENV SOURCE_DATE_EPOCH=1397818193
|
||||
|
||||
RUN apt update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y build-essential cmake g++-mingw-w64 gettext git libtool pkg-config \
|
||||
@@ -19,26 +21,35 @@ RUN git clone -b v0.17.0.0 --depth 1 https://github.com/monero-project/monero &&
|
||||
|
||||
RUN make -j$THREADS -C /depends HOST=x86_64-w64-mingw32 NO_QT=1
|
||||
|
||||
RUN curl -LO https://download.qt.io/archive/qt/5.9/5.9.9/single/qt-everywhere-opensource-src-5.9.9.tar.xz && \
|
||||
echo "5ce285209290a157d7f42ec8eb22bf3f1d76f2e03a95fc0b99b553391be01642 qt-everywhere-opensource-src-5.9.9.tar.xz" > hashsum.txt && \
|
||||
sha256sum -c hashsum.txt && \
|
||||
tar -xf qt-everywhere-opensource-src-5.9.9.tar.xz && \
|
||||
rm qt-everywhere-opensource-src-5.9.9.tar.xz && \
|
||||
cd qt-everywhere-opensource-src-5.9.9 && \
|
||||
RUN git clone git://code.qt.io/qt/qt5.git -b ${QT_VERSION} --depth 1 && \
|
||||
cd qt5 && \
|
||||
git clone git://code.qt.io/qt/qtbase.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtdeclarative.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtgraphicaleffects.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtimageformats.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtmultimedia.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtquickcontrols.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtquickcontrols2.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtsvg.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qttools.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qttranslations.git -b ${QT_VERSION} --depth 1 && \
|
||||
git clone git://code.qt.io/qt/qtxmlpatterns.git -b ${QT_VERSION} --depth 1 && \
|
||||
./configure --prefix=/depends/x86_64-w64-mingw32 -xplatform win32-g++ \
|
||||
-device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \
|
||||
-I $(pwd)/qtbase/src/3rdparty/angle/include \
|
||||
-opensource -confirm-license -release -static -static-runtime -opengl dynamic -no-angle \
|
||||
-no-avx -no-openssl -no-sql-sqlite \
|
||||
-no-feature-qml-worker-script -no-openssl -no-sql-sqlite \
|
||||
-qt-freetype -qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
|
||||
-skip gamepad -skip location -skip qt3d -skip qtactiveqt -skip qtandroidextras -skip qtcanvas3d -skip qtcharts \
|
||||
-skip qtconnectivity -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras \
|
||||
-skip qtmultimedia -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 location -skip qt3d -skip qtactiveqt -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 serialbus -skip webengine \
|
||||
-nomake examples -nomake tests -nomake tools && \
|
||||
make QMAKE="$(pwd)/qtbase/bin/qmake CONFIG-='debug debug_and_release'" -j$THREADS && \
|
||||
make -j$THREADS && \
|
||||
make -j$THREADS install && \
|
||||
cd qttools/src/linguist/lrelease && \
|
||||
../../../../qtbase/bin/qmake && \
|
||||
|
||||
102
LeftPanel.qml
102
LeftPanel.qml
@@ -58,12 +58,9 @@ Rectangle {
|
||||
signal historyClicked()
|
||||
signal transferClicked()
|
||||
signal receiveClicked()
|
||||
signal txkeyClicked()
|
||||
signal sharedringdbClicked()
|
||||
signal advancedClicked()
|
||||
signal settingsClicked()
|
||||
signal addressBookClicked()
|
||||
signal miningClicked()
|
||||
signal signClicked()
|
||||
signal accountClicked()
|
||||
|
||||
function selectItem(pos) {
|
||||
@@ -72,10 +69,6 @@ Rectangle {
|
||||
else if(pos === "Transfer") menuColumn.previousButton = transferButton
|
||||
else if(pos === "Receive") menuColumn.previousButton = receiveButton
|
||||
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
|
||||
else if(pos === "Mining") menuColumn.previousButton = miningButton
|
||||
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
|
||||
else if(pos === "SharedRingDB") menuColumn.previousButton = sharedringdbButton
|
||||
else if(pos === "Sign") menuColumn.previousButton = signButton
|
||||
else if(pos === "Settings") menuColumn.previousButton = settingsButton
|
||||
else if(pos === "Advanced") menuColumn.previousButton = advancedButton
|
||||
else if(pos === "Account") menuColumn.previousButton = accountButton
|
||||
@@ -480,6 +473,7 @@ Rectangle {
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = advancedButton
|
||||
panel.advancedClicked()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,98 +484,6 @@ Rectangle {
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- Mining tab ---------------
|
||||
MoneroComponents.MenuButton {
|
||||
id: miningButton
|
||||
visible: !isAndroid && !isIOS && appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Mining") + translationManager.emptyString
|
||||
symbol: qsTr("M") + translationManager.emptyString
|
||||
under: advancedButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = miningButton
|
||||
panel.miningClicked()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.MenuButtonDivider {
|
||||
visible: miningButton.present && appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- TxKey tab ---------------
|
||||
MoneroComponents.MenuButton {
|
||||
id: txkeyButton
|
||||
visible: appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Prove/check") + translationManager.emptyString
|
||||
symbol: qsTr("K") + translationManager.emptyString
|
||||
under: advancedButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = txkeyButton
|
||||
panel.txkeyClicked()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.MenuButtonDivider {
|
||||
visible: txkeyButton.present && appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- Shared RingDB tab ---------------
|
||||
MoneroComponents.MenuButton {
|
||||
id: sharedringdbButton
|
||||
visible: appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Shared RingDB") + translationManager.emptyString
|
||||
symbol: qsTr("G") + translationManager.emptyString
|
||||
under: advancedButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = sharedringdbButton
|
||||
panel.sharedringdbClicked()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.MenuButtonDivider {
|
||||
visible: sharedringdbButton.present && appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- Sign/verify tab ---------------
|
||||
MoneroComponents.MenuButton {
|
||||
id: signButton
|
||||
visible: appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Sign/verify") + translationManager.emptyString
|
||||
symbol: qsTr("I") + translationManager.emptyString
|
||||
under: advancedButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = signButton
|
||||
panel.signClicked()
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.MenuButtonDivider {
|
||||
visible: signButton.present && appWindow.walletMode >= 2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 20
|
||||
}
|
||||
|
||||
// ------------- Settings tab ---------------
|
||||
MoneroComponents.MenuButton {
|
||||
id: settingsButton
|
||||
|
||||
29
Makefile
29
Makefile
@@ -2,19 +2,30 @@ ANDROID_STANDALONE_TOOLCHAIN_PATH ?= /usr/local/toolchain
|
||||
MANUAL_SUBMODULES ?= OFF
|
||||
|
||||
dotgit=$(shell ls -d .git/config)
|
||||
ifneq ($(dotgit), .git/config)
|
||||
USE_SINGLE_BUILDDIR=1
|
||||
ifeq ($(dotgit), .git/config)
|
||||
ifeq ($(shell git --version > /dev/null 2>&1 ; echo $$?), 0)
|
||||
git = yes
|
||||
else
|
||||
$(warning git command not found)
|
||||
endif
|
||||
endif
|
||||
|
||||
subbuilddir:=$(shell echo `uname | sed -e 's|[:/\\ \(\)]|_|g'`/`git branch | grep '\* ' | cut -f2- -d' '| sed -e 's|[:/\\ \(\)]|_|g'`)
|
||||
builddir := build
|
||||
topdir := ../..
|
||||
ifeq ($(USE_SINGLE_BUILDDIR), OFF)
|
||||
builddir := build/"$(subbuilddir)"
|
||||
topdir := ../../../..
|
||||
deldirs := $(builddir)
|
||||
os := $(shell echo `uname | sed -e 's|[:/\\ \(\)]|_|g'`)
|
||||
builddir := $(builddir)/$(os)
|
||||
topdir := $(topdir)/..
|
||||
|
||||
ifdef git
|
||||
branch := $(shell git branch | grep '\* ' | cut -f2- -d' '| sed -e 's|[:/\\ \(\)]|_|g')
|
||||
builddir := $(builddir)/$(branch)
|
||||
topdir := $(topdir)/..
|
||||
endif
|
||||
|
||||
deldirs := $(builddir)
|
||||
else
|
||||
builddir := build
|
||||
topdir := ../..
|
||||
deldirs := $(builddir)/debug $(builddir)/release $(builddir)/fuzz
|
||||
deldirs := $(builddir)/debug $(builddir)/release $(builddir)/fuzz
|
||||
endif
|
||||
|
||||
default:
|
||||
|
||||
@@ -51,22 +51,19 @@ Rectangle {
|
||||
property alias flickable: mainFlickable
|
||||
|
||||
property Transfer transferView: Transfer {
|
||||
onPaymentClicked: root.paymentClicked(address, paymentId, amount, mixinCount, priority, description)
|
||||
onPaymentClicked: root.paymentClicked(recipients, paymentId, mixinCount, priority, description)
|
||||
onSweepUnmixableClicked: root.sweepUnmixableClicked()
|
||||
}
|
||||
property Receive receiveView: Receive { }
|
||||
property Merchant merchantView: Merchant { }
|
||||
property TxKey txkeyView: TxKey { }
|
||||
property SharedRingDB sharedringdbView: SharedRingDB { }
|
||||
property History historyView: History { }
|
||||
property Sign signView: Sign { }
|
||||
property Advanced advancedView: Advanced { }
|
||||
property Settings settingsView: Settings { }
|
||||
property Mining miningView: Mining { }
|
||||
property AddressBook addressBookView: AddressBook { }
|
||||
property Keys keysView: Keys { }
|
||||
property Account accountView: Account { }
|
||||
|
||||
signal paymentClicked(string address, string paymentId, string amount, int mixinCount, int priority, string description)
|
||||
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
||||
signal sweepUnmixableClicked()
|
||||
signal generatePaymentIdInvoked()
|
||||
signal getProofClicked(string txid, string address, string message);
|
||||
@@ -136,30 +133,18 @@ Rectangle {
|
||||
name: "Merchant"
|
||||
PropertyChanges { target: root; currentView: merchantView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: merchantView.merchantHeight + 80 }
|
||||
}, State {
|
||||
name: "TxKey"
|
||||
PropertyChanges { target: root; currentView: txkeyView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: txkeyView.txkeyHeight + 80 }
|
||||
}, State {
|
||||
name: "SharedRingDB"
|
||||
PropertyChanges { target: root; currentView: sharedringdbView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: sharedringdbView.panelHeight + 80 }
|
||||
}, State {
|
||||
name: "AddressBook"
|
||||
PropertyChanges { target: root; currentView: addressBookView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: addressBookView.addressbookHeight + 80 }
|
||||
}, State {
|
||||
name: "Sign"
|
||||
PropertyChanges { target: root; currentView: signView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: signView.signHeight + 80 }
|
||||
name: "Advanced"
|
||||
PropertyChanges { target: root; currentView: advancedView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: advancedView.panelHeight }
|
||||
}, State {
|
||||
name: "Settings"
|
||||
PropertyChanges { target: root; currentView: settingsView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: settingsView.settingsHeight }
|
||||
}, State {
|
||||
name: "Mining"
|
||||
PropertyChanges { target: root; currentView: miningView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: miningView.miningHeight + 80 }
|
||||
}, State {
|
||||
name: "Keys"
|
||||
PropertyChanges { target: root; currentView: keysView }
|
||||
@@ -168,7 +153,7 @@ Rectangle {
|
||||
name: "Account"
|
||||
PropertyChanges { target: root; currentView: accountView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: accountView.accountHeight + 80 }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
31
README.md
31
README.md
@@ -72,7 +72,7 @@ Packages are available for
|
||||
* Debian: See the [whonix/monero-gui repository](https://gitlab.com/whonix/monero-gui#how-to-install-monero-using-apt-get)
|
||||
* Void Linux: `xbps-install -S monero-gui`
|
||||
* GuixSD: `guix package -i monero-gui`
|
||||
* macOS (homebrew): `brew cask install monero-wallet`
|
||||
* macOS (homebrew): `brew install --cask monero-wallet`
|
||||
|
||||
Packaging for your favorite distribution would be a welcome contribution!
|
||||
|
||||
@@ -80,13 +80,14 @@ 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 Windows static binaries with Docker (any OS)
|
||||
### Building Reproducible Windows 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
|
||||
git clone --branch master --recursive https://github.com/monero-project/monero-gui.git
|
||||
```
|
||||
\* `master` - replace with the desired version tag (e.g. `v0.17.1.9`) to build the release binaries.
|
||||
3. Prepare build environment
|
||||
```
|
||||
cd monero-gui
|
||||
@@ -102,13 +103,14 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
\* `4` - number of CPU threads to use
|
||||
5. Monero GUI Windows static binaries will be placed in `monero-gui/build/x86_64-w64-mingw32/release/bin` directory
|
||||
|
||||
### Building Linux static binaries with Docker (any OS)
|
||||
### Building Reproducible 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
|
||||
git clone --branch master --recursive https://github.com/monero-project/monero-gui.git
|
||||
```
|
||||
\* `master` - replace with the desired version tag (e.g. `v0.17.1.9`) to build the release binaries.
|
||||
3. Prepare build environment
|
||||
```
|
||||
cd monero-gui
|
||||
@@ -123,6 +125,11 @@ Packaging for your favorite distribution would be a welcome contribution!
|
||||
\* `<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
|
||||
6. (*Optional*) Compare `monero-wallet-gui` SHA-256 hash to the one obtained from a trusted source
|
||||
```
|
||||
docker run --rm -it -v <MONERO_GUI_DIR_FULL_PATH>:/monero-gui -w /monero-gui monero:build-env-linux sh -c 'shasum -a 256 /monero-gui/build/release/bin/monero-wallet-gui'
|
||||
```
|
||||
\* `<MONERO_GUI_DIR_FULL_PATH>` - absolute path to `monero-gui` directory
|
||||
|
||||
### Building Android APK with Docker (any OS) *Experimental*
|
||||
- Minimum Android 9 Pie (API 28)
|
||||
@@ -201,7 +208,7 @@ The following instructions will fetch Qt from your distribution's repositories i
|
||||
|
||||
- For Ubuntu 17.10+
|
||||
|
||||
`sudo apt 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`
|
||||
`sudo apt install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtqml-models2 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`
|
||||
|
||||
- For Gentoo
|
||||
|
||||
@@ -211,13 +218,13 @@ The following instructions will fetch Qt from your distribution's repositories i
|
||||
|
||||
- For Ubuntu
|
||||
|
||||
`sudo apt install qtmultimedia5-dev qml-module-qtmultimedia libzbar-dev`
|
||||
`sudo apt install qtmultimedia5-dev qml-module-qtmultimedia`
|
||||
|
||||
- For Gentoo
|
||||
|
||||
The *qml* USE flag must be enabled.
|
||||
|
||||
`emerge dev-qt/qtmultimedia:5 media-gfx/zbar`
|
||||
`emerge dev-qt/qtmultimedia:5`
|
||||
|
||||
|
||||
3. Clone repository
|
||||
@@ -245,7 +252,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 libgcrypt`
|
||||
`brew install boost hidapi zmq libpgm libsodium miniupnpc ldns expat libunwind-headers protobuf libgcrypt`
|
||||
|
||||
4. Install Qt:
|
||||
|
||||
@@ -284,12 +291,6 @@ The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not off
|
||||
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`
|
||||
|
||||
```
|
||||
pacman -S mingw-w64-x86_64-zbar
|
||||
```
|
||||
|
||||
You find more details about those dependencies in the [Monero documentation](https://github.com/monero-project/monero). Note that that there is no more need to compile Boost from source; like everything else, you can install it now with a MSYS2 package.
|
||||
|
||||
4. Install Qt5
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# from http://code.google.com/p/low-cost-vision-2012/source/browse/CMakeModules/FindZBar0.cmake?name=2-helium-1&r=d61f248bd5565b3c086bf4769a04bfd98f7079df
|
||||
# - Try to find ZBar
|
||||
# This will define
|
||||
#
|
||||
# ZBAR_FOUND -
|
||||
# ZBAR_LIBRARY_DIR -
|
||||
# ZBAR_INCLUDE_DIR -
|
||||
# ZBAR_LIBRARIES -
|
||||
#
|
||||
|
||||
find_package(PkgConfig)
|
||||
if(PkgConfig_FOUND)
|
||||
pkg_check_modules(PC_ZBAR QUIET zbar)
|
||||
if(PC_ZBAR_FOUND)
|
||||
set(ZBAR_DEFINITIONS ${PC_ZBAR_CFLAGS_OTHER})
|
||||
find_library(ZBAR_LIBRARIES NAMES zbar HINTS ${PC_ZBAR_LIBDIR} ${PC_ZBAR_LIBRARY_DIRS})
|
||||
find_path(ZBAR_INCLUDE_DIR Decoder.h HINTS ${PC_ZBAR_INCLUDEDIR} ${PC_ZBAR_INCLUDE_DIRS} PATH_SUFFIXES zbar)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT ZBAR_LIBRARIES AND ANDROID)
|
||||
find_library(ZBARJNI_LIBRARY NAMES zbarjni)
|
||||
find_library(ICONV_LIBRARY NAMES iconv)
|
||||
if(ZBARJNI_LIBRARY AND ICONV_LIBRARY)
|
||||
set(ZBAR_LIBRARIES ${ZBARJNI_LIBRARY} ${ICONV_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT ZBAR_INCLUDE_DIR)
|
||||
find_path(ZBAR_H_PATH zbar.h)
|
||||
if(ZBAR_H_PATH)
|
||||
set(ZBAR_INCLUDE_DIR "${ZBAR_H_PATH}/zbar")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(ZBAR DEFAULT_MSG ZBAR_LIBRARIES ZBAR_INCLUDE_DIR)
|
||||
message(STATUS "Found zbar libraries ${ZBAR_LIBRARIES}")
|
||||
@@ -49,8 +49,14 @@ Item {
|
||||
property alias fontColor: label.color
|
||||
property bool iconOnTheLeft: true
|
||||
signal clicked()
|
||||
|
||||
height: 25
|
||||
width: checkBoxLayout.width
|
||||
opacity: enabled ? 1 : 0.7
|
||||
|
||||
Keys.onEnterPressed: toggle()
|
||||
Keys.onReturnPressed: Keys.onEnterPressed(event)
|
||||
Keys.onSpacePressed: Keys.onEnterPressed(event)
|
||||
|
||||
function toggle(){
|
||||
if (checkBox.toggleOnClick) {
|
||||
@@ -76,7 +82,7 @@ Item {
|
||||
radius: 3
|
||||
color: checkBox.enabled ? "transparent" : MoneroComponents.Style.inputBoxBackgroundDisabled
|
||||
border.color:
|
||||
if(checkBox.checked){
|
||||
if (checkBox.activeFocus) {
|
||||
return MoneroComponents.Style.inputBorderColorActive;
|
||||
} else {
|
||||
return MoneroComponents.Style.inputBorderColorInActive;
|
||||
|
||||
@@ -52,6 +52,7 @@ Window {
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 480
|
||||
height: 200
|
||||
color: MoneroComponents.Style.middlePanelBackgroundColor
|
||||
|
||||
// Make window draggable
|
||||
MouseArea {
|
||||
@@ -96,7 +97,7 @@ Window {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
themeTransition: false
|
||||
color: "black"
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
66
components/Dialog.qml
Normal file
66
components/Dialog.qml
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2021, 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 "." as MoneroComponents
|
||||
|
||||
Popup {
|
||||
id: dialog
|
||||
|
||||
default property alias content: mainLayout.children
|
||||
property alias title: header.text
|
||||
|
||||
background: Rectangle {
|
||||
border.color: MoneroComponents.Style.blackTheme ? Qt.rgba(255, 255, 255, 0.25) : Qt.rgba(0, 0, 0, 0.25)
|
||||
border.width: 1
|
||||
color: MoneroComponents.Style.blackTheme ? "black" : "white"
|
||||
radius: 10
|
||||
}
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
focus: true
|
||||
padding: 20
|
||||
x: (appWindow.width - width) / 2
|
||||
y: (appWindow.height - height) / 2
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
spacing: dialog.padding
|
||||
|
||||
Text {
|
||||
id: header
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.bold: true
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 18
|
||||
visible: text != ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,30 +30,25 @@ import QtQuick 2.9
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtGraphicalEffects 1.0
|
||||
|
||||
import FontAwesome 1.0
|
||||
|
||||
import "." as MoneroComponents
|
||||
import "./effects/" as MoneroEffects
|
||||
|
||||
Item {
|
||||
id: inlineButton
|
||||
height: parent.height
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
property bool small: false
|
||||
property string shadowPressedColor: "#B32D00"
|
||||
property string shadowReleasedColor: "#FF4304"
|
||||
property string pressedColor: "#FF4304"
|
||||
property string releasedColor: "#FF6C3C"
|
||||
property string icon: ""
|
||||
property string textColor: MoneroComponents.Style.inlineButtonTextColor
|
||||
property int fontSize: small ? 14 : 16
|
||||
property int rectHeight: small ? 24 : 24
|
||||
property int rectHMargin: small ? 16 : 22
|
||||
property alias text: inlineText.text
|
||||
property alias fontPixelSize: inlineText.font.pixelSize
|
||||
property alias fontFamily: inlineText.font.family
|
||||
property bool isFontAwesomeIcon: fontFamily == FontAwesome.fontFamily || fontFamily == FontAwesome.fontFamilySolid
|
||||
property alias buttonColor: rect.color
|
||||
property alias buttonHeight: rect.height
|
||||
|
||||
height: isFontAwesomeIcon ? 30 : 24
|
||||
width: isFontAwesomeIcon ? height : inlineText.width + 16
|
||||
|
||||
signal clicked()
|
||||
|
||||
function doClick() {
|
||||
@@ -64,20 +59,16 @@ Item {
|
||||
|
||||
Rectangle{
|
||||
id: rect
|
||||
anchors.fill: parent
|
||||
color: MoneroComponents.Style.buttonInlineBackgroundColor
|
||||
height: 24
|
||||
width: inlineText.text ? (inlineText.width + 16) : inlineButton.icon ? (inlineImage.width + 16) : rect.height
|
||||
radius: 4
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 4
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: inlineText
|
||||
font.family: MoneroComponents.Style.fontBold.name
|
||||
font.bold: true
|
||||
font.pixelSize: inlineButton.fontSize
|
||||
font.pixelSize: inlineButton.isFontAwesomeIcon ? 22 : inlineButton.small ? 14 : 16
|
||||
color: inlineButton.textColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -90,13 +81,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: inlineImage
|
||||
visible: inlineButton.icon !== ""
|
||||
anchors.centerIn: parent
|
||||
source: inlineButton.icon
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: buttonArea
|
||||
cursorShape: rect.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
|
||||
@@ -131,9 +131,9 @@ Drawer {
|
||||
translationManager.setLanguage(locale_spl[0]);
|
||||
|
||||
// set wizard language settings
|
||||
wizard.language_locale = locale;
|
||||
wizard.language_wallet = wallet_language;
|
||||
wizard.language_language = display_name;
|
||||
persistentSettings.locale = locale;
|
||||
persistentSettings.language = display_name;
|
||||
persistentSettings.language_wallet = wallet_language;
|
||||
|
||||
appWindow.showStatusMessage(qsTr("Language changed."), 3);
|
||||
appWindow.toggleLanguageView();
|
||||
|
||||
@@ -29,11 +29,15 @@
|
||||
import FontAwesome 1.0
|
||||
import QtQuick 2.9
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
|
||||
Item {
|
||||
id: item
|
||||
|
||||
default property alias content: inlineButtons.children
|
||||
|
||||
property alias input: input
|
||||
property alias text: input.text
|
||||
|
||||
@@ -48,13 +52,20 @@ Item {
|
||||
property int placeholderFontSize: 18
|
||||
property string placeholderColor: MoneroComponents.Style.defaultFontColor
|
||||
property real placeholderOpacity: 0.35
|
||||
property real placeholderLeftMargin: {
|
||||
if (placeholderCenter) {
|
||||
return undefined;
|
||||
} else if (inlineIcon.visible) {
|
||||
return inlineIcon.width + inlineIcon.anchors.leftMargin + inputPadding;
|
||||
} else {
|
||||
return inputPadding;
|
||||
}
|
||||
}
|
||||
|
||||
property alias acceptableInput: input.acceptableInput
|
||||
property alias validator: input.validator
|
||||
property alias readOnly : input.readOnly
|
||||
property alias cursorPosition: input.cursorPosition
|
||||
property alias inlineButton: inlineButtonId
|
||||
property alias inlineButtonText: inlineButtonId.text
|
||||
property alias inlineIcon: inlineIcon.visible
|
||||
property bool copyButton: false
|
||||
property alias copyButtonText: copyButtonId.text
|
||||
@@ -71,6 +82,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
property string fontFamily: MoneroComponents.Style.fontRegular.name
|
||||
property int fontSize: 18
|
||||
property bool fontBold: false
|
||||
property alias fontColor: input.color
|
||||
@@ -86,14 +98,16 @@ Item {
|
||||
property alias labelHorizontalAlignment: inputLabel.horizontalAlignment
|
||||
property bool showingHeader: inputLabel.text !== "" || copyButton
|
||||
property int inputHeight: 42
|
||||
property int inputPadding: 10
|
||||
|
||||
signal labelLinkActivated(); // input label, rich text <a> signal
|
||||
signal editingFinished();
|
||||
signal accepted();
|
||||
signal textUpdated();
|
||||
|
||||
height: showingHeader ? (inputLabel.height + inputItem.height + 2) : 42
|
||||
height: showingHeader ? (inputLabel.height + inputItem.height + 2) : inputHeight
|
||||
|
||||
onActiveFocusChanged: activeFocus && input.forceActiveFocus()
|
||||
onTextUpdated: {
|
||||
// check to remove placeholder text when there is content
|
||||
if(item.isEmpty()){
|
||||
@@ -174,7 +188,7 @@ Item {
|
||||
id: inputItem
|
||||
height: inputHeight
|
||||
anchors.top: showingHeader ? inputLabel.bottom : parent.top
|
||||
anchors.topMargin: showingHeader ? 12 : 2
|
||||
anchors.topMargin: showingHeader ? 12 : 0
|
||||
width: parent.width
|
||||
clip: true
|
||||
|
||||
@@ -184,13 +198,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: placeholderCenter ? parent.horizontalCenter : undefined
|
||||
anchors.left: placeholderCenter ? undefined : parent.left
|
||||
anchors.leftMargin: {
|
||||
if(placeholderCenter){
|
||||
return undefined;
|
||||
}
|
||||
else if(inlineIcon.visible){ return 50; }
|
||||
else { return 10; }
|
||||
}
|
||||
anchors.leftMargin: placeholderLeftMargin
|
||||
|
||||
opacity: item.placeholderOpacity
|
||||
color: item.placeholderColor
|
||||
@@ -232,13 +240,18 @@ Item {
|
||||
id: input
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: inlineIcon.visible ? 44 : 0
|
||||
font.family: item.fontFamily
|
||||
font.pixelSize: item.fontSize
|
||||
font.bold: item.fontBold
|
||||
KeyNavigation.backtab: item.KeyNavigation.backtab
|
||||
KeyNavigation.tab: item.KeyNavigation.tab
|
||||
onEditingFinished: item.editingFinished()
|
||||
onAccepted: item.accepted();
|
||||
onTextChanged: item.textUpdated()
|
||||
topPadding: 10
|
||||
bottomPadding: 10
|
||||
leftPadding: inputPadding
|
||||
rightPadding: (inlineButtons.width > 0 ? inlineButtons.width + inlineButtons.spacing : 0) + inputPadding
|
||||
topPadding: inputPadding
|
||||
bottomPadding: inputPadding
|
||||
echoMode: isPasswordHidden() ? TextInput.Password : TextInput.Normal
|
||||
|
||||
MoneroComponents.Label {
|
||||
@@ -260,13 +273,17 @@ Item {
|
||||
onClicked: passwordToggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
id: inlineButtonId
|
||||
visible: item.inlineButtonText ? true : false
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 8
|
||||
RowLayout {
|
||||
id: inlineButtons
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: inputPadding
|
||||
anchors.bottomMargin: inputPadding
|
||||
anchors.rightMargin: inputPadding
|
||||
spacing: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ ColumnLayout {
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
default property alias content: inlineButtons.children
|
||||
|
||||
property alias text: input.text
|
||||
property alias labelText: inputLabel.text
|
||||
property alias labelButtonText: labelButton.text
|
||||
@@ -73,6 +75,7 @@ ColumnLayout {
|
||||
property bool labelButtonVisible: false
|
||||
|
||||
property string fontColor: MoneroComponents.Style.defaultFontColor
|
||||
property string fontFamily: MoneroComponents.Style.fontRegular.name
|
||||
property bool fontBold: false
|
||||
property int fontSize: 16
|
||||
|
||||
@@ -85,15 +88,12 @@ ColumnLayout {
|
||||
property alias addressValidation: input.addressValidation
|
||||
property string backgroundColor: "" // mock
|
||||
|
||||
property alias inlineButton: inlineButtonId
|
||||
property bool inlineButtonVisible: false
|
||||
property alias inlineButton2: inlineButton2Id
|
||||
property bool inlineButton2Visible: false
|
||||
|
||||
signal labelButtonClicked();
|
||||
signal inputLabelLinkActivated();
|
||||
signal editingFinished();
|
||||
|
||||
onActiveFocusChanged: activeFocus && input.forceActiveFocus()
|
||||
|
||||
spacing: 0
|
||||
Rectangle {
|
||||
id: inputLabelRect
|
||||
@@ -159,14 +159,18 @@ ColumnLayout {
|
||||
id: input
|
||||
readOnly: false
|
||||
addressValidation: false
|
||||
KeyNavigation.backtab: item.KeyNavigation.backtab
|
||||
KeyNavigation.priority: KeyNavigation.BeforeItem
|
||||
KeyNavigation.tab: item.KeyNavigation.tab
|
||||
Layout.fillWidth: true
|
||||
|
||||
|
||||
leftPadding: item.inputPaddingLeft
|
||||
rightPadding: item.inputPaddingRight
|
||||
rightPadding: (inlineButtons.width > 0 ? inlineButtons.width + inlineButtons.spacing : 0) + inputPaddingRight
|
||||
topPadding: item.inputPaddingTop
|
||||
bottomPadding: item.inputPaddingBottom
|
||||
|
||||
wrapMode: item.wrapMode
|
||||
font.family: item.fontFamily
|
||||
fontSize: item.fontSize
|
||||
fontBold: item.fontBold
|
||||
fontColor: item.fontColor
|
||||
@@ -198,18 +202,12 @@ ColumnLayout {
|
||||
visible: !item.borderDisabled
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
id: inlineButtonId
|
||||
visible: (inlineButtonId.text || inlineButtonId.icon) && inlineButtonVisible ? true : false
|
||||
RowLayout {
|
||||
id: inlineButtons
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
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
|
||||
anchors.rightMargin: inputPaddingRight
|
||||
spacing: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
207
components/Navbar.qml
Normal file
207
components/Navbar.qml
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) 2014-2021, 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.Layouts 1.1
|
||||
import "." as MoneroComponents
|
||||
|
||||
Rectangle {
|
||||
default property list<MoneroComponents.NavbarItem> items
|
||||
|
||||
color: "transparent"
|
||||
height: grid.height
|
||||
width: grid.width
|
||||
|
||||
GridLayout {
|
||||
id: grid
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
columnSpacing: 0
|
||||
property string fontColorActive: MoneroComponents.Style.blackTheme ? "white" : "white"
|
||||
property string fontColorInActive: MoneroComponents.Style.blackTheme ? "white" : MoneroComponents.Style.dimmedFontColor
|
||||
property int fontSize: 15
|
||||
property bool fontBold: true
|
||||
property var fontFamily: MoneroComponents.Style.fontRegular.name
|
||||
property string borderColor: MoneroComponents.Style.blackTheme ? "#808080" : "#B9B9B9"
|
||||
property int textMargin: {
|
||||
// left-right margins for a given cell
|
||||
if(appWindow.width < 890){
|
||||
return 32;
|
||||
} else {
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// navbar left side border
|
||||
id: navBarLeft
|
||||
Layout.preferredWidth: 2
|
||||
Layout.preferredHeight: 32
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 1
|
||||
height: parent.height - 2
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: 1
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 1
|
||||
color: items.length > 0 && items[0].active ? grid.borderColor : "transparent";
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: items.length
|
||||
|
||||
RowLayout {
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 32
|
||||
color: grid.borderColor
|
||||
visible: index > 0 && items[index - 1].visible
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.minimumWidth: 72
|
||||
Layout.preferredHeight: 32
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
visible: items[index].visible
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.minimumHeight: 30
|
||||
color: items[index].active ? grid.borderColor : "transparent"
|
||||
height: children[0].height
|
||||
width: children[0].width
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: grid.fontFamily
|
||||
font.pixelSize: grid.fontSize
|
||||
font.bold: grid.fontBold
|
||||
leftPadding: grid.textMargin / 2
|
||||
rightPadding: grid.textMargin / 2
|
||||
text: items[index].text
|
||||
color: items[index].active ? grid.fontColorActive : grid.fontColorInActive
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: items[index].selected()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// navbar right side border
|
||||
id: navBarRight
|
||||
Layout.preferredWidth: 2
|
||||
Layout.preferredHeight: 32
|
||||
color: "transparent"
|
||||
rotation: 180
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 1
|
||||
height: parent.height - 2
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: 1
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 1
|
||||
color: items.length > 0 && items[items.length - 1].active ? grid.borderColor : "transparent"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
components/NavbarItem.qml
Normal file
37
components/NavbarItem.qml
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2021, 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
|
||||
|
||||
QtObject {
|
||||
property bool active: false
|
||||
property string text
|
||||
property bool visible: true
|
||||
|
||||
signal selected()
|
||||
}
|
||||
153
components/RemoteNodeDialog.qml
Normal file
153
components/RemoteNodeDialog.qml
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2021, 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 "." as MoneroComponents
|
||||
|
||||
MoneroComponents.Dialog {
|
||||
id: root
|
||||
title: (editMode ? qsTr("Edit remote node") : qsTr("Add remote node")) + translationManager.emptyString
|
||||
|
||||
property var callbackOnSuccess: null
|
||||
property bool editMode: false
|
||||
property bool success: false
|
||||
|
||||
onActiveFocusChanged: activeFocus && remoteNodeAddress.forceActiveFocus()
|
||||
|
||||
function add(callbackOnSuccess) {
|
||||
root.editMode = false;
|
||||
root.callbackOnSuccess = callbackOnSuccess;
|
||||
|
||||
open();
|
||||
}
|
||||
|
||||
function edit(remoteNode, callbackOnSuccess) {
|
||||
const hostPort = remoteNode.address.match(/^(.*?)(?:\:?(\d*))$/);
|
||||
if (hostPort) {
|
||||
remoteNodeAddress.daemonAddrText = hostPort[1];
|
||||
remoteNodeAddress.daemonPortText = hostPort[2];
|
||||
}
|
||||
daemonUsername.text = remoteNode.username;
|
||||
daemonPassword.text = remoteNode.password;
|
||||
setTrustedDaemonCheckBox.checked = remoteNode.trusted;
|
||||
root.callbackOnSuccess = callbackOnSuccess;
|
||||
root.editMode = true;
|
||||
|
||||
open();
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
if (root.success && callbackOnSuccess) {
|
||||
callbackOnSuccess({
|
||||
address: remoteNodeAddress.getAddress(),
|
||||
username: daemonUsername.text,
|
||||
password: daemonPassword.text,
|
||||
trusted: setTrustedDaemonCheckBox.checked,
|
||||
});
|
||||
}
|
||||
|
||||
remoteNodeAddress.daemonAddrText = "";
|
||||
remoteNodeAddress.daemonPortText = "";
|
||||
daemonUsername.text = "";
|
||||
daemonPassword.text = "";
|
||||
setTrustedDaemonCheckBox.checked = false;
|
||||
root.success = false;
|
||||
}
|
||||
|
||||
MoneroComponents.RemoteNodeEdit {
|
||||
id: remoteNodeAddress
|
||||
Layout.fillWidth: true
|
||||
placeholderFontSize: 15
|
||||
|
||||
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
||||
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 32
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: daemonUsername
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 220
|
||||
labelText: qsTr("Daemon username") + translationManager.emptyString
|
||||
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
||||
placeholderFontSize: 15
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: daemonPassword
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 220
|
||||
labelText: qsTr("Daemon password") + translationManager.emptyString
|
||||
placeholderText: qsTr("Password") + translationManager.emptyString
|
||||
password: true
|
||||
placeholderFontSize: 15
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: setTrustedDaemonCheckBox
|
||||
activeFocusOnTab: true
|
||||
text: qsTr("Mark as Trusted Daemon") + translationManager.emptyString
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: parent.spacing
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
activeFocusOnTab: true
|
||||
fontBold: false
|
||||
primary: false
|
||||
text: qsTr("Cancel") + translationManager.emptyString
|
||||
|
||||
onClicked: root.close()
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
activeFocusOnTab: true
|
||||
fontBold: false
|
||||
enabled: remoteNodeAddress.getAddress() != ""
|
||||
text: qsTr("Ok") + translationManager.emptyString
|
||||
|
||||
onClicked: {
|
||||
root.success = true;
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,6 @@ GridLayout {
|
||||
property int labelFontSize: 14
|
||||
|
||||
property string lineEditBackgroundColor: "transparent"
|
||||
property string lineEditBorderColor: MoneroComponents.Style.inputBorderColorInActive
|
||||
property string lineEditFontColor: MoneroComponents.Style.defaultFontColor
|
||||
property bool lineEditFontBold: false
|
||||
property int lineEditFontSize: 15
|
||||
@@ -67,6 +66,8 @@ GridLayout {
|
||||
signal editingFinished()
|
||||
signal textChanged()
|
||||
|
||||
onActiveFocusChanged: activeFocus && daemonAddr.forceActiveFocus()
|
||||
|
||||
function isValid() {
|
||||
return daemonAddr.text.trim().length > 0 && daemonPort.acceptableInput
|
||||
}
|
||||
@@ -92,7 +93,6 @@ GridLayout {
|
||||
placeholderColor: root.placeholderColor
|
||||
placeholderOpacity: root.placeholderOpacity
|
||||
labelFontSize: root.labelFontSize
|
||||
borderColor: lineEditBorderColor
|
||||
backgroundColor: lineEditBackgroundColor
|
||||
fontColor: lineEditFontColor
|
||||
fontBold: lineEditFontBold
|
||||
@@ -115,7 +115,6 @@ GridLayout {
|
||||
placeholderColor: root.placeholderColor
|
||||
placeholderOpacity: root.placeholderOpacity
|
||||
labelFontSize: root.labelFontSize
|
||||
borderColor: lineEditBorderColor
|
||||
backgroundColor: lineEditBackgroundColor
|
||||
fontColor: lineEditFontColor
|
||||
fontBold: lineEditFontBold
|
||||
|
||||
@@ -68,7 +68,7 @@ Item {
|
||||
id: buttonRect
|
||||
anchors.fill: parent
|
||||
radius: 3
|
||||
border.width: parent.focus ? 1 : 0
|
||||
border.width: parent.focus && parent.enabled ? 1 : 0
|
||||
|
||||
state: button.enabled ? "active" : "disabled"
|
||||
Component.onCompleted: state = state
|
||||
@@ -76,7 +76,7 @@ Item {
|
||||
states: [
|
||||
State {
|
||||
name: "hover"
|
||||
when: buttonArea.containsMouse || button.focus
|
||||
when: button.enabled && (buttonArea.containsMouse || button.focus)
|
||||
PropertyChanges {
|
||||
target: buttonRect
|
||||
color: primary
|
||||
|
||||
@@ -45,20 +45,11 @@ Rectangle {
|
||||
radius: 10
|
||||
border.color: MoneroComponents.Style.blackTheme ? Qt.rgba(255, 255, 255, 0.25) : Qt.rgba(0, 0, 0, 0.25)
|
||||
border.width: 1
|
||||
focus: true
|
||||
Keys.enabled: true
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
Keys.onEnterPressed: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
Keys.onReturnPressed: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
}
|
||||
KeyNavigation.tab: doneButton
|
||||
|
||||
Clipboard { id: clipboard }
|
||||
@@ -72,7 +63,6 @@ Rectangle {
|
||||
function open(txid) {
|
||||
root.transactionID = txid;
|
||||
root.visible = true;
|
||||
root.forceActiveFocus();
|
||||
}
|
||||
|
||||
function close() {
|
||||
@@ -156,13 +146,6 @@ Rectangle {
|
||||
text: qsTr("Open folder") + translationManager.emptyString;
|
||||
width: 200
|
||||
KeyNavigation.tab: doneButton
|
||||
Keys.enabled: openFolderButton.visible
|
||||
Keys.onReturnPressed: openFolderButton.onClicked
|
||||
Keys.onEnterPressed: openFolderButton.onClicked
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
onClicked: {
|
||||
oshelper.openContainingFolder(walletManager.urlToLocalPath(saveTxDialog.fileUrl))
|
||||
}
|
||||
@@ -172,15 +155,8 @@ Rectangle {
|
||||
id: doneButton
|
||||
text: qsTr("Done") + translationManager.emptyString;
|
||||
width: 200
|
||||
focus: true
|
||||
focus: root.visible
|
||||
KeyNavigation.tab: openFolderButton
|
||||
Keys.enabled: doneButton.visible
|
||||
Keys.onReturnPressed: doneButton.onClicked
|
||||
Keys.onEnterPressed: doneButton.onClicked
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.rejected()
|
||||
}
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
// 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.Controls 1.4 as QtQuickControls1
|
||||
import QtQuick.Controls 2.2
|
||||
import QtQuick.Layouts 1.1
|
||||
|
||||
import "../components" as MoneroComponents
|
||||
@@ -35,47 +36,29 @@ import FontAwesome 1.0
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property int margins: 25
|
||||
|
||||
x: parent.width/2 - root.width/2
|
||||
y: parent.height/2 - root.height/2
|
||||
// TODO: implement without hardcoding sizes
|
||||
width: 580
|
||||
height: 400
|
||||
width: 590
|
||||
height: layout.height + layout.anchors.margins * 2
|
||||
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
|
||||
focus: true
|
||||
Keys.enabled: true
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.clearFields()
|
||||
root.rejected()
|
||||
}
|
||||
Keys.onEnterPressed: {
|
||||
if (root.state == "default") {
|
||||
root.close()
|
||||
root.accepted()
|
||||
} else if (root.state == "error") {
|
||||
root.close()
|
||||
root.clearFields()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
Keys.onReturnPressed: {
|
||||
if (root.state == "default") {
|
||||
root.close()
|
||||
root.accepted()
|
||||
} else if (root.state == "error") {
|
||||
root.close()
|
||||
root.clearFields()
|
||||
root.rejected()
|
||||
}
|
||||
}
|
||||
KeyNavigation.tab: confirmButton
|
||||
|
||||
property var recipients: []
|
||||
property var transactionAmount: ""
|
||||
property var transactionAddress: ""
|
||||
property var transactionDescription: ""
|
||||
property var transactionFee: ""
|
||||
property var transactionPriority: ""
|
||||
@@ -101,7 +84,7 @@ Rectangle {
|
||||
PropertyChanges { target: bottomMessage; visible: false }
|
||||
PropertyChanges { target: buttons; visible: true }
|
||||
PropertyChanges { target: backButton; visible: true; primary: false }
|
||||
PropertyChanges { target: confirmButton; visible: true }
|
||||
PropertyChanges { target: confirmButton; visible: true; focus: true }
|
||||
}, State {
|
||||
// error message being displayed, show only back button
|
||||
name: "error";
|
||||
@@ -115,7 +98,7 @@ Rectangle {
|
||||
PropertyChanges { target: bottom; visible: true }
|
||||
PropertyChanges { target: bottomMessage; visible: false }
|
||||
PropertyChanges { target: buttons; visible: true }
|
||||
PropertyChanges { target: backButton; visible: true; primary: true }
|
||||
PropertyChanges { target: backButton; visible: true; primary: true; focus: true }
|
||||
PropertyChanges { target: confirmButton; visible: false }
|
||||
}, State {
|
||||
// creating or sending transaction, show tx details and don't show any button
|
||||
@@ -141,7 +124,6 @@ Rectangle {
|
||||
|
||||
//clean previous error message
|
||||
errorText.text = "";
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
|
||||
function close() {
|
||||
@@ -149,8 +131,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
function clearFields() {
|
||||
root.recipients = [];
|
||||
root.transactionAmount = "";
|
||||
root.transactionAddress = "";
|
||||
root.transactionDescription = "";
|
||||
root.transactionFee = "";
|
||||
root.transactionPriority = "";
|
||||
@@ -163,9 +145,12 @@ Rectangle {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: parent.margins
|
||||
spacing: 10
|
||||
anchors.fill: parent
|
||||
anchors.margins: 25
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: 10
|
||||
@@ -203,11 +188,11 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 71
|
||||
|
||||
BusyIndicator {
|
||||
id: txAmountBusyIndicator
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
running: root.transactionAmount == "(all)"
|
||||
QtQuickControls1.BusyIndicator {
|
||||
id: txAmountBusyIndicator
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
running: root.transactionAmount == "(all)"
|
||||
}
|
||||
|
||||
Text {
|
||||
@@ -242,16 +227,10 @@ Rectangle {
|
||||
columnSpacing: 15
|
||||
rowSpacing: 16
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("From") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
Text {
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("From") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -288,57 +267,70 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 15
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("To") + ":" + translationManager.emptyString
|
||||
}
|
||||
Text {
|
||||
font.pixelSize: 15
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("To") + ":" + translationManager.emptyString
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Flickable {
|
||||
id: flickable
|
||||
property int linesInMultipleRecipientsMode: 7
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
Layout.preferredHeight: recipients.length > 1
|
||||
? linesInMultipleRecipientsMode * (recipientsArea.contentHeight / recipientsArea.lineCount)
|
||||
: recipientsArea.contentHeight
|
||||
boundsBehavior: isMac ? Flickable.DragAndOvershootBounds : Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: 15
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
textFormat: Text.RichText
|
||||
wrapMode: Text.Wrap
|
||||
TextArea.flickable: TextArea {
|
||||
id : recipientsArea
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
font.family: MoneroComponents.Style.fontMonoRegular.name
|
||||
font.pixelSize: 14
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
textMargin: 0
|
||||
readOnly: true
|
||||
selectByKeyboard: true
|
||||
selectByMouse: true
|
||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||
textFormat: TextEdit.RichText
|
||||
wrapMode: TextEdit.Wrap
|
||||
text: {
|
||||
if (root.transactionAddress) {
|
||||
const addressBookName = currentWallet ? currentWallet.addressBook.getDescription(root.transactionAddress) : null;
|
||||
var fulladdress = root.transactionAddress;
|
||||
var spacedaddress = fulladdress.match(/.{1,4}/g);
|
||||
var spacedaddress = spacedaddress.join(' ');
|
||||
if (!addressBookName) {
|
||||
return qsTr("Monero address") + "<br>" + spacedaddress + translationManager.emptyString;
|
||||
} else {
|
||||
return FontAwesome.addressBook + " " + addressBookName + "<br>" + spacedaddress;
|
||||
return recipients.map(function (recipient, index) {
|
||||
var addressBookName = null;
|
||||
if (currentWallet) {
|
||||
addressBookName = currentWallet.addressBook.getDescription(recipient.address);
|
||||
}
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
var title;
|
||||
if (addressBookName) {
|
||||
title = FontAwesome.addressBook + " " + addressBookName;
|
||||
} else {
|
||||
title = qsTr("Monero address") + translationManager.emptyString;
|
||||
}
|
||||
if (recipients.length > 1) {
|
||||
title = "%1. %2 - %3 XMR".arg(index + 1).arg(title).arg(recipient.amount);
|
||||
if (persistentSettings.fiatPriceEnabled) {
|
||||
title += " (%1)".arg(showFiatConversion(recipient.amount));
|
||||
}
|
||||
}
|
||||
const spacedaddress = recipient.address.match(/.{1,4}/g).join(' ');
|
||||
return title + "<br>" + spacedaddress;
|
||||
}).join("<br><br>");
|
||||
}
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: recipientsArea.contentHeight > flickable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment : Qt.AlignTop | Qt.AlignLeft
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("Fee") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
Text {
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
text: qsTr("Fee") + ":" + translationManager.emptyString
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -386,7 +378,7 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 50
|
||||
|
||||
BusyIndicator {
|
||||
QtQuickControls1.BusyIndicator {
|
||||
visible: !bottomTextAnimation.running
|
||||
running: !bottomTextAnimation.running
|
||||
scale: .5
|
||||
@@ -422,17 +414,8 @@ Rectangle {
|
||||
id: backButton
|
||||
text: qsTr("Back") + translationManager.emptyString;
|
||||
width: 200
|
||||
focus: false
|
||||
primary: false
|
||||
KeyNavigation.tab: confirmButton
|
||||
Keys.enabled: backButton.visible
|
||||
Keys.onReturnPressed: backButton.onClicked
|
||||
Keys.onEnterPressed: backButton.onClicked
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.clearFields()
|
||||
root.rejected()
|
||||
}
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.clearFields()
|
||||
@@ -445,16 +428,7 @@ Rectangle {
|
||||
text: qsTr("Confirm") + translationManager.emptyString;
|
||||
rightIcon: "qrc:///images/rightArrow.png"
|
||||
width: 200
|
||||
focus: false
|
||||
KeyNavigation.tab: backButton
|
||||
Keys.enabled: confirmButton.visible
|
||||
Keys.onReturnPressed: confirmButton.onClicked
|
||||
Keys.onEnterPressed: confirmButton.onClicked
|
||||
Keys.onEscapePressed: {
|
||||
root.close()
|
||||
root.clearFields()
|
||||
root.rejected()
|
||||
}
|
||||
onClicked: {
|
||||
root.close()
|
||||
root.accepted()
|
||||
|
||||
7
external/CMakeLists.txt
vendored
Normal file
7
external/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
add_library(quirc STATIC
|
||||
quirc/lib/decode.c
|
||||
quirc/lib/identify.c
|
||||
quirc/lib/quirc.c
|
||||
quirc/lib/version_db.c
|
||||
)
|
||||
target_include_directories(quirc PUBLIC quirc/lib)
|
||||
1
external/quirc
vendored
Submodule
1
external/quirc
vendored
Submodule
Submodule external/quirc added at 7e7ab596e4
@@ -403,6 +403,7 @@ Object {
|
||||
property string inbox : "\uf01c"
|
||||
property string indent : "\uf03c"
|
||||
property string industry : "\uf275"
|
||||
property string infinity : "\uf534"
|
||||
property string info : "\uf129"
|
||||
property string infoCircle : "\uf05a"
|
||||
property string inr : "\uf156"
|
||||
|
||||
@@ -105,9 +105,6 @@ Source: "bin\extras\monero-gen-ssl-cert.exe"; DestDir: "{app}"; Flags: ignorever
|
||||
; 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
|
||||
|
||||
; Use a scale factor of 2 for Qt for high-DPI systems, as long as Qt does not handle some such systems adequately
|
||||
Source: "bin\start-high-dpi.bat"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
; Mesa, open-source OpenGL implementation; part of "low-level graphics" support
|
||||
Source: "bin\opengl32sw.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
@@ -170,6 +167,7 @@ Type: files; Name: "{app}\libssp-0.dll"
|
||||
Type: files; Name: "{app}\libhidapi-0.dll"
|
||||
Type: files; Name: "{app}\libeay32.dll"
|
||||
Type: files; Name: "{app}\ssleay32.dll"
|
||||
Type: files; Name: "{app}\start-high-dpi.bat"
|
||||
Type: files; Name: "{group}\Utilities\x (Check Blockchain Folder).lnk"
|
||||
|
||||
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
<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
|
||||
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
|
||||
not be able to move them or you even may not see how many you currently have, but you most probably won't lose any.
|
||||
But do remember that the seed needed to re-create the wallet <b>is</b> critical, however: <b>Never lose your
|
||||
seed!</b></p>
|
||||
|
||||
<p>In the <i>Utilities</i> sub-folder there are several more icons that may help you to solve problems.
|
||||
|
||||
@@ -113,5 +113,5 @@ function capitalize(s){
|
||||
}
|
||||
|
||||
function removeTrailingZeros(value) {
|
||||
return (value + '').replace(/(\.\d*[1-9])0+$/, '$1');
|
||||
return (value + '').replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
|
||||
}
|
||||
|
||||
310
main.qml
310
main.qml
@@ -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 QtQml.Models 2.12
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Window 2.0
|
||||
import QtQuick.Controls 1.1
|
||||
@@ -53,7 +54,10 @@ import "version.js" as Version
|
||||
|
||||
ApplicationWindow {
|
||||
id: appWindow
|
||||
title: "Monero" + (walletName ? " - " + walletName : "")
|
||||
title: "Monero" +
|
||||
(persistentSettings.displayWalletNameInTitleBar && walletName
|
||||
? " - " + walletName
|
||||
: "")
|
||||
minimumWidth: 750
|
||||
minimumHeight: 450
|
||||
|
||||
@@ -140,12 +144,8 @@ ApplicationWindow {
|
||||
|
||||
if(seq === "Ctrl+S") middlePanel.state = "Transfer"
|
||||
else if(seq === "Ctrl+R") middlePanel.state = "Receive"
|
||||
else if(seq === "Ctrl+K") middlePanel.state = "TxKey"
|
||||
else if(seq === "Ctrl+H") middlePanel.state = "History"
|
||||
else if(seq === "Ctrl+B") middlePanel.state = "AddressBook"
|
||||
else if(seq === "Ctrl+M") middlePanel.state = "Mining"
|
||||
else if(seq === "Ctrl+I") middlePanel.state = "Sign"
|
||||
else if(seq === "Ctrl+G") middlePanel.state = "SharedRingDB"
|
||||
else if(seq === "Ctrl+E") middlePanel.state = "Settings"
|
||||
else if(seq === "Ctrl+D") middlePanel.state = "Advanced"
|
||||
else if(seq === "Ctrl+T") middlePanel.state = "Account"
|
||||
@@ -165,11 +165,8 @@ ApplicationWindow {
|
||||
else if(middlePanel.state === "Transfer") middlePanel.state = "AddressBook"
|
||||
else if(middlePanel.state === "AddressBook") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "Mining"
|
||||
else if(middlePanel.state === "Mining") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "SharedRingDB"
|
||||
else if(middlePanel.state === "SharedRingDB") middlePanel.state = "Sign"
|
||||
else if(middlePanel.state === "Sign") middlePanel.state = "Settings"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "Advanced"
|
||||
else if(middlePanel.state === "Advanced") middlePanel.state = "Settings"
|
||||
} else if(seq === "Ctrl+Shift+Backtab" || seq === "Alt+Shift+Backtab") {
|
||||
/*
|
||||
if(middlePanel.state === "Settings") middlePanel.state = "Sign"
|
||||
@@ -181,11 +178,8 @@ ApplicationWindow {
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "Transfer"
|
||||
*/
|
||||
if(middlePanel.state === "Settings") middlePanel.state = "Sign"
|
||||
else if(middlePanel.state === "Sign") middlePanel.state = "SharedRingDB"
|
||||
else if(middlePanel.state === "SharedRingDB") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "Mining"
|
||||
else if(middlePanel.state === "Mining") middlePanel.state = "History"
|
||||
if(middlePanel.state === "Settings") middlePanel.state = "Advanced"
|
||||
else if(middlePanel.state === "Advanced") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "AddressBook"
|
||||
else if(middlePanel.state === "AddressBook") middlePanel.state = "Transfer"
|
||||
@@ -233,12 +227,6 @@ ApplicationWindow {
|
||||
else
|
||||
walletManager.setLogLevel(persistentSettings.logLevel)
|
||||
|
||||
// setup language
|
||||
var locale = persistentSettings.locale
|
||||
if (locale !== "") {
|
||||
translationManager.setLanguage(locale.split("_")[0]);
|
||||
}
|
||||
|
||||
// Reload transfer page with translations enabled
|
||||
middlePanel.transferView.onPageCompleted();
|
||||
|
||||
@@ -377,13 +365,13 @@ ApplicationWindow {
|
||||
console.log("Recovering from seed: ", persistentSettings.is_recovering)
|
||||
console.log("restore Height", persistentSettings.restore_height)
|
||||
|
||||
// Use saved daemon rpc login settings
|
||||
currentWallet.setDaemonLogin(persistentSettings.daemonUsername, persistentSettings.daemonPassword)
|
||||
|
||||
if(persistentSettings.useRemoteNode)
|
||||
currentDaemonAddress = persistentSettings.remoteNodeAddress
|
||||
else
|
||||
currentDaemonAddress = localDaemonAddress
|
||||
if (persistentSettings.useRemoteNode) {
|
||||
const remoteNode = remoteNodesModel.currentRemoteNode();
|
||||
currentDaemonAddress = remoteNode.address;
|
||||
currentWallet.setDaemonLogin(remoteNode.username, remoteNode.password);
|
||||
} else {
|
||||
currentDaemonAddress = localDaemonAddress;
|
||||
}
|
||||
|
||||
console.log("initializing with daemon address: ", currentDaemonAddress)
|
||||
currentWallet.initAsync(
|
||||
@@ -400,7 +388,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
function isTrustedDaemon() {
|
||||
return !persistentSettings.useRemoteNode || persistentSettings.is_trusted_daemon;
|
||||
return !persistentSettings.useRemoteNode || remoteNodesModel.currentRemoteNode().trusted;
|
||||
}
|
||||
|
||||
function usefulName(path) {
|
||||
@@ -502,7 +490,7 @@ ApplicationWindow {
|
||||
walletInitialized = true
|
||||
|
||||
// check if daemon was already mining and add mining logo if true
|
||||
middlePanel.miningView.update();
|
||||
middlePanel.advancedView.miningView.update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,7 +617,9 @@ ApplicationWindow {
|
||||
|
||||
const callback = function() {
|
||||
persistentSettings.useRemoteNode = true;
|
||||
currentDaemonAddress = persistentSettings.remoteNodeAddress;
|
||||
const remoteNode = remoteNodesModel.currentRemoteNode();
|
||||
currentDaemonAddress = remoteNode.address;
|
||||
currentWallet.setDaemonLogin(remoteNode.username, remoteNode.password);
|
||||
currentWallet.initAsync(
|
||||
currentDaemonAddress,
|
||||
isTrustedDaemon(),
|
||||
@@ -655,6 +645,7 @@ ApplicationWindow {
|
||||
console.log("disconnecting remote node");
|
||||
persistentSettings.useRemoteNode = false;
|
||||
currentDaemonAddress = localDaemonAddress
|
||||
currentWallet.setDaemonLogin("", "");
|
||||
currentWallet.initAsync(
|
||||
currentDaemonAddress,
|
||||
isTrustedDaemon(),
|
||||
@@ -727,7 +718,7 @@ ApplicationWindow {
|
||||
|
||||
const noSync = appWindow.walletMode === 0;
|
||||
const bootstrapNodeAddress = persistentSettings.walletMode < 2 ? "auto" : persistentSettings.bootstrapNodeAddress
|
||||
daemonManager.start(flags, persistentSettings.nettype, persistentSettings.blockchainDataDir, bootstrapNodeAddress, noSync);
|
||||
daemonManager.start(flags, persistentSettings.nettype, persistentSettings.blockchainDataDir, bootstrapNodeAddress, noSync, persistentSettings.pruneBlockchain);
|
||||
}
|
||||
|
||||
function stopDaemon(callback, splash){
|
||||
@@ -823,7 +814,7 @@ ApplicationWindow {
|
||||
return false;
|
||||
}
|
||||
|
||||
function onTransactionCreated(pendingTransaction,address,paymentId,mixinCount){
|
||||
function onTransactionCreated(pendingTransaction, addresses, paymentId, mixinCount) {
|
||||
console.log("Transaction created");
|
||||
txConfirmationPopup.bottomText.text = "";
|
||||
transaction = pendingTransaction;
|
||||
@@ -855,48 +846,49 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayAmountTotal(recipients) {
|
||||
const amounts = recipients.map(function (recipient) {
|
||||
return recipient.amount;
|
||||
});
|
||||
const total = walletManager.amountsSumFromStrings(amounts);
|
||||
return Utils.removeTrailingZeros(walletManager.displayAmount(total));
|
||||
}
|
||||
|
||||
// called on "transfer"
|
||||
function handlePayment(address, paymentId, amount, mixinCount, priority, description, createFile) {
|
||||
function handlePayment(recipients, paymentId, mixinCount, priority, description, createFile) {
|
||||
console.log("Creating transaction: ")
|
||||
console.log("\taddress: ", address,
|
||||
console.log("\trecipients: ", recipients,
|
||||
", payment_id: ", paymentId,
|
||||
", amount: ", amount,
|
||||
", mixins: ", mixinCount,
|
||||
", priority: ", priority,
|
||||
", description: ", description);
|
||||
txConfirmationPopup.bottomTextAnimation.running = false
|
||||
txConfirmationPopup.bottomText.text = qsTr("Creating transaction...") + translationManager.emptyString;
|
||||
txConfirmationPopup.transactionAddress = address;
|
||||
txConfirmationPopup.transactionAmount = Utils.removeTrailingZeros(amount);
|
||||
txConfirmationPopup.transactionPriority = priority;
|
||||
txConfirmationPopup.transactionDescription = description;
|
||||
|
||||
// validate amount;
|
||||
if (amount !== "(all)") {
|
||||
var amountxmr = walletManager.amountFromString(amount);
|
||||
console.log("integer amount: ", amountxmr);
|
||||
console.log("integer unlocked", currentWallet.unlockedBalance())
|
||||
if (amountxmr <= 0) {
|
||||
txConfirmationPopup.errorText.text = qsTr("Amount is wrong: expected number from %1 to %2")
|
||||
.arg(walletManager.displayAmount(0))
|
||||
.arg(walletManager.displayAmount(currentWallet.unlockedBalance()))
|
||||
+ translationManager.emptyString;
|
||||
return;
|
||||
} else if (amountxmr > currentWallet.unlockedBalance()) {
|
||||
txConfirmationPopup.errorText.text = qsTr("Insufficient funds. Unlocked balance: %1")
|
||||
.arg(walletManager.displayAmount(currentWallet.unlockedBalance()))
|
||||
+ translationManager.emptyString;
|
||||
return;
|
||||
}
|
||||
const recipientAll = recipients.find(function (recipient) {
|
||||
return recipient.amount == "(all)";
|
||||
});
|
||||
if (recipientAll && recipients.length > 1) {
|
||||
throw "Sending all requires one destination address";
|
||||
}
|
||||
|
||||
txConfirmationPopup.bottomTextAnimation.running = false;
|
||||
txConfirmationPopup.bottomText.text = qsTr("Creating transaction...") + translationManager.emptyString;
|
||||
txConfirmationPopup.recipients = recipients;
|
||||
txConfirmationPopup.transactionAmount = recipientAll ? "(all)" : getDisplayAmountTotal(recipients);
|
||||
txConfirmationPopup.transactionPriority = priority;
|
||||
txConfirmationPopup.transactionDescription = description;
|
||||
txConfirmationPopup.open();
|
||||
|
||||
if (amount === "(all)")
|
||||
currentWallet.createTransactionAllAsync(address, paymentId, mixinCount, priority);
|
||||
else
|
||||
currentWallet.createTransactionAsync(address, paymentId, amountxmr, mixinCount, priority);
|
||||
if (recipientAll) {
|
||||
currentWallet.createTransactionAllAsync(recipientAll.address, paymentId, mixinCount, priority);
|
||||
} else {
|
||||
const addresses = recipients.map(function (recipient) {
|
||||
return recipient.address;
|
||||
});
|
||||
const amountsxmr = recipients.map(function (recipient) {
|
||||
return recipient.amount;
|
||||
});
|
||||
currentWallet.createTransactionAsync(addresses, paymentId, amountsxmr, mixinCount, priority);
|
||||
}
|
||||
}
|
||||
|
||||
//Choose where to save transaction
|
||||
@@ -958,10 +950,12 @@ ApplicationWindow {
|
||||
// Store to file
|
||||
transaction.setFilename(path);
|
||||
}
|
||||
appWindow.showProcessingSplash(qsTr("Sending transaction ..."));
|
||||
currentWallet.commitTransactionAsync(transaction);
|
||||
}
|
||||
|
||||
function onTransactionCommitted(success, transaction, txid) {
|
||||
hideProcessingSplash();
|
||||
if (!success) {
|
||||
console.log("Error committing transaction: " + transaction.errorString);
|
||||
informationPopup.title = qsTr("Error") + translationManager.emptyString
|
||||
@@ -1127,11 +1121,14 @@ ApplicationWindow {
|
||||
|
||||
objectName: "appWindow"
|
||||
visible: true
|
||||
width: screenWidth > 980 ? 980 : 800
|
||||
height: screenHeight > maxWindowHeight ? maxWindowHeight : 700
|
||||
width: Screen.desktopAvailableWidth > 980
|
||||
? 980
|
||||
: Math.min(Screen.desktopAvailableWidth, 800)
|
||||
height: Screen.desktopAvailableHeight > maxWindowHeight
|
||||
? maxWindowHeight
|
||||
: Math.min(Screen.desktopAvailableHeight, 700)
|
||||
color: MoneroComponents.Style.appWindowBackgroundColor
|
||||
flags: persistentSettings.customDecorations ? Windows.flagsCustomDecorations : Windows.flags
|
||||
onWidthChanged: x -= 0
|
||||
|
||||
Timer {
|
||||
id: fiatPriceTimer
|
||||
@@ -1154,7 +1151,7 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
var key = currency === "xmreur" ? "XXMRZEUR" : "XXMRZUSD";
|
||||
var ticker = resp.result[key]["o"];
|
||||
var ticker = resp.result[key]["c"][0];
|
||||
return ticker;
|
||||
} else if(url.startsWith("https://api.coingecko.com/api/v3/")){
|
||||
var key = currency === "xmreur" ? "eur" : "usd";
|
||||
@@ -1288,8 +1285,10 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
x = (Screen.width - width) / 2
|
||||
y = (Screen.height - maxWindowHeight) / 2
|
||||
x = (Screen.desktopAvailableWidth - width) / 2;
|
||||
y = (Screen.desktopAvailableHeight - height) / 2;
|
||||
|
||||
translationManager.setLanguage(persistentSettings.locale.split("_")[0]);
|
||||
|
||||
applyWalletMode(persistentSettings.walletMode);
|
||||
|
||||
@@ -1336,6 +1335,27 @@ ApplicationWindow {
|
||||
appWindow.fiatApiRefresh();
|
||||
appWindow.fiatTimerStart();
|
||||
}
|
||||
|
||||
if (persistentSettings.askDesktopShortcut && !persistentSettings.portable) {
|
||||
persistentSettings.askDesktopShortcut = false;
|
||||
|
||||
if (isTails) {
|
||||
oshelper.createDesktopEntry();
|
||||
} else if (isLinux) {
|
||||
confirmationDialog.title = qsTr("Desktop entry") + translationManager.emptyString;
|
||||
confirmationDialog.text = qsTr("Would you like to register Monero GUI Desktop entry?") + translationManager.emptyString;
|
||||
confirmationDialog.icon = StandardIcon.Question;
|
||||
confirmationDialog.cancelText = qsTr("No") + translationManager.emptyString;
|
||||
confirmationDialog.okText = qsTr("Yes") + translationManager.emptyString;
|
||||
confirmationDialog.onAcceptedCallback = function() {
|
||||
oshelper.createDesktopEntry();
|
||||
};
|
||||
confirmationDialog.onRejectedCallback = null;
|
||||
confirmationDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
remoteNodesModel.initialize();
|
||||
}
|
||||
|
||||
MoneroSettings {
|
||||
@@ -1346,35 +1366,49 @@ ApplicationWindow {
|
||||
return "";
|
||||
}
|
||||
|
||||
property string language
|
||||
property string locale
|
||||
property bool askDesktopShortcut: isLinux
|
||||
property string language: 'English (US)'
|
||||
property string language_wallet: 'English'
|
||||
property string locale: 'en_US'
|
||||
property string account_name
|
||||
property string wallet_path
|
||||
property bool allow_background_mining : false
|
||||
property bool miningIgnoreBattery : true
|
||||
property var nettype: NetworkType.MAINNET
|
||||
property int restore_height : 0
|
||||
property bool is_trusted_daemon : false
|
||||
property bool is_trusted_daemon : false // TODO: drop after v0.17.2.0 release
|
||||
property bool is_recovering : false
|
||||
property bool is_recovering_from_device : false
|
||||
property bool customDecorations : true
|
||||
property string daemonFlags
|
||||
property int logLevel: 0
|
||||
property string logCategories: ""
|
||||
property string daemonUsername: ""
|
||||
property string daemonPassword: ""
|
||||
property string daemonUsername: "" // TODO: drop after v0.17.2.0 release
|
||||
property string daemonPassword: "" // TODO: drop after v0.17.2.0 release
|
||||
property bool transferShowAdvanced: false
|
||||
property bool receiveShowAdvanced: false
|
||||
property bool historyShowAdvanced: false
|
||||
property bool historyHumanDates: true
|
||||
property string blockchainDataDir: ""
|
||||
property bool useRemoteNode: false
|
||||
property string remoteNodeAddress: ""
|
||||
property string remoteNodeAddress: "" // TODO: drop after v0.17.2.0 release
|
||||
property string remoteNodesSerialized: JSON.stringify({
|
||||
selected: 0,
|
||||
nodes: remoteNodeAddress != ""
|
||||
? [{
|
||||
address: remoteNodeAddress,
|
||||
username: daemonUsername,
|
||||
password: daemonPassword,
|
||||
trusted: is_trusted_daemon,
|
||||
}]
|
||||
: [],
|
||||
})
|
||||
property string bootstrapNodeAddress: ""
|
||||
property bool segregatePreForkOutputs: true
|
||||
property bool keyReuseMitigation2: true
|
||||
property int segregationHeight: 0
|
||||
property int kdfRounds: 1
|
||||
property bool displayWalletNameInTitleBar: true
|
||||
property bool hideBalance: false
|
||||
property bool askPasswordBeforeSending: true
|
||||
property bool lockOnUserInActivity: true
|
||||
@@ -1384,6 +1418,7 @@ ApplicationWindow {
|
||||
property bool checkForUpdates: true
|
||||
property bool autosave: true
|
||||
property int autosaveMinutes: 10
|
||||
property bool pruneBlockchain: false
|
||||
|
||||
property bool fiatPriceEnabled: false
|
||||
property bool fiatPriceToggle: false
|
||||
@@ -1414,6 +1449,88 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: remoteNodesModel
|
||||
|
||||
property int selected: 0
|
||||
|
||||
signal store()
|
||||
|
||||
function initialize() {
|
||||
try {
|
||||
const remoteNodes = JSON.parse(persistentSettings.remoteNodesSerialized);
|
||||
for (var index = 0; index < remoteNodes.nodes.length; ++index) {
|
||||
const remoteNode = remoteNodes.nodes[index];
|
||||
remoteNodesModel.append(remoteNode);
|
||||
}
|
||||
selected = remoteNodes.selected % remoteNodesModel.count || 0;
|
||||
} catch (e) {
|
||||
console.error('failed to parse remoteNodesSerialized', e);
|
||||
}
|
||||
|
||||
store.connect(function() {
|
||||
var remoteNodes = [];
|
||||
for (var index = 0; index < remoteNodesModel.count; ++index) {
|
||||
remoteNodes.push(remoteNodesModel.get(index));
|
||||
}
|
||||
persistentSettings.remoteNodesSerialized = JSON.stringify({
|
||||
selected: selected,
|
||||
nodes: remoteNodes
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function appendIfNotExists(newRemoteNode) {
|
||||
for (var index = 0; index < remoteNodesModel.count; ++index) {
|
||||
const remoteNode = remoteNodesModel.get(index);
|
||||
if (remoteNode.address == newRemoteNode.address &&
|
||||
remoteNode.username == newRemoteNode.username &&
|
||||
remoteNode.password == newRemoteNode.password &&
|
||||
remoteNode.trusted == newRemoteNode.trusted) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
remoteNodesModel.append(newRemoteNode);
|
||||
return remoteNodesModel.count - 1;
|
||||
}
|
||||
|
||||
function applyRemoteNode(index) {
|
||||
selected = index;
|
||||
const remoteNode = currentRemoteNode();
|
||||
persistentSettings.useRemoteNode = true;
|
||||
if (currentWallet) {
|
||||
currentWallet.setDaemonLogin(remoteNode.username, remoteNode.password);
|
||||
currentWallet.setTrustedDaemon(remoteNode.trusted);
|
||||
appWindow.connectRemoteNode();
|
||||
}
|
||||
}
|
||||
|
||||
function currentRemoteNode() {
|
||||
if (selected < remoteNodesModel.count) {
|
||||
return remoteNodesModel.get(selected);
|
||||
}
|
||||
return {
|
||||
address: "",
|
||||
username: "",
|
||||
password: "",
|
||||
trusted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function removeSelectNextIfNeeded(index) {
|
||||
remoteNodesModel.remove(index);
|
||||
if (selected == index) {
|
||||
applyRemoteNode(selected % remoteNodesModel.count || 0);
|
||||
} else if (selected > index) {
|
||||
selected = selected - 1;
|
||||
}
|
||||
}
|
||||
|
||||
onCountChanged: store()
|
||||
onDataChanged: store()
|
||||
onSelectedChanged: store()
|
||||
}
|
||||
|
||||
// Information dialog
|
||||
StandardDialog {
|
||||
// dynamically change onclose handler
|
||||
@@ -1494,6 +1611,10 @@ ApplicationWindow {
|
||||
y: (parent.height - height) / 2
|
||||
}
|
||||
|
||||
MoneroComponents.RemoteNodeDialog {
|
||||
id: remoteNodeDialog
|
||||
}
|
||||
|
||||
// Choose blockchain folder
|
||||
FileDialog {
|
||||
id: blockchainFileDialog
|
||||
@@ -1665,18 +1786,6 @@ ApplicationWindow {
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onTxkeyClicked: {
|
||||
middlePanel.state = "TxKey";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onSharedringdbClicked: {
|
||||
middlePanel.state = "SharedRingDB";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onHistoryClicked: {
|
||||
middlePanel.state = "History";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
@@ -1689,14 +1798,8 @@ ApplicationWindow {
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onMiningClicked: {
|
||||
middlePanel.state = "Mining";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
onSignClicked: {
|
||||
middlePanel.state = "Sign";
|
||||
onAdvancedClicked: {
|
||||
middlePanel.state = "Advanced";
|
||||
middlePanel.flickable.contentY = 0;
|
||||
updateBalance();
|
||||
}
|
||||
@@ -1739,7 +1842,9 @@ ApplicationWindow {
|
||||
anchors.fill: blurredArea
|
||||
source: blurredArea
|
||||
radius: 64
|
||||
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible || txConfirmationPopup.visible || successfulTxPopup.visible
|
||||
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible ||
|
||||
devicePassphraseDialog.visible || txConfirmationPopup.visible || successfulTxPopup.visible ||
|
||||
remoteNodeDialog.visible
|
||||
}
|
||||
|
||||
|
||||
@@ -1792,7 +1897,7 @@ ApplicationWindow {
|
||||
TitleBar {
|
||||
id: titleBar
|
||||
visible: persistentSettings.customDecorations && middlePanel.state !== "Merchant"
|
||||
walletName: appWindow.walletName
|
||||
walletName: persistentSettings.displayWalletNameInTitleBar ? appWindow.walletName : ""
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
onCloseClicked: appWindow.close();
|
||||
@@ -1845,11 +1950,6 @@ ApplicationWindow {
|
||||
function toggleLanguageView(){
|
||||
languageSidebar.isOpened ? languageSidebar.close() : languageSidebar.open();
|
||||
resetLanguageFields()
|
||||
// update after changing language from settings page
|
||||
if (persistentSettings.language != wizard.language_language) {
|
||||
persistentSettings.language = wizard.language_language
|
||||
persistentSettings.locale = wizard.language_locale
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
@@ -1924,12 +2024,13 @@ ApplicationWindow {
|
||||
return;
|
||||
}
|
||||
|
||||
const simpleModeFlags = "--enable-dns-blocklist --out-peers 16";
|
||||
if (appWindow.daemonRunning) {
|
||||
appWindow.stopDaemon(function() {
|
||||
appWindow.startDaemon("")
|
||||
appWindow.startDaemon(simpleModeFlags)
|
||||
});
|
||||
} else {
|
||||
appWindow.startDaemon("");
|
||||
appWindow.startDaemon(simpleModeFlags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2133,6 +2234,7 @@ ApplicationWindow {
|
||||
|
||||
passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
|
||||
if (inputDialogVisible) inputDialog.close()
|
||||
remoteNodeDialog.close();
|
||||
passwordDialog.open();
|
||||
}
|
||||
|
||||
|
||||
2
monero
2
monero
Submodule monero updated: f690e4984d...f6e63ef260
@@ -190,7 +190,7 @@ Rectangle {
|
||||
height: subaddressAccountListRow.subaddressAccountListItemHeight
|
||||
width: parent ? parent.width : undefined
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
color: itemMouseArea.containsMouse || index === currentAccountIndex ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent"
|
||||
|
||||
Rectangle {
|
||||
color: MoneroComponents.Style.appWindowBorderColor
|
||||
@@ -226,7 +226,7 @@ Rectangle {
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: nameLabel
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
color: index === currentAccountIndex ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: idLabel.right
|
||||
anchors.leftMargin: 6
|
||||
@@ -276,11 +276,10 @@ Rectangle {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: itemMouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: tableItem2.color = MoneroComponents.Style.titleBarButtonHoverColor
|
||||
onExited: tableItem2.color = "transparent"
|
||||
onClicked: {
|
||||
appWindow.currentWallet.switchSubaddressAccount(index);
|
||||
if (selectAndSend)
|
||||
|
||||
@@ -132,13 +132,13 @@ Rectangle {
|
||||
delegate: Rectangle {
|
||||
id: tableItem2
|
||||
height: addressBookListRow.addressBookListItemHeight
|
||||
width: parent.width
|
||||
width: parent ? parent.width : undefined
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
|
||||
function doSend() {
|
||||
console.log("Sending to: ", address +" "+ paymentId);
|
||||
middlePanel.sendTo(address, paymentId, description);
|
||||
middlePanel.sendTo(address, paymentId);
|
||||
leftPanel.selectItem(middlePanel.state)
|
||||
}
|
||||
|
||||
@@ -314,16 +314,16 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
inlineButton.text: FontAwesome.qrcode
|
||||
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(root.updateFromQrCode)
|
||||
MoneroComponents.InlineButton {
|
||||
buttonColor: MoneroComponents.Style.orange
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
text: FontAwesome.qrcode
|
||||
visible : appWindow.qrScannerEnabled && !addressLine.text
|
||||
onClicked: {
|
||||
cameraUi.state = "Capture"
|
||||
cameraUi.qrcode_decoded.connect(root.updateFromQrCode)
|
||||
}
|
||||
}
|
||||
inlineButtonVisible : appWindow.qrScannerEnabled && !addressLine.text
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
|
||||
148
pages/Advanced.qml
Normal file
148
pages/Advanced.qml
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) 2021, 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 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import "../components" as MoneroComponents
|
||||
import "."
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 900
|
||||
spacing: 0
|
||||
property int panelHeight: 900
|
||||
property alias miningView: stateView.miningView
|
||||
property alias state: stateView.state
|
||||
|
||||
MoneroComponents.Navbar {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: height
|
||||
Layout.bottomMargin: height
|
||||
|
||||
MoneroComponents.NavbarItem {
|
||||
active: state == "Mining"
|
||||
text: qsTr("Mining") + translationManager.emptyString
|
||||
onSelected: state = "Mining"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: state == "Prove"
|
||||
text: qsTr("Prove/check") + translationManager.emptyString
|
||||
onSelected: state = "Prove"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: state == "SharedRingDB"
|
||||
text: qsTr("Shared RingDB") + translationManager.emptyString
|
||||
onSelected: state = "SharedRingDB"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: state == "Sign"
|
||||
text: qsTr("Sign/verify") + translationManager.emptyString
|
||||
onSelected: state = "Sign"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
id: stateView
|
||||
property Item currentView
|
||||
property Item previousView
|
||||
property Mining miningView: Mining { }
|
||||
property TxKey prooveView: TxKey { }
|
||||
property SharedRingDB sharedRingDBView: SharedRingDB { }
|
||||
property Sign signView: Sign { }
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: panelHeight
|
||||
color: "transparent"
|
||||
state: "Mining"
|
||||
|
||||
onCurrentViewChanged: {
|
||||
if (previousView) {
|
||||
if (typeof previousView.onPageClosed === "function") {
|
||||
previousView.onPageClosed();
|
||||
}
|
||||
}
|
||||
previousView = currentView
|
||||
if (currentView) {
|
||||
stackView.replace(currentView)
|
||||
if (typeof currentView.onPageCompleted === "function") {
|
||||
currentView.onPageCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "Mining"
|
||||
PropertyChanges { target: stateView; currentView: stateView.miningView }
|
||||
PropertyChanges { target: root; panelHeight: stateView.miningView.miningHeight + 140 }
|
||||
}, State {
|
||||
name: "Prove"
|
||||
PropertyChanges { target: stateView; currentView: stateView.prooveView }
|
||||
PropertyChanges { target: root; panelHeight: stateView.prooveView.txkeyHeight + 140 }
|
||||
}, State {
|
||||
name: "SharedRingDB"
|
||||
PropertyChanges { target: stateView; currentView: stateView.sharedRingDBView }
|
||||
PropertyChanges { target: root; panelHeight: stateView.sharedRingDBView.panelHeight + 140 }
|
||||
}, State {
|
||||
name: "Sign"
|
||||
PropertyChanges { target: stateView; currentView: stateView.signView }
|
||||
PropertyChanges { target: root; panelHeight: stateView.signView.signHeight + 140 }
|
||||
}
|
||||
]
|
||||
|
||||
StackView {
|
||||
id: stackView
|
||||
initialItem: stateView.miningView
|
||||
anchors.fill: parent
|
||||
clip: false // otherwise animation will affect left panel
|
||||
|
||||
delegate: StackViewDelegate {
|
||||
pushTransition: StackViewTransition {
|
||||
PropertyAnimation {
|
||||
target: enterItem
|
||||
property: "x"
|
||||
from: 0 - target.width
|
||||
to: 0
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
PropertyAnimation {
|
||||
target: exitItem
|
||||
property: "x"
|
||||
from: 0
|
||||
to: target.width
|
||||
duration: 300
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ Rectangle {
|
||||
image: "qrc:///images/whiteDropIndicator.png"
|
||||
fontAwesomeFallbackIcon: FontAwesome.arrowDown
|
||||
fontAwesomeFallbackSize: 14
|
||||
rotation: sortAndFilter.collapsed ? 0 : 180
|
||||
rotation: sortAndFilter.collapsed ? 180 : 0
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
|
||||
MouseArea {
|
||||
@@ -771,7 +771,6 @@ Rectangle {
|
||||
return qsTr("Waiting confirmation...") + translationManager.emptyString;
|
||||
}
|
||||
if (address) {
|
||||
const addressBookName = currentWallet ? currentWallet.addressBook.getDescription(address) : null;
|
||||
return (addressBookName ? FontAwesome.addressBook + " " + addressBookName : TxUtils.addressTruncate(address, 8));
|
||||
}
|
||||
if (amount != 0) {
|
||||
@@ -780,8 +779,6 @@ Rectangle {
|
||||
return qsTr("My wallet") + translationManager.emptyString;
|
||||
}
|
||||
} else {
|
||||
const receivingAddress = currentWallet ? currentWallet.address(subaddrAccount, subaddrIndex) : null;
|
||||
const receivingAddressLabel = currentWallet ? appWindow.currentWallet.getSubaddressLabel(subaddrAccount, subaddrIndex) : null;
|
||||
if (receivingAddress) {
|
||||
if (subaddrIndex == 0) {
|
||||
return qsTr("Address") + " #0" + " (" + qsTr("Primary address") + ")" + translationManager.emptyString;
|
||||
@@ -1417,6 +1414,12 @@ Rectangle {
|
||||
txs.push(item);
|
||||
} else if(item.address !== "" && item.address.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
|
||||
txs.push(item);
|
||||
} else if(item.receivingAddress !== "" && item.receivingAddress.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
|
||||
txs.push(item);
|
||||
} else if(item.receivingAddressLabel !== "" && item.receivingAddressLabel.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
|
||||
txs.push(item);
|
||||
} else if(item.addressBookName !== "" && item.addressBookName.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
|
||||
txs.push(item);
|
||||
} 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) {
|
||||
@@ -1522,8 +1525,16 @@ Rectangle {
|
||||
|
||||
var tx_note = currentWallet.getUserNote(hash);
|
||||
var address = "";
|
||||
if(isout) {
|
||||
var addressBookName = "";
|
||||
var receivingAddress = "";
|
||||
var receivingAddressLabel = "";
|
||||
|
||||
if (isout) {
|
||||
address = TxUtils.destinationsToAddress(destinations);
|
||||
addressBookName = currentWallet ? currentWallet.addressBook.getDescription(address) : null;
|
||||
} else {
|
||||
receivingAddress = currentWallet ? currentWallet.address(subaddrAccount, subaddrIndex) : null;
|
||||
receivingAddressLabel = currentWallet ? appWindow.currentWallet.getSubaddressLabel(subaddrAccount, subaddrIndex) : null;
|
||||
}
|
||||
|
||||
if (isout)
|
||||
@@ -1541,6 +1552,7 @@ Rectangle {
|
||||
"hash": hash,
|
||||
"paymentId": paymentId,
|
||||
"address": address,
|
||||
"addressBookName": addressBookName,
|
||||
"destinations": destinations,
|
||||
"tx_note": tx_note,
|
||||
"dateHuman": dateHuman,
|
||||
@@ -1551,6 +1563,8 @@ Rectangle {
|
||||
"fee": fee,
|
||||
"confirmations": confirmations,
|
||||
"confirmationsRequired": confirmationsRequired,
|
||||
"receivingAddress": receivingAddress,
|
||||
"receivingAddressLabel": receivingAddressLabel,
|
||||
"subaddrAccount": subaddrAccount,
|
||||
"subaddrIndex": subaddrIndex
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ Rectangle {
|
||||
id: mainLayout
|
||||
Layout.fillWidth: true
|
||||
anchors.margins: 20
|
||||
anchors.topMargin: 40
|
||||
anchors.topMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -107,7 +107,7 @@ Rectangle {
|
||||
height: subaddressListRow.subaddressListItemHeight
|
||||
width: parent ? parent.width : undefined
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
color: itemMouseArea.containsMouse || index === appWindow.current_subaddress_table_index ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent"
|
||||
|
||||
Rectangle{
|
||||
anchors.right: parent.right
|
||||
@@ -143,7 +143,7 @@ Rectangle {
|
||||
|
||||
MoneroComponents.Label {
|
||||
id: nameLabel
|
||||
color: MoneroComponents.Style.dimmedFontColor
|
||||
color: index === appWindow.current_subaddress_table_index ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: idLabel.right
|
||||
anchors.leftMargin: 6
|
||||
@@ -167,11 +167,10 @@ Rectangle {
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: itemMouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: tableItem2.color = MoneroComponents.Style.titleBarButtonHoverColor
|
||||
onExited: tableItem2.color = "transparent"
|
||||
onClicked: subaddressListView.currentIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -299,18 +298,6 @@ Rectangle {
|
||||
onClicked: qrFileDialog.open()
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
Layout.preferredWidth: 220
|
||||
small: true
|
||||
text: FontAwesome.clipboard + " %1".arg(qsTr("Copy to clipboard")) + translationManager.emptyString
|
||||
label.font.family: FontAwesome.fontFamily
|
||||
fontSize: 13
|
||||
onClicked: {
|
||||
clipboard.setText(TxUtils.makeQRCodeString(appWindow.current_address));
|
||||
appWindow.showStatusMessage(qsTr("Copied to clipboard") + translationManager.emptyString, 3);
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
Layout.preferredWidth: 220
|
||||
small: true
|
||||
|
||||
@@ -81,8 +81,7 @@ Rectangle {
|
||||
id: mainLayout
|
||||
Layout.fillWidth: true
|
||||
anchors.margins: 20
|
||||
anchors.topMargin: 40
|
||||
|
||||
anchors.topMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -89,8 +89,7 @@ Rectangle {
|
||||
id: mainLayout
|
||||
Layout.fillWidth: true
|
||||
anchors.margins: 20
|
||||
anchors.topMargin: 40
|
||||
|
||||
anchors.topMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -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 QtQml.Models 2.2
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
@@ -44,8 +45,7 @@ import "../js/Utils.js" as Utils
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
signal paymentClicked(string address, string paymentId, string amount, int mixinCount,
|
||||
int priority, string description)
|
||||
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
||||
signal sweepUnmixableClicked()
|
||||
|
||||
color: "transparent"
|
||||
@@ -61,19 +61,18 @@ Rectangle {
|
||||
}
|
||||
|
||||
// There are sufficient unlocked funds available
|
||||
if (walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()) {
|
||||
if (recipientModel.getAmountTotal() > appWindow.getUnlockedBalance()) {
|
||||
return qsTr("Amount is more than unlocked balance.") + translationManager.emptyString;
|
||||
}
|
||||
|
||||
if (addressLine.text)
|
||||
{
|
||||
if (!recipientModel.hasEmptyAddress()) {
|
||||
// Address is valid
|
||||
if (!TxUtils.checkAddress(addressLine.text, appWindow.persistentSettings.nettype)) {
|
||||
if (recipientModel.hasInvalidAddress()) {
|
||||
return qsTr("Address is invalid.") + translationManager.emptyString;
|
||||
}
|
||||
|
||||
// Amount is nonzero
|
||||
if (!amountLine.text || parseFloat(amountLine.text) <= 0) {
|
||||
if (recipientModel.hasEmptyAmount()) {
|
||||
return qsTr("Enter an amount.") + translationManager.emptyString;
|
||||
}
|
||||
}
|
||||
@@ -93,12 +92,22 @@ Rectangle {
|
||||
oaPopup.open()
|
||||
}
|
||||
|
||||
function fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name) {
|
||||
if (recipientModel.count > 0) {
|
||||
const last = recipientModel.count - 1;
|
||||
if (recipientModel.get(recipientModel.count - 1).address == "") {
|
||||
recipientModel.remove(last);
|
||||
}
|
||||
}
|
||||
|
||||
recipientModel.newRecipient(address, Utils.removeTrailingZeros(amount || ""));
|
||||
setPaymentId(payment_id || "");
|
||||
setDescription((recipient_name ? recipient_name + " " : "") + (tx_description || ""));
|
||||
}
|
||||
|
||||
function updateFromQrCode(address, payment_id, amount, tx_description, recipient_name) {
|
||||
console.log("updateFromQrCode")
|
||||
addressLine.text = address
|
||||
setPaymentId(payment_id);
|
||||
amountLine.text = amount
|
||||
setDescription(recipient_name + " " + tx_description);
|
||||
fillPaymentDetails(address, payment_id, amount, tx_description, recipient_name);
|
||||
cameraUi.qrcode_decoded.disconnect(updateFromQrCode)
|
||||
}
|
||||
|
||||
@@ -113,10 +122,8 @@ Rectangle {
|
||||
}
|
||||
|
||||
function clearFields() {
|
||||
addressLine.text = ""
|
||||
setPaymentId("");
|
||||
amountLine.text = ""
|
||||
setDescription("");
|
||||
recipientModel.clear();
|
||||
fillPaymentDetails("", "", "", "", "");
|
||||
priorityDropdown.currentIndex = 0
|
||||
}
|
||||
|
||||
@@ -163,166 +170,496 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
// recipient address input
|
||||
RowLayout {
|
||||
id: addressLineRow
|
||||
Layout.fillWidth: true
|
||||
ListModel {
|
||||
id: recipientModel
|
||||
|
||||
LineEditMulti {
|
||||
id: addressLine
|
||||
spacing: 0
|
||||
inputPaddingRight: inlineButtonVisible && inlineButton2Visible ? 100 : 60
|
||||
fontBold: true
|
||||
labelText: qsTr("Address") + translationManager.emptyString
|
||||
labelButtonText: qsTr("Resolve") + translationManager.emptyString
|
||||
placeholderText: {
|
||||
if(persistentSettings.nettype == NetworkType.MAINNET){
|
||||
return "4.. / 8.. / OpenAlias";
|
||||
} else if (persistentSettings.nettype == NetworkType.STAGENET){
|
||||
return "5.. / 7..";
|
||||
} else if(persistentSettings.nettype == NetworkType.TESTNET){
|
||||
return "9.. / B..";
|
||||
}
|
||||
}
|
||||
wrapMode: Text.WrapAnywhere
|
||||
addressValidation: true
|
||||
onTextChanged: {
|
||||
const parsed = walletManager.parse_uri_to_object(text);
|
||||
if (!parsed.error) {
|
||||
addressLine.text = parsed.address;
|
||||
setPaymentId(parsed.payment_id);
|
||||
amountLine.text = parsed.amount;
|
||||
setDescription(parsed.tx_description);
|
||||
}
|
||||
}
|
||||
inlineButton.text: FontAwesome.addressBook
|
||||
inlineButton.buttonHeight: 30
|
||||
inlineButton.fontPixelSize: 22
|
||||
inlineButton.fontFamily: FontAwesome.fontFamily
|
||||
inlineButton.textColor: MoneroComponents.Style.defaultFontColor
|
||||
inlineButton.onClicked: {
|
||||
middlePanel.addressBookView.selectAndSend = true;
|
||||
appWindow.showPageRequest("AddressBook");
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
readonly property int maxRecipients: 16
|
||||
|
||||
StandardButton {
|
||||
id: resolveButton
|
||||
width: 80
|
||||
text: qsTr("Resolve") + translationManager.emptyString
|
||||
visible: TxUtils.isValidOpenAliasAddress(addressLine.text)
|
||||
enabled : visible
|
||||
onClicked: {
|
||||
var result = walletManager.resolveOpenAlias(addressLine.text)
|
||||
if (result) {
|
||||
var parts = result.split("|")
|
||||
if (parts.length == 2) {
|
||||
var address_ok = walletManager.addressValid(parts[1], appWindow.persistentSettings.nettype)
|
||||
if (parts[0] === "true") {
|
||||
if (address_ok) {
|
||||
// prepend openalias to description
|
||||
descriptionLine.text = descriptionLine.text ? addressLine.text + " " + descriptionLine.text : addressLine.text
|
||||
descriptionCheckbox.checked = true
|
||||
addressLine.text = parts[1]
|
||||
}
|
||||
else
|
||||
oa_message(qsTr("No valid address found at this OpenAlias address"))
|
||||
}
|
||||
else if (parts[0] === "false") {
|
||||
if (address_ok) {
|
||||
addressLine.text = parts[1]
|
||||
oa_message(qsTr("Address found, but the DNSSEC signatures could not be verified, so this address may be spoofed"))
|
||||
}
|
||||
else
|
||||
{
|
||||
oa_message(qsTr("No valid address found at this OpenAlias address, but the DNSSEC signatures could not be verified, so this may be spoofed"))
|
||||
}
|
||||
}
|
||||
else {
|
||||
oa_message(qsTr("Internal error"))
|
||||
}
|
||||
}
|
||||
else {
|
||||
oa_message(qsTr("Internal error"))
|
||||
}
|
||||
}
|
||||
else {
|
||||
oa_message(qsTr("No address found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
ListElement {
|
||||
address: ""
|
||||
amount: ""
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: appWindow.walletMode < 2 ? 1 : 2
|
||||
Layout.fillWidth: true
|
||||
columnSpacing: 32
|
||||
function newRecipient(address, amount) {
|
||||
if (recipientModel.count < maxRecipients) {
|
||||
recipientModel.append({address: address, amount: amount});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: 200
|
||||
function getRecipients() {
|
||||
var recipients = [];
|
||||
for (var index = 0; index < recipientModel.count; ++index) {
|
||||
const recipient = recipientModel.get(index);
|
||||
recipients.push({
|
||||
address: recipient.address,
|
||||
amount: recipient.amount,
|
||||
});
|
||||
}
|
||||
return recipients;
|
||||
}
|
||||
|
||||
// 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
|
||||
function getAmountTotal() {
|
||||
var sum = [];
|
||||
for (var index = 0; index < recipientModel.count; ++index) {
|
||||
const amount = recipientModel.get(index).amount;
|
||||
if (amount == "(all)") {
|
||||
return appWindow.getUnlockedBalance();
|
||||
}
|
||||
sum.push(amount || "0");
|
||||
}
|
||||
return walletManager.amountsSumFromStrings(sum);
|
||||
}
|
||||
|
||||
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;
|
||||
function hasEmptyAmount() {
|
||||
for (var index = 0; index < recipientModel.count; ++index) {
|
||||
if (recipientModel.get(index).amount === "") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasEmptyAddress() {
|
||||
for (var index = 0; index < recipientModel.count; ++index) {
|
||||
if (recipientModel.get(index).address === "") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasInvalidAddress() {
|
||||
for (var index = 0; index < recipientModel.count; ++index) {
|
||||
if (!TxUtils.checkAddress(recipientModel.get(index).address, appWindow.persistentSettings.nettype)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: recipientLayout.height
|
||||
|
||||
ColumnLayout {
|
||||
id: recipientLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
readonly property int colSpacing: 10
|
||||
readonly property int rowSpacing: 10
|
||||
readonly property int secondRowWidth: 125
|
||||
readonly property int thirdRowWidth: 50
|
||||
|
||||
RowLayout {
|
||||
Layout.bottomMargin: recipientLayout.rowSpacing / 2
|
||||
spacing: recipientLayout.colSpacing
|
||||
|
||||
RowLayout {
|
||||
id: addressLabel
|
||||
spacing: 6
|
||||
Layout.fillWidth: true
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.leftMargin: 10
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 16
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
text: qsTr("Address") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
fontPixelSize: 18
|
||||
text: FontAwesome.desktop
|
||||
onClicked: {
|
||||
clearFields();
|
||||
const codes = oshelper.grabQrCodesFromScreen();
|
||||
for (var index = 0; index < codes.length; ++index) {
|
||||
const parsed = walletManager.parse_uri_to_object(codes[index]);
|
||||
if (!parsed.error) {
|
||||
fillPaymentDetails(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description, parsed.recipient_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
|
||||
}
|
||||
|
||||
validator: RegExpValidator {
|
||||
regExp: /^(\d{1,8})?([\.,]\d{1,12})?$/
|
||||
}
|
||||
}
|
||||
MoneroComponents.InlineButton {
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
text: FontAwesome.qrcode
|
||||
visible: appWindow.qrScannerEnabled
|
||||
onClicked: {
|
||||
cameraUi.state = "Capture"
|
||||
cameraUi.qrcode_decoded.connect(updateFromQrCode)
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
text: FontAwesome.addressBook
|
||||
onClicked: {
|
||||
middlePanel.addressBookView.selectAndSend = true;
|
||||
appWindow.showPageRequest("AddressBook");
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.preferredWidth: recipientLayout.secondRowWidth
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 16
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
text: qsTr("Amount") + translationManager.emptyString
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: recipientLayout.thirdRowWidth
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: recipientRepeater
|
||||
model: recipientModel
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: recipientLayout.thirdRowWidth
|
||||
color: MoneroComponents.Style.inputBorderColorInActive
|
||||
height: 1
|
||||
visible: index > 0
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 0
|
||||
|
||||
MoneroComponents.LineEditMulti {
|
||||
KeyNavigation.backtab: index > 0 ? recipientRepeater.itemAt(index - 1).children[1].children[2] : sendButton
|
||||
KeyNavigation.tab: parent.children[2]
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.topMargin: index > 0 ? recipientLayout.rowSpacing / 2 : 0
|
||||
Layout.bottomMargin: recipientLayout.rowSpacing / 2
|
||||
Layout.fillWidth: true
|
||||
addressValidation: true
|
||||
borderDisabled: true
|
||||
fontFamily: MoneroComponents.Style.fontMonoRegular.name
|
||||
fontSize: 14
|
||||
inputPaddingBottom: 0
|
||||
inputPaddingTop: 0
|
||||
inputPaddingRight: 0
|
||||
placeholderFontFamily: MoneroComponents.Style.fontMonoRegular.name
|
||||
placeholderFontSize: 14
|
||||
spacing: 0
|
||||
wrapMode: Text.WrapAnywhere
|
||||
placeholderText: {
|
||||
if(persistentSettings.nettype == NetworkType.MAINNET){
|
||||
return "4.. / 8.. / OpenAlias";
|
||||
} else if (persistentSettings.nettype == NetworkType.STAGENET){
|
||||
return "5.. / 7..";
|
||||
} else if(persistentSettings.nettype == NetworkType.TESTNET){
|
||||
return "9.. / B..";
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
const parsed = walletManager.parse_uri_to_object(text);
|
||||
if (!parsed.error) {
|
||||
fillPaymentDetails(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description);
|
||||
}
|
||||
address = text;
|
||||
}
|
||||
text: address
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
small: true
|
||||
text: qsTr("Resolve") + translationManager.emptyString
|
||||
visible: TxUtils.isValidOpenAliasAddress(address)
|
||||
onClicked: {
|
||||
var result = walletManager.resolveOpenAlias(address)
|
||||
if (result) {
|
||||
var parts = result.split("|")
|
||||
if (parts.length == 2) {
|
||||
var address_ok = walletManager.addressValid(parts[1], appWindow.persistentSettings.nettype)
|
||||
if (parts[0] === "true") {
|
||||
if (address_ok) {
|
||||
// prepend openalias to description
|
||||
descriptionLine.text = descriptionLine.text ? address + " " + descriptionLine.text : address
|
||||
descriptionCheckbox.checked = true
|
||||
recipientRepeater.itemAt(index).children[1].children[0].text = parts[1];
|
||||
}
|
||||
else
|
||||
oa_message(qsTr("No valid address found at this OpenAlias address"))
|
||||
}
|
||||
else if (parts[0] === "false") {
|
||||
if (address_ok) {
|
||||
recipientRepeater.itemAt(index).children[1].children[0].text = parts[1];
|
||||
oa_message(qsTr("Address found, but the DNSSEC signatures could not be verified, so this address may be spoofed"))
|
||||
}
|
||||
else
|
||||
{
|
||||
oa_message(qsTr("No valid address found at this OpenAlias address, but the DNSSEC signatures could not be verified, so this may be spoofed"))
|
||||
}
|
||||
}
|
||||
else {
|
||||
oa_message(qsTr("Internal error"))
|
||||
}
|
||||
}
|
||||
else {
|
||||
oa_message(qsTr("Internal error"))
|
||||
}
|
||||
}
|
||||
else {
|
||||
oa_message(qsTr("No address found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: recipientLayout.colSpacing / 2 - width
|
||||
Layout.rightMargin: recipientLayout.colSpacing / 2
|
||||
color: MoneroComponents.Style.inputBorderColorInActive
|
||||
width: 1
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
KeyNavigation.backtab: parent.children[0]
|
||||
KeyNavigation.tab: index + 1 < recipientRepeater.count ? recipientRepeater.itemAt(index + 1).children[1].children[0] : sendButton
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.topMargin: recipientLayout.rowSpacing / 2
|
||||
Layout.bottomMargin: recipientLayout.rowSpacing / 2
|
||||
Layout.rightMargin: recipientLayout.colSpacing / 2
|
||||
Layout.preferredWidth: 125
|
||||
borderDisabled: true
|
||||
fontFamily: MoneroComponents.Style.fontMonoRegular.name
|
||||
fontSize: 14
|
||||
inputPadding: 0
|
||||
placeholderFontFamily: MoneroComponents.Style.fontMonoRegular.name
|
||||
placeholderFontSize: 14
|
||||
placeholderLeftMargin: 0
|
||||
placeholderText: "0.00"
|
||||
text: amount
|
||||
onTextChanged: {
|
||||
text = text.trim().replace(",", ".");
|
||||
const match = text.match(/^0+(\d.*)/);
|
||||
if (match) {
|
||||
const cursorPosition = cursorPosition;
|
||||
text = match[1];
|
||||
cursorPosition = Math.max(cursorPosition, 1) - 1;
|
||||
} else if(text.indexOf('.') === 0){
|
||||
text = '0' + text;
|
||||
if (text.length > 2) {
|
||||
cursorPosition = 1;
|
||||
}
|
||||
}
|
||||
error = walletManager.amountFromString(text) > appWindow.getUnlockedBalance();
|
||||
|
||||
amount = text;
|
||||
}
|
||||
validator: RegExpValidator {
|
||||
regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.leftMargin: recipientLayout.colSpacing / 2
|
||||
Layout.preferredWidth: recipientLayout.thirdRowWidth
|
||||
font.family: FontAwesome.fontFamilySolid
|
||||
font.styleName: "Solid"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: mouseArea.containsMouse ? 1 : 0.85
|
||||
text: recipientModel.count == 1 ? FontAwesome.infinity : FontAwesome.times
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
if (recipientModel.count == 1) {
|
||||
parent.parent.children[2].text = "(all)";
|
||||
} else {
|
||||
recipientModel.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: totalLayout
|
||||
Layout.topMargin: recipientLayout.rowSpacing / 2
|
||||
Layout.fillWidth: true
|
||||
columns: 3
|
||||
columnSpacing: recipientLayout.colSpacing
|
||||
rowSpacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.column: 0
|
||||
Layout.row: 0
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
CheckBox {
|
||||
border: false
|
||||
checked: false
|
||||
enabled: {
|
||||
if (recipientModel.count > 0 && recipientModel.get(0).amount == "(all)") {
|
||||
return false;
|
||||
}
|
||||
if (recipientModel.count >= recipientModel.maxRecipients) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
fontAwesomeIcons: true
|
||||
fontSize: descriptionLine.labelFontSize
|
||||
iconOnTheLeft: true
|
||||
text: qsTr("Add recipient") + translationManager.emptyString
|
||||
toggleOnClick: false
|
||||
uncheckedIcon: FontAwesome.plusCircle
|
||||
onClicked: {
|
||||
recipientModel.newRecipient("", "");
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 16
|
||||
text: recipientModel.count > 1 ? qsTr("Total") + translationManager.emptyString : ""
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: totalValue
|
||||
Layout.column: 1
|
||||
Layout.row: 0
|
||||
Layout.preferredWidth: recipientLayout.secondRowWidth
|
||||
borderDisabled: true
|
||||
fontFamily: MoneroComponents.Style.fontMonoRegular.name
|
||||
fontSize: 14
|
||||
inputHeight: 30
|
||||
inputPadding: 0
|
||||
readOnly: true
|
||||
text: Utils.removeTrailingZeros(walletManager.displayAmount(recipientModel.getAmountTotal()))
|
||||
visible: recipientModel.count > 1
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.column: 2
|
||||
Layout.row: 0
|
||||
Layout.preferredWidth: recipientLayout.thirdRowWidth
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
text: "XMR"
|
||||
visible: recipientModel.count > 1
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
Layout.column: 1
|
||||
Layout.row: recipientModel.count > 1 ? 1 : 0
|
||||
Layout.preferredWidth: recipientLayout.secondRowWidth
|
||||
borderDisabled: true
|
||||
fontFamily: MoneroComponents.Style.fontMonoRegular.name
|
||||
fontSize: 14
|
||||
inputHeight: 30
|
||||
inputPadding: 0
|
||||
opacity: 0.7
|
||||
readOnly: true
|
||||
text: fiatApiConvertToFiat(walletManager.displayAmount(recipientModel.getAmountTotal()))
|
||||
visible: persistentSettings.fiatPriceEnabled
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
Layout.column: 2
|
||||
Layout.row: recipientModel.count > 1 ? 1 : 0
|
||||
Layout.preferredWidth: recipientLayout.thirdRowWidth
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
opacity: 0.7
|
||||
text: fiatApiCurrencySymbol()
|
||||
visible: persistentSettings.fiatPriceEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: recipientLayout.top
|
||||
anchors.topMargin: addressLabel.height + recipientLayout.rowSpacing / 2
|
||||
anchors.bottom: recipientLayout.bottom
|
||||
anchors.bottomMargin: totalLayout.height + recipientLayout.rowSpacing / 2
|
||||
anchors.left: recipientLayout.left
|
||||
anchors.right: recipientLayout.right
|
||||
anchors.rightMargin: recipientLayout.thirdRowWidth
|
||||
color: "transparent"
|
||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
||||
border.width: 1
|
||||
radius: 4
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
visible: appWindow.walletMode >= 2
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.topMargin: 5
|
||||
spacing: 10
|
||||
|
||||
StandardDropdown {
|
||||
Layout.preferredWidth: 200
|
||||
id: priorityDropdown
|
||||
currentIndex: 0
|
||||
dataModel: priorityModelV5
|
||||
}
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: feeLabel
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.topMargin: 12
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
font.family: MoneroComponents.Style.fontRegular.name
|
||||
font.pixelSize: 14
|
||||
color: MoneroComponents.Style.defaultFontColor
|
||||
opacity: 0.7
|
||||
property bool estimating: false
|
||||
property var estimatedFee: null
|
||||
property string estimatedFeeFiat: {
|
||||
@@ -338,12 +675,21 @@ Rectangle {
|
||||
if (!sendButton.enabled || !currentWallet) {
|
||||
return;
|
||||
}
|
||||
var addresses = [];
|
||||
var amounts = [];
|
||||
for (var index = 0; index < recipientModel.count; ++index) {
|
||||
const recipient = recipientModel.get(index);
|
||||
addresses.push(recipient.address);
|
||||
amounts.push(walletManager.amountFromString(recipient.amount));
|
||||
}
|
||||
currentWallet.estimateTransactionFeeAsync(
|
||||
addressLine.text,
|
||||
walletManager.amountFromString(amountLine.text),
|
||||
addresses,
|
||||
amounts,
|
||||
priorityModelV5.get(priorityDropdown.currentIndex).priority,
|
||||
function (amount) {
|
||||
estimatedFee = Utils.removeTrailingZeros(amount);
|
||||
if (amount) {
|
||||
estimatedFee = Utils.removeTrailingZeros(amount);
|
||||
}
|
||||
estimating = false;
|
||||
});
|
||||
}
|
||||
@@ -351,56 +697,20 @@ Rectangle {
|
||||
if (!sendButton.enabled || estimatedFee == null) {
|
||||
return ""
|
||||
}
|
||||
return "%1: ~%2 XMR".arg(qsTr("Fee")).arg(estimatedFee) +
|
||||
estimatedFeeFiat +
|
||||
translationManager.emptyString;
|
||||
return "~%1 XMR%2 %3".arg(estimatedFee)
|
||||
.arg(estimatedFeeFiat)
|
||||
.arg(qsTr("fee") + translationManager.emptyString);
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
running: feeLabel.estimating
|
||||
height: parent.height
|
||||
width: 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. \
|
||||
@@ -492,21 +802,21 @@ Rectangle {
|
||||
rightIconInactive: "qrc:///images/rightArrowInactive.png"
|
||||
Layout.topMargin: 4
|
||||
text: qsTr("Send") + translationManager.emptyString
|
||||
enabled: !sendButtonWarningBox.visible && !warningContent && addressLine.text && !paymentIdWarningBox.visible
|
||||
enabled: !sendButtonWarningBox.visible && !warningContent && !recipientModel.hasEmptyAddress() && !paymentIdWarningBox.visible
|
||||
onClicked: {
|
||||
console.log("Transfer: paymentClicked")
|
||||
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)
|
||||
root.paymentClicked(recipientModel.getRecipients(), paymentIdLine.text, root.mixin, priority, descriptionLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkInformation(amount, address, nettype) {
|
||||
return amount.length > 0 && walletManager.amountFromString(amountLine.text) <= appWindow.getUnlockedBalance() && TxUtils.checkAddress(address, nettype)
|
||||
function checkInformation() {
|
||||
return !recipientModel.hasEmptyAmount() &&
|
||||
recipientModel.getAmountTotal() <= appWindow.getUnlockedBalance() &&
|
||||
!recipientModel.hasInvalidAddress();
|
||||
}
|
||||
|
||||
} // pageRoot
|
||||
@@ -566,15 +876,13 @@ Rectangle {
|
||||
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.enabled: appWindow.viewOnly && pageRoot.checkInformation()
|
||||
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)
|
||||
root.paymentClicked(recipientModel.getRecipients(), paymentIdLine.text, root.mixin, priority, descriptionLine.text)
|
||||
}
|
||||
button2.text: qsTr("Sign (offline)") + translationManager.emptyString
|
||||
button2.enabled: !appWindow.viewOnly
|
||||
@@ -591,7 +899,7 @@ Rectangle {
|
||||
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)){
|
||||
if (appWindow.viewOnly && !pageRoot.checkInformation()) {
|
||||
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:" + MoneroComponents.Style.defaultFontColor +
|
||||
@@ -792,19 +1100,9 @@ Rectangle {
|
||||
}
|
||||
|
||||
// Popuplate fields from addressbook.
|
||||
function sendTo(address, paymentId, description, amount){
|
||||
function sendTo(address, paymentId, description, amount) {
|
||||
middlePanel.state = 'Transfer';
|
||||
|
||||
if(typeof address !== 'undefined')
|
||||
addressLine.text = address
|
||||
|
||||
if(typeof paymentId !== 'undefined')
|
||||
setPaymentId(paymentId);
|
||||
|
||||
if(typeof description !== 'undefined')
|
||||
setDescription(description);
|
||||
|
||||
if(typeof amount !== 'undefined')
|
||||
amountLine.text = amount;
|
||||
fillPaymentDetails(address, paymentId, amount, description);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ Rectangle {
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
anchors.margins: 20
|
||||
anchors.topMargin: 40
|
||||
anchors.topMargin: 0
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
// Copyright (c) 2014-2018, 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 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
import "../../js/Windows.js" as Windows
|
||||
import "../../js/Utils.js" as Utils
|
||||
import "../../components" as MoneroComponents
|
||||
import "../../pages"
|
||||
import "."
|
||||
import moneroComponents.Clipboard 1.0
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 96
|
||||
color: "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
Layout.preferredHeight: 32
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
GridLayout {
|
||||
id: grid
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
columnSpacing: 0
|
||||
property string fontColorActive: MoneroComponents.Style.blackTheme ? "white" : "white"
|
||||
property string fontColorInActive: MoneroComponents.Style.blackTheme ? "white" : MoneroComponents.Style.dimmedFontColor
|
||||
property int fontSize: 15
|
||||
property bool fontBold: true
|
||||
property var fontFamily: MoneroComponents.Style.fontRegular.name
|
||||
property string borderColor: MoneroComponents.Style.blackTheme ? "#808080" : "#B9B9B9"
|
||||
property int textMargin: {
|
||||
// left-right margins for a given cell
|
||||
if(appWindow.width < 890){
|
||||
return 32;
|
||||
} else {
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// navbar left side border
|
||||
id: navBarLeft
|
||||
property bool isActive: settingsStateView.state === "Wallet"
|
||||
Layout.preferredWidth: 2
|
||||
Layout.preferredHeight: 32
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 1
|
||||
height: parent.height - 2
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: 1
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 1
|
||||
color: navBarLeft.isActive ? grid.borderColor : "transparent"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
// WALLET
|
||||
id: navWallet
|
||||
property bool isActive: settingsStateView.state === "Wallet"
|
||||
Layout.preferredWidth: navWalletText.width + grid.textMargin
|
||||
Layout.minimumWidth: 72
|
||||
Layout.preferredHeight: 32
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: parent.isActive ? grid.borderColor : "transparent"
|
||||
height: 30
|
||||
Layout.fillWidth: true
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: navWalletText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: grid.fontFamily
|
||||
font.pixelSize: grid.fontSize
|
||||
font.bold: grid.fontBold
|
||||
text: qsTr("Wallet") + translationManager.emptyString
|
||||
color: navWallet.isActive ? grid.fontColorActive : grid.fontColorInActive
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: { settingsStateView.state = "Wallet" }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
Rectangle{
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 32
|
||||
color: grid.borderColor
|
||||
}
|
||||
ColumnLayout {
|
||||
// UI
|
||||
id: navUI
|
||||
property bool isActive: settingsStateView.state === "UI"
|
||||
Layout.preferredWidth: navUIText.width + grid.textMargin
|
||||
Layout.preferredHeight: 32
|
||||
Layout.minimumWidth: 72
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: parent.isActive ? grid.borderColor : "transparent"
|
||||
height: 30
|
||||
Layout.fillWidth: true
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: navUIText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: grid.fontFamily
|
||||
font.pixelSize: grid.fontSize
|
||||
font.bold: grid.fontBold
|
||||
text: qsTr("Interface") + translationManager.emptyString
|
||||
color: navUI.isActive ? grid.fontColorActive : grid.fontColorInActive
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: { settingsStateView.state = "UI" }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
Rectangle{
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 32
|
||||
color: grid.borderColor
|
||||
}
|
||||
ColumnLayout {
|
||||
// NODE
|
||||
id: navNode
|
||||
property bool isActive: settingsStateView.state === "Node"
|
||||
visible: appWindow.walletMode >= 2
|
||||
Layout.preferredWidth: navNodeText.width + grid.textMargin
|
||||
Layout.preferredHeight: 32
|
||||
Layout.minimumWidth: 72
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: parent.isActive ? grid.borderColor : "transparent"
|
||||
height: 30
|
||||
Layout.fillWidth: true
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: navNodeText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: grid.fontFamily
|
||||
font.pixelSize: grid.fontSize
|
||||
font.bold: grid.fontBold
|
||||
text: qsTr("Node") + translationManager.emptyString
|
||||
color: navNode.isActive ? grid.fontColorActive : grid.fontColorInActive
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: { settingsStateView.state = "Node" }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
Rectangle{
|
||||
visible: appWindow.walletMode >= 2
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 32
|
||||
color: grid.borderColor
|
||||
}
|
||||
ColumnLayout {
|
||||
// LOG
|
||||
id: navLog
|
||||
property bool isActive: settingsStateView.state === "Log"
|
||||
Layout.preferredWidth: navLogText.width + grid.textMargin
|
||||
Layout.preferredHeight: 32
|
||||
Layout.minimumWidth: 72
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: parent.isActive ? grid.borderColor : "transparent"
|
||||
height: 30
|
||||
Layout.fillWidth: true
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: navLogText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: grid.fontFamily
|
||||
font.pixelSize: grid.fontSize
|
||||
font.bold: grid.fontBold
|
||||
text: qsTr("Log") + translationManager.emptyString
|
||||
color: navLog.isActive ? grid.fontColorActive : grid.fontColorInActive
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: { settingsStateView.state = "Log" }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
Rectangle{
|
||||
visible: appWindow.walletMode >= 2
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 32
|
||||
color: grid.borderColor
|
||||
}
|
||||
ColumnLayout {
|
||||
// INFO
|
||||
id: navInfo
|
||||
property bool isActive: settingsStateView.state === "Info"
|
||||
Layout.preferredWidth: navInfoText.width + grid.textMargin
|
||||
Layout.preferredHeight: 32
|
||||
Layout.minimumWidth: 72
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: parent.isActive ? grid.borderColor : "transparent"
|
||||
height: 30
|
||||
Layout.fillWidth: true
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
id: navInfoText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.family: grid.fontFamily
|
||||
font.pixelSize: grid.fontSize
|
||||
font.bold: grid.fontBold
|
||||
text: qsTr("Info") + translationManager.emptyString
|
||||
color: navInfo.isActive ? grid.fontColorActive : grid.fontColorInActive
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: { settingsStateView.state = "Info" }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
// navbar right side border
|
||||
id: navBarRight
|
||||
property bool isActive: settingsStateView.state === "Info"
|
||||
Layout.preferredWidth: 2
|
||||
Layout.preferredHeight: 32
|
||||
color: "transparent"
|
||||
rotation: 180
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 1
|
||||
height: parent.height - 2
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
width: 1
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
color: grid.borderColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
width: 1
|
||||
color: navBarRight.isActive ? grid.borderColor : "transparent"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: grid.borderColor
|
||||
Layout.preferredHeight: 1
|
||||
Layout.preferredWidth: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: "transparent"
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,38 @@ ColumnLayout {
|
||||
property int settingsHeight: 900
|
||||
property alias settingsStateViewState: settingsStateView.state
|
||||
|
||||
Navbar{}
|
||||
MoneroComponents.Navbar {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: height
|
||||
Layout.bottomMargin: height
|
||||
|
||||
MoneroComponents.NavbarItem {
|
||||
active: settingsStateView.state == "Wallet"
|
||||
text: qsTr("Wallet") + translationManager.emptyString
|
||||
onSelected: settingsStateView.state = "Wallet"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: settingsStateView.state == "UI"
|
||||
text: qsTr("Interface") + translationManager.emptyString
|
||||
onSelected: settingsStateView.state = "UI"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: settingsStateView.state == "Node"
|
||||
text: qsTr("Node") + translationManager.emptyString
|
||||
visible: appWindow.walletMode >= 2
|
||||
onSelected: settingsStateView.state = "Node"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: settingsStateView.state == "Log"
|
||||
text: qsTr("Log") + translationManager.emptyString
|
||||
onSelected: settingsStateView.state = "Log"
|
||||
}
|
||||
MoneroComponents.NavbarItem {
|
||||
active: settingsStateView.state == "Info"
|
||||
text: qsTr("Info") + translationManager.emptyString
|
||||
onSelected: settingsStateView.state = "Info"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle{
|
||||
id: settingsStateView
|
||||
|
||||
@@ -66,6 +66,12 @@ Rectangle {
|
||||
text: qsTr("Check for updates periodically") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
checked: persistentSettings.displayWalletNameInTitleBar
|
||||
onClicked: persistentSettings.displayWalletNameInTitleBar = !persistentSettings.displayWalletNameInTitleBar
|
||||
text: qsTr("Display wallet name in title bar") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: hideBalanceCheckBox
|
||||
checked: persistentSettings.hideBalance
|
||||
|
||||
@@ -215,6 +215,7 @@ Rectangle {
|
||||
Layout.fillWidth: true
|
||||
property var lastCommands: []
|
||||
property int currentCommandIndex
|
||||
enabled: !persistentSettings.useRemoteNode
|
||||
fontBold: false
|
||||
placeholderText: qsTr("command + enter (e.g 'help' or 'status')") + translationManager.emptyString
|
||||
placeholderFontSize: 16
|
||||
|
||||
@@ -130,7 +130,7 @@ 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -262,80 +262,94 @@ Rectangle{
|
||||
text: qsTr("To find a remote node, type 'Monero remote node' into your favorite search engine. Please ensure the node is run by a trusted third-party.") + translationManager.emptyString
|
||||
}
|
||||
|
||||
MoneroComponents.RemoteNodeEdit {
|
||||
id: remoteNodeEdit
|
||||
Layout.minimumWidth: 100
|
||||
placeholderFontSize: 15
|
||||
|
||||
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
||||
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
||||
|
||||
initialAddress: persistentSettings.remoteNodeAddress
|
||||
onEditingFinished: {
|
||||
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
|
||||
console.log("setting remote node to " + persistentSettings.remoteNodeAddress);
|
||||
if (persistentSettings.is_trusted_daemon) {
|
||||
persistentSettings.is_trusted_daemon = !persistentSettings.is_trusted_daemon
|
||||
currentWallet.setTrustedDaemon(persistentSettings.is_trusted_daemon)
|
||||
setTrustedDaemonCheckBox.checked = !setTrustedDaemonCheckBox.checked
|
||||
appWindow.showStatusMessage(qsTr("Remote node updated. Trusted daemon has been reset. Mark again, if desired."), 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: 2
|
||||
columnSpacing: 32
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: daemonUsername
|
||||
Layout.fillWidth: true
|
||||
labelText: qsTr("Daemon username") + translationManager.emptyString
|
||||
text: persistentSettings.daemonUsername
|
||||
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
||||
placeholderFontSize: 15
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
}
|
||||
|
||||
MoneroComponents.LineEdit {
|
||||
id: daemonPassword
|
||||
Layout.fillWidth: true
|
||||
labelText: qsTr("Daemon password") + translationManager.emptyString
|
||||
text: persistentSettings.daemonPassword
|
||||
placeholderText: qsTr("Password") + translationManager.emptyString
|
||||
password: true
|
||||
placeholderFontSize: 15
|
||||
labelFontSize: 14
|
||||
fontSize: 15
|
||||
}
|
||||
}
|
||||
|
||||
MoneroComponents.CheckBox {
|
||||
id: setTrustedDaemonCheckBox
|
||||
checked: persistentSettings.is_trusted_daemon
|
||||
onClicked: {
|
||||
persistentSettings.is_trusted_daemon = !persistentSettings.is_trusted_daemon
|
||||
currentWallet.setTrustedDaemon(persistentSettings.is_trusted_daemon)
|
||||
}
|
||||
text: qsTr("Mark as Trusted Daemon") + translationManager.emptyString
|
||||
border: false
|
||||
checkedIcon: FontAwesome.minusCircle
|
||||
uncheckedIcon: FontAwesome.plusCircle
|
||||
fontAwesomeIcons: true
|
||||
fontSize: 16
|
||||
iconOnTheLeft: true
|
||||
text: qsTr("Add remote node") + translationManager.emptyString
|
||||
toggleOnClick: false
|
||||
onClicked: remoteNodeDialog.add(remoteNodesModel.append)
|
||||
}
|
||||
|
||||
MoneroComponents.StandardButton {
|
||||
id: btnConnectRemote
|
||||
enabled: remoteNodeEdit.isValid()
|
||||
small: true
|
||||
text: qsTr("Connect") + translationManager.emptyString
|
||||
onClicked: {
|
||||
// Update daemon login
|
||||
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
|
||||
persistentSettings.daemonUsername = daemonUsername.text;
|
||||
persistentSettings.daemonPassword = daemonPassword.text;
|
||||
persistentSettings.useRemoteNode = true
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
currentWallet.setDaemonLogin(persistentSettings.daemonUsername, persistentSettings.daemonPassword);
|
||||
Repeater {
|
||||
model: remoteNodesModel
|
||||
|
||||
appWindow.connectRemoteNode()
|
||||
Rectangle {
|
||||
height: 30
|
||||
Layout.fillWidth: true
|
||||
color: itemMouseArea.containsMouse || index === remoteNodesModel.selected ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent"
|
||||
|
||||
Rectangle {
|
||||
color: MoneroComponents.Style.appWindowBorderColor
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
height: 1
|
||||
visible: index > 0
|
||||
|
||||
MoneroEffects.ColorTransition {
|
||||
targetObj: parent
|
||||
blackColor: MoneroComponents.Style._b_appWindowBorderColor
|
||||
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 80
|
||||
color: "transparent"
|
||||
|
||||
MoneroComponents.TextPlain {
|
||||
color: index === remoteNodesModel.selected ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 6
|
||||
font.pixelSize: 16
|
||||
text: address
|
||||
themeTransition: false
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: itemMouseArea
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: remoteNodesModel.applyRemoteNode(index)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
height: 30
|
||||
spacing: 10
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
buttonColor: "transparent"
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
fontPixelSize: 18
|
||||
text: FontAwesome.edit
|
||||
onClicked: remoteNodeDialog.edit(remoteNodesModel.get(index), function (remoteNode) {
|
||||
remoteNodesModel.set(index, remoteNode)
|
||||
})
|
||||
}
|
||||
|
||||
MoneroComponents.InlineButton {
|
||||
buttonColor: "transparent"
|
||||
fontFamily: FontAwesome.fontFamily
|
||||
text: FontAwesome.times
|
||||
visible: remoteNodesModel.count > 1
|
||||
onClicked: remoteNodesModel.removeSelectNextIfNeeded(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,7 +412,7 @@ Rectangle{
|
||||
placeholderFontSize: 15
|
||||
text: persistentSettings.daemonFlags
|
||||
addressValidation: false
|
||||
error: text.match(/(^|\s)--(data-dir|bootstrap-daemon-address)/)
|
||||
error: text.match(/(^|\s)--(data-dir|bootstrap-daemon-address|non-interactive)/)
|
||||
onEditingFinished: {
|
||||
if (!daemonFlags.error) {
|
||||
persistentSettings.daemonFlags = daemonFlags.text;
|
||||
@@ -431,7 +445,7 @@ Rectangle{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
qml.qrc
6
qml.qrc
@@ -3,8 +3,12 @@
|
||||
<file>main.qml</file>
|
||||
<file>LeftPanel.qml</file>
|
||||
<file>MiddlePanel.qml</file>
|
||||
<file>components/Dialog.qml</file>
|
||||
<file>components/Label.qml</file>
|
||||
<file>components/LanguageButton.qml</file>
|
||||
<file>components/Navbar.qml</file>
|
||||
<file>components/NavbarItem.qml</file>
|
||||
<file>components/RemoteNodeDialog.qml</file>
|
||||
<file>components/SettingsListItem.qml</file>
|
||||
<file>components/Slider.qml</file>
|
||||
<file>components/UpdateDialog.qml</file>
|
||||
@@ -15,6 +19,7 @@
|
||||
<file>monero/utils/gpg_keys/fluffypony.asc</file>
|
||||
<file>monero/utils/gpg_keys/luigi1111.asc</file>
|
||||
<file>pages/Account.qml</file>
|
||||
<file>pages/Advanced.qml</file>
|
||||
<file>pages/Transfer.qml</file>
|
||||
<file>pages/History.qml</file>
|
||||
<file>pages/AddressBook.qml</file>
|
||||
@@ -162,7 +167,6 @@
|
||||
<file>pages/settings/SettingsLog.qml</file>
|
||||
<file>pages/settings/SettingsLayout.qml</file>
|
||||
<file>pages/settings/SettingsInfo.qml</file>
|
||||
<file>pages/settings/Navbar.qml</file>
|
||||
<file>components/WarningBox.qml</file>
|
||||
<file>images/miningxmr.png</file>
|
||||
<file>images/miningxmr@2x.png</file>
|
||||
|
||||
@@ -20,7 +20,7 @@ file(GLOB SOURCE_FILES
|
||||
"libwalletqt/PendingTransaction.cpp"
|
||||
"libwalletqt/TransactionHistory.cpp"
|
||||
"libwalletqt/TransactionInfo.cpp"
|
||||
"libwalletqt/QRCodeImageProvider.cpp" QR
|
||||
"libwalletqt/QRCodeImageProvider.cpp"
|
||||
"libwalletqt/AddressBook.cpp"
|
||||
"libwalletqt/Subaddress.cpp"
|
||||
"libwalletqt/SubaddressAccount.cpp"
|
||||
@@ -55,13 +55,6 @@ if(ENABLE_PASS_STRENGTH_METER)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_SCANNER)
|
||||
file(GLOB QR_CODE_FILES
|
||||
"QR-Code-scanner/*.h"
|
||||
"QR-Code-scanner/*.cpp"
|
||||
)
|
||||
endif()
|
||||
|
||||
set(EXECUTABLE_FLAG)
|
||||
if(MINGW)
|
||||
set(EXECUTABLE_FLAG WIN32)
|
||||
@@ -84,7 +77,6 @@ endif()
|
||||
set(monero_wallet_gui_sources
|
||||
${SOURCE_FILES}
|
||||
${PASS_STRENGTH_FILES}
|
||||
${QR_CODE_FILES}
|
||||
${RESOURCES}
|
||||
)
|
||||
|
||||
@@ -128,7 +120,6 @@ target_include_directories(monero-wallet-gui PUBLIC
|
||||
${X11_INCLUDE_DIR}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
${ZBAR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_compile_definitions(monero-wallet-gui
|
||||
@@ -137,6 +128,14 @@ target_compile_definitions(monero-wallet-gui
|
||||
${Qt5Qml_DEFINITIONS}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
if(NOT ICU_ROOT)
|
||||
execute_process(COMMAND brew --prefix icu4c OUTPUT_VARIABLE ICU_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
find_package(ICU REQUIRED COMPONENTS data i18n uc)
|
||||
target_link_directories(monero-wallet-gui PRIVATE ${ICU_ROOT}/lib)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
|
||||
target_link_libraries(monero-wallet-gui
|
||||
@@ -159,6 +158,7 @@ target_link_libraries(monero-wallet-gui
|
||||
${EXTRA_LIBRARIES}
|
||||
${ICU_LIBRARIES}
|
||||
openpgp
|
||||
qrdecoder
|
||||
translations
|
||||
)
|
||||
|
||||
@@ -171,16 +171,14 @@ if(X11_FOUND)
|
||||
endif()
|
||||
|
||||
if(WITH_SCANNER)
|
||||
if(NOT ANDROID)
|
||||
target_link_libraries(monero-wallet-gui qrscanner)
|
||||
if(LINUX AND NOT ANDROID)
|
||||
target_link_libraries(monero-wallet-gui
|
||||
${ZBAR_LIBRARIES}
|
||||
jpeg
|
||||
v4l2
|
||||
v4lconvert
|
||||
rt
|
||||
)
|
||||
else()
|
||||
target_link_libraries(monero-wallet-gui ${ZBAR_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
file(GLOB_RECURSE SRC_SOURCES *.cpp)
|
||||
file(GLOB_RECURSE SRC_HEADERS *.h)
|
||||
|
||||
add_library(qrdecoder STATIC
|
||||
Decoder.cpp
|
||||
)
|
||||
target_link_libraries(qrdecoder
|
||||
PUBLIC
|
||||
Qt5::Gui
|
||||
PRIVATE
|
||||
quirc
|
||||
)
|
||||
|
||||
if(WITH_SCANNER)
|
||||
add_library(qrscanner
|
||||
QrCodeScanner.cpp
|
||||
QrScanThread.cpp
|
||||
)
|
||||
target_link_libraries(qrscanner
|
||||
PUBLIC
|
||||
Qt5::Multimedia
|
||||
qrdecoder
|
||||
)
|
||||
endif()
|
||||
|
||||
99
src/QR-Code-scanner/Decoder.cpp
Normal file
99
src/QR-Code-scanner/Decoder.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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 "Decoder.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "quirc.h"
|
||||
|
||||
QrDecoder::QrDecoder()
|
||||
: m_qr(quirc_new())
|
||||
{
|
||||
if (m_qr == nullptr)
|
||||
{
|
||||
throw std::runtime_error("QUIRC: failed to allocate memory");
|
||||
}
|
||||
}
|
||||
|
||||
QrDecoder::~QrDecoder()
|
||||
{
|
||||
quirc_destroy(m_qr);
|
||||
}
|
||||
|
||||
std::vector<std::string> QrDecoder::decode(const QImage &image)
|
||||
{
|
||||
if (image.format() == QImage::Format_Grayscale8)
|
||||
{
|
||||
return decodeGrayscale8(image);
|
||||
}
|
||||
return decodeGrayscale8(image.convertToFormat(QImage::Format_Grayscale8));
|
||||
}
|
||||
|
||||
std::vector<std::string> QrDecoder::decodeGrayscale8(const QImage &image)
|
||||
{
|
||||
if (quirc_resize(m_qr, image.width(), image.height()) < 0)
|
||||
{
|
||||
throw std::runtime_error("QUIRC: failed to allocate video memory");
|
||||
}
|
||||
|
||||
uint8_t *rawImage = quirc_begin(m_qr, nullptr, nullptr);
|
||||
if (rawImage == nullptr)
|
||||
{
|
||||
throw std::runtime_error("QUIRC: failed to get image buffer");
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
std::copy(image.constBits(), image.constBits() + image.sizeInBytes(), rawImage);
|
||||
#else
|
||||
std::copy(image.constBits(), image.constBits() + image.byteCount(), rawImage);
|
||||
#endif
|
||||
quirc_end(m_qr);
|
||||
|
||||
const int count = quirc_count(m_qr);
|
||||
if (count < 0)
|
||||
{
|
||||
throw std::runtime_error("QUIRC: failed to get the number of recognized QR-codes");
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
result.reserve(static_cast<size_t>(count));
|
||||
for (int index = 0; index < count; ++index)
|
||||
{
|
||||
quirc_code code;
|
||||
quirc_extract(m_qr, index, &code);
|
||||
|
||||
quirc_data data;
|
||||
const quirc_decode_error_t err = quirc_decode(&code, &data);
|
||||
if (err == QUIRC_SUCCESS)
|
||||
{
|
||||
result.emplace_back(&data.payload[0], &data.payload[data.payload_len]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
49
src/QR-Code-scanner/Decoder.h
Normal file
49
src/QR-Code-scanner/Decoder.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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 <QImage>
|
||||
|
||||
struct quirc;
|
||||
|
||||
class QrDecoder
|
||||
{
|
||||
public:
|
||||
QrDecoder(const QrDecoder &) = delete;
|
||||
QrDecoder &operator=(const QrDecoder &) = delete;
|
||||
|
||||
QrDecoder();
|
||||
~QrDecoder();
|
||||
|
||||
std::vector<std::string> decode(const QImage &image);
|
||||
|
||||
private:
|
||||
std::vector<std::string> decodeGrayscale8(const QImage &image);
|
||||
|
||||
private:
|
||||
quirc *m_qr;
|
||||
};
|
||||
@@ -27,7 +27,6 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "QrCodeScanner.h"
|
||||
#include <WalletManager.h>
|
||||
#include <QVideoProbe>
|
||||
#include <QCamera>
|
||||
|
||||
@@ -40,7 +39,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, SIGNAL(decoded(int, QString)));
|
||||
QObject::connect(m_thread, SIGNAL(decoded(QString)), this, SIGNAL(decoded(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)));
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ public Q_SLOTS:
|
||||
Q_SIGNALS:
|
||||
void enabledChanged();
|
||||
|
||||
void decoded(int type, const QString &data);
|
||||
void decode(int type, const QString &data);
|
||||
void decoded(const QString &data);
|
||||
void notifyError(const QString &error, bool warning = false);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -38,62 +38,15 @@ QrScanThread::QrScanThread(QObject *parent)
|
||||
: QThread(parent)
|
||||
,m_running(true)
|
||||
{
|
||||
m_scanner.set_handler(*this);
|
||||
}
|
||||
|
||||
void QrScanThread::image_callback(zbar::Image &image)
|
||||
{
|
||||
qDebug() << "image_callback : Found Code ! " ;
|
||||
for(zbar::Image::SymbolIterator sym = image.symbol_begin();
|
||||
sym != image.symbol_end();
|
||||
++sym)
|
||||
if(!sym->get_count()) {
|
||||
QString data = QString::fromStdString(sym->get_data());
|
||||
emit decoded(sym->get_type(), data);
|
||||
}
|
||||
}
|
||||
|
||||
void QrScanThread::processZImage(zbar::Image &image)
|
||||
{
|
||||
m_scanner.recycle_image(image);
|
||||
zbar::Image tmp = image.convert(*(long*)"Y800");
|
||||
m_scanner.scan(tmp);
|
||||
image.set_symbols(tmp.get_symbols());
|
||||
}
|
||||
|
||||
bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
|
||||
{
|
||||
switch( qimg.format() ){
|
||||
case QImage::Format_RGB32 :
|
||||
case QImage::Format_ARGB32 :
|
||||
case QImage::Format_ARGB32_Premultiplied :
|
||||
break;
|
||||
default :
|
||||
emit notifyError(QString("Invalid QImage Format !"));
|
||||
return false;
|
||||
}
|
||||
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 !"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void QrScanThread::processQImage(const QImage &qimg)
|
||||
{
|
||||
try {
|
||||
m_image = QSharedPointer<zbar::Image>(new zbar::Image());
|
||||
if( ! zimageFromQImage(qimg, *m_image) )
|
||||
return;
|
||||
processZImage(*m_image);
|
||||
for (const std::string &code : m_decoder.decode(qimg))
|
||||
{
|
||||
emit decoded(QString::fromStdString(code));
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
qDebug() << "ERROR: " << e.what();
|
||||
|
||||
@@ -35,9 +35,10 @@
|
||||
#include <QEvent>
|
||||
#include <QVideoFrame>
|
||||
#include <QCamera>
|
||||
#include <zbar.h>
|
||||
|
||||
class QrScanThread : public QThread, public zbar::Image::Handler
|
||||
#include "Decoder.h"
|
||||
|
||||
class QrScanThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -47,20 +48,16 @@ public:
|
||||
virtual void stop();
|
||||
|
||||
Q_SIGNALS:
|
||||
void decoded(int type, const QString &data);
|
||||
void decoded(const QString &data);
|
||||
void notifyError(const QString &error, bool warning = false);
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
void processVideoFrame(const QVideoFrame &);
|
||||
void processQImage(const QImage &);
|
||||
void processZImage(zbar::Image &image);
|
||||
virtual void image_callback(zbar::Image &image);
|
||||
bool zimageFromQImage(const QImage&, zbar::Image &);
|
||||
|
||||
private:
|
||||
zbar::ImageScanner m_scanner;
|
||||
QSharedPointer<zbar::Image> m_image;
|
||||
QrDecoder m_decoder;
|
||||
bool m_running;
|
||||
QMutex m_mutex;
|
||||
QWaitCondition m_waitCondition;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "DaemonManager.h"
|
||||
#include "common/util.h"
|
||||
#include <QElapsedTimer>
|
||||
#include <QFile>
|
||||
#include <QMutexLocker>
|
||||
@@ -47,24 +48,7 @@ namespace {
|
||||
static const int DAEMON_START_TIMEOUT_SECONDS = 120;
|
||||
}
|
||||
|
||||
DaemonManager * DaemonManager::m_instance = nullptr;
|
||||
QStringList DaemonManager::m_clArgs;
|
||||
|
||||
DaemonManager *DaemonManager::instance(const QStringList *args/* = nullptr*/)
|
||||
{
|
||||
if (!m_instance) {
|
||||
m_instance = new DaemonManager;
|
||||
// store command line arguments for later use
|
||||
if (args != nullptr)
|
||||
{
|
||||
m_clArgs = *args;
|
||||
}
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const QString &dataDir, const QString &bootstrapNodeAddress, bool noSync /* = false*/)
|
||||
bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const QString &dataDir, const QString &bootstrapNodeAddress, bool noSync /* = false*/, bool pruneBlockchain /* = false*/)
|
||||
{
|
||||
if (!QFileInfo(m_monerod).isFile())
|
||||
{
|
||||
@@ -85,12 +69,6 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
|
||||
else if (nettype == NetworkType::STAGENET)
|
||||
arguments << "--stagenet";
|
||||
|
||||
foreach (const QString &str, m_clArgs) {
|
||||
qDebug() << QString(" [%1] ").arg(str);
|
||||
if (!str.isEmpty())
|
||||
arguments << str;
|
||||
}
|
||||
|
||||
// Custom startup flags for daemon
|
||||
foreach (const QString &str, flags.split(" ")) {
|
||||
qDebug() << QString(" [%1] ").arg(str);
|
||||
@@ -108,15 +86,18 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
|
||||
arguments << "--bootstrap-daemon-address" << bootstrapNodeAddress;
|
||||
}
|
||||
|
||||
if (pruneBlockchain) {
|
||||
if (!checkLmdbExists(dataDir)) { // check that DB has not already been created
|
||||
arguments << "--prune-blockchain";
|
||||
}
|
||||
}
|
||||
|
||||
if (noSync) {
|
||||
arguments << "--no-sync";
|
||||
}
|
||||
|
||||
if (!flags.contains("--out-peers", Qt::CaseSensitive) && bootstrapNodeAddress == "auto") {
|
||||
arguments << "--out-peers" << "16";
|
||||
}
|
||||
|
||||
arguments << "--check-updates" << "disabled";
|
||||
arguments << "--non-interactive";
|
||||
|
||||
// --max-concurrency based on threads available.
|
||||
int32_t concurrency = qMax(1, QThread::idealThreadCount() / 2);
|
||||
@@ -349,6 +330,13 @@ QVariantMap DaemonManager::validateDataDir(const QString &dataDir) const
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DaemonManager::checkLmdbExists(QString datadir) {
|
||||
if (datadir.isEmpty() || datadir.isNull()) {
|
||||
datadir = QString::fromStdString(tools::get_default_data_dir());
|
||||
}
|
||||
return validateDataDir(datadir).value("lmdbExists").value<bool>();
|
||||
}
|
||||
|
||||
DaemonManager::DaemonManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_scheduler(this)
|
||||
|
||||
@@ -44,10 +44,10 @@ class DaemonManager : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DaemonManager(QObject *parent = 0);
|
||||
~DaemonManager();
|
||||
|
||||
static DaemonManager * instance(const QStringList *args = nullptr);
|
||||
|
||||
Q_INVOKABLE bool start(const QString &flags, NetworkType::Type nettype, const QString &dataDir = "", const QString &bootstrapNodeAddress = "", bool noSync = false);
|
||||
Q_INVOKABLE bool start(const QString &flags, NetworkType::Type nettype, const QString &dataDir = "", const QString &bootstrapNodeAddress = "", bool noSync = false, bool pruneBlockchain = false);
|
||||
Q_INVOKABLE void stopAsync(NetworkType::Type nettype, const QJSValue& callback);
|
||||
|
||||
Q_INVOKABLE bool noSync() const noexcept;
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
Q_INVOKABLE void sendCommandAsync(const QStringList &cmd, NetworkType::Type nettype, const QJSValue& callback) const;
|
||||
Q_INVOKABLE void exit();
|
||||
Q_INVOKABLE QVariantMap validateDataDir(const QString &dataDir) const;
|
||||
Q_INVOKABLE bool checkLmdbExists(QString datadir);
|
||||
|
||||
private:
|
||||
|
||||
@@ -76,11 +77,6 @@ public slots:
|
||||
void stateChanged(QProcess::ProcessState state);
|
||||
|
||||
private:
|
||||
explicit DaemonManager(QObject *parent = 0);
|
||||
~DaemonManager();
|
||||
|
||||
static DaemonManager * m_instance;
|
||||
static QStringList m_clArgs;
|
||||
std::unique_ptr<QProcess> m_daemon;
|
||||
QMutex m_daemonMutex;
|
||||
QString m_monerod;
|
||||
|
||||
@@ -78,7 +78,14 @@ void Subaddress::setLabel(quint32 accountIndex, quint32 addressIndex, const QStr
|
||||
|
||||
void Subaddress::refresh(quint32 accountIndex) const
|
||||
{
|
||||
m_subaddressImpl->refresh(accountIndex);
|
||||
try
|
||||
{
|
||||
m_subaddressImpl->refresh(accountIndex);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
qCritical() << "Failed to refresh account" << accountIndex << "subaddresses:" << e.what();
|
||||
}
|
||||
getAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -104,10 +104,7 @@ void Wallet::updateConnectionStatusAsync()
|
||||
setConnectionStatus(ConnectionStatus_Connecting);
|
||||
}
|
||||
ConnectionStatus newStatus = static_cast<ConnectionStatus>(m_walletImpl->connected());
|
||||
if (newStatus != m_connectionStatus || !m_initialized) {
|
||||
m_initialized = true;
|
||||
setConnectionStatus(newStatus);
|
||||
}
|
||||
setConnectionStatus(newStatus);
|
||||
// Release lock
|
||||
m_connectionStatusRunning = false;
|
||||
});
|
||||
@@ -115,8 +112,13 @@ void Wallet::updateConnectionStatusAsync()
|
||||
|
||||
Wallet::ConnectionStatus Wallet::connected(bool forceCheck)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return ConnectionStatus_Connecting;
|
||||
}
|
||||
|
||||
// cache connection status
|
||||
if (forceCheck || !m_initialized || (m_connectionStatusTime.elapsed() / 1000 > m_connectionStatusTtl && !m_connectionStatusRunning) || m_connectionStatusTime.elapsed() > 30000) {
|
||||
if (forceCheck || (m_connectionStatusTime.elapsed() / 1000 > m_connectionStatusTtl && !m_connectionStatusRunning) || m_connectionStatusTime.elapsed() > 30000) {
|
||||
qDebug() << "Checking connection status";
|
||||
m_connectionStatusRunning = true;
|
||||
m_connectionStatusTime.restart();
|
||||
@@ -277,14 +279,25 @@ void Wallet::initAsync(
|
||||
{
|
||||
qDebug() << "initAsync: " + daemonAddress;
|
||||
const auto future = m_scheduler.run([this, daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress] {
|
||||
bool success = init(daemonAddress, trustedDaemon, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight, proxyAddress);
|
||||
if (success)
|
||||
m_initialized = init(
|
||||
daemonAddress,
|
||||
trustedDaemon,
|
||||
upperTransactionLimit,
|
||||
isRecovering,
|
||||
isRecoveringFromDevice,
|
||||
restoreHeight,
|
||||
proxyAddress);
|
||||
if (m_initialized)
|
||||
{
|
||||
emit walletCreationHeightChanged();
|
||||
qDebug() << "init async finished - starting refresh";
|
||||
connected(true);
|
||||
startRefresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Failed to initialize the wallet";
|
||||
}
|
||||
});
|
||||
if (future.first)
|
||||
{
|
||||
@@ -519,25 +532,44 @@ void Wallet::pauseRefresh()
|
||||
m_refreshEnabled = false;
|
||||
}
|
||||
|
||||
PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id,
|
||||
quint64 amount, quint32 mixin_count,
|
||||
PendingTransaction::Priority priority)
|
||||
PendingTransaction *Wallet::createTransaction(
|
||||
const QVector<QString> &destinationAddresses,
|
||||
const QString &payment_id,
|
||||
const QVector<QString> &destinationAmounts,
|
||||
quint32 mixin_count,
|
||||
PendingTransaction::Priority priority)
|
||||
{
|
||||
std::vector<std::string> destinations;
|
||||
for (const auto &address : destinationAddresses) {
|
||||
destinations.push_back(address.toStdString());
|
||||
}
|
||||
std::vector<uint64_t> amounts;
|
||||
for (const auto &amount : destinationAmounts) {
|
||||
amounts.push_back(Monero::Wallet::amountFromString(amount.toStdString()));
|
||||
}
|
||||
std::set<uint32_t> subaddr_indices;
|
||||
Monero::PendingTransaction * ptImpl = m_walletImpl->createTransaction(
|
||||
dst_addr.toStdString(), payment_id.toStdString(), amount, mixin_count,
|
||||
static_cast<Monero::PendingTransaction::Priority>(priority), currentSubaddressAccount(), subaddr_indices);
|
||||
PendingTransaction * result = new PendingTransaction(ptImpl,0);
|
||||
Monero::PendingTransaction *ptImpl = m_walletImpl->createTransactionMultDest(
|
||||
destinations,
|
||||
payment_id.toStdString(),
|
||||
amounts,
|
||||
mixin_count,
|
||||
static_cast<Monero::PendingTransaction::Priority>(priority),
|
||||
currentSubaddressAccount(),
|
||||
subaddr_indices);
|
||||
PendingTransaction *result = new PendingTransaction(ptImpl, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Wallet::createTransactionAsync(const QString &dst_addr, const QString &payment_id,
|
||||
quint64 amount, quint32 mixin_count,
|
||||
PendingTransaction::Priority priority)
|
||||
void Wallet::createTransactionAsync(
|
||||
const QVector<QString> &destinationAddresses,
|
||||
const QString &payment_id,
|
||||
const QVector<QString> &destinationAmounts,
|
||||
quint32 mixin_count,
|
||||
PendingTransaction::Priority priority)
|
||||
{
|
||||
m_scheduler.run([this, dst_addr, payment_id, amount, mixin_count, priority] {
|
||||
PendingTransaction *tx = createTransaction(dst_addr, payment_id, amount, mixin_count, priority);
|
||||
emit transactionCreated(tx, dst_addr, payment_id, mixin_count);
|
||||
m_scheduler.run([this, destinationAddresses, payment_id, destinationAmounts, mixin_count, priority] {
|
||||
PendingTransaction *tx = createTransaction(destinationAddresses, payment_id, destinationAmounts, mixin_count, priority);
|
||||
emit transactionCreated(tx, destinationAddresses, payment_id, mixin_count);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -558,7 +590,7 @@ void Wallet::createTransactionAllAsync(const QString &dst_addr, const QString &p
|
||||
{
|
||||
m_scheduler.run([this, dst_addr, payment_id, mixin_count, priority] {
|
||||
PendingTransaction *tx = createTransactionAll(dst_addr, payment_id, mixin_count, priority);
|
||||
emit transactionCreated(tx, dst_addr, payment_id, mixin_count);
|
||||
emit transactionCreated(tx, {dst_addr}, payment_id, mixin_count);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -573,7 +605,7 @@ void Wallet::createSweepUnmixableTransactionAsync()
|
||||
{
|
||||
m_scheduler.run([this] {
|
||||
PendingTransaction *tx = createSweepUnmixableTransaction();
|
||||
emit transactionCreated(tx, "", "", 0);
|
||||
emit transactionCreated(tx, {""}, "", 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -613,17 +645,32 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
|
||||
delete t;
|
||||
}
|
||||
|
||||
void Wallet::estimateTransactionFeeAsync(const QString &destination,
|
||||
quint64 amount,
|
||||
PendingTransaction::Priority priority,
|
||||
const QJSValue &callback)
|
||||
void Wallet::estimateTransactionFeeAsync(
|
||||
const QVector<QString> &destinationAddresses,
|
||||
const QVector<quint64> &amounts,
|
||||
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);
|
||||
m_scheduler.run(
|
||||
[this, destinationAddresses, amounts, priority] {
|
||||
if (destinationAddresses.size() != amounts.size())
|
||||
{
|
||||
return QJSValueList({""});
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, uint64_t>> destinations;
|
||||
destinations.reserve(destinationAddresses.size());
|
||||
for (size_t index = 0; index < destinationAddresses.size(); ++index)
|
||||
{
|
||||
destinations.emplace_back(std::make_pair(destinationAddresses[index].toStdString(), amounts[index]));
|
||||
}
|
||||
|
||||
const uint64_t fee = m_walletImpl->estimateTransactionFee(
|
||||
destinations,
|
||||
static_cast<Monero::PendingTransaction::Priority>(priority));
|
||||
return QJSValueList({QString::fromStdString(Monero::Wallet::displayAmount(fee))});
|
||||
},
|
||||
callback);
|
||||
}
|
||||
|
||||
TransactionHistory *Wallet::history() const
|
||||
@@ -1054,6 +1101,7 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||
, m_connectionStatus(Wallet::ConnectionStatus_Disconnected)
|
||||
, m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS)
|
||||
, m_disconnected(true)
|
||||
, m_initialized(false)
|
||||
, m_currentSubaddressAccount(0)
|
||||
, m_subaddress(nullptr)
|
||||
, m_subaddressModel(nullptr)
|
||||
@@ -1074,7 +1122,6 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||
m_connectionStatusTime.start();
|
||||
m_daemonBlockChainHeightTime.start();
|
||||
m_daemonBlockChainTargetHeightTime.start();
|
||||
m_initialized = false;
|
||||
m_connectionStatusRunning = false;
|
||||
m_daemonUsername = "";
|
||||
m_daemonPassword = "";
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#ifndef WALLET_H
|
||||
#define WALLET_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
@@ -214,15 +216,13 @@ public:
|
||||
Q_INVOKABLE void startRefresh();
|
||||
Q_INVOKABLE void pauseRefresh();
|
||||
|
||||
//! creates transaction
|
||||
Q_INVOKABLE PendingTransaction * createTransaction(const QString &dst_addr, const QString &payment_id,
|
||||
quint64 amount, quint32 mixin_count,
|
||||
PendingTransaction::Priority priority);
|
||||
|
||||
//! creates async transaction
|
||||
Q_INVOKABLE void createTransactionAsync(const QString &dst_addr, const QString &payment_id,
|
||||
quint64 amount, quint32 mixin_count,
|
||||
PendingTransaction::Priority priority);
|
||||
Q_INVOKABLE void createTransactionAsync(
|
||||
const QVector<QString> &destinationAddresses,
|
||||
const QString &payment_id,
|
||||
const QVector<QString> &destinationAmounts,
|
||||
quint32 mixin_count,
|
||||
PendingTransaction::Priority priority);
|
||||
|
||||
//! creates transaction with all outputs
|
||||
Q_INVOKABLE PendingTransaction * createTransactionAll(const QString &dst_addr, const QString &payment_id,
|
||||
@@ -253,10 +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);
|
||||
Q_INVOKABLE void estimateTransactionFeeAsync(
|
||||
const QVector<QString> &destinationAddresses,
|
||||
const QVector<quint64> &amounts,
|
||||
PendingTransaction::Priority priority,
|
||||
const QJSValue &callback);
|
||||
|
||||
//! returns transaction history
|
||||
TransactionHistory * history() const;
|
||||
@@ -380,7 +381,11 @@ signals:
|
||||
void deviceShowAddressShowed();
|
||||
|
||||
// emitted when transaction is created async
|
||||
void transactionCreated(PendingTransaction * transaction, QString address, QString paymentId, quint32 mixinCount);
|
||||
void transactionCreated(
|
||||
PendingTransaction *transaction,
|
||||
const QVector<QString> &addresses,
|
||||
const QString &paymentId,
|
||||
quint32 mixinCount);
|
||||
|
||||
void connectionStatusChanged(int status) const;
|
||||
void currentSubaddressAccountChanged() const;
|
||||
@@ -413,6 +418,13 @@ private:
|
||||
quint64 restoreHeight,
|
||||
const QString& proxyAddress);
|
||||
|
||||
PendingTransaction *createTransaction(
|
||||
const QVector<QString> &destinationAddresses,
|
||||
const QString &payment_id,
|
||||
const QVector<QString> &destinationAmounts,
|
||||
quint32 mixin_count,
|
||||
PendingTransaction::Priority priority);
|
||||
|
||||
bool disconnected() const;
|
||||
bool refreshing() const;
|
||||
void refreshingSet(bool value);
|
||||
@@ -444,7 +456,7 @@ private:
|
||||
int m_connectionStatusTtl;
|
||||
mutable QElapsedTimer m_connectionStatusTime;
|
||||
bool m_disconnected;
|
||||
mutable bool m_initialized;
|
||||
std::atomic<bool> m_initialized;
|
||||
uint32_t m_currentSubaddressAccount;
|
||||
Subaddress * m_subaddress;
|
||||
mutable SubaddressModel * m_subaddressModel;
|
||||
|
||||
@@ -251,7 +251,7 @@ QString WalletManager::errorString() const
|
||||
return tr("Unknown error");
|
||||
}
|
||||
|
||||
quint64 WalletManager::maximumAllowedAmount() const
|
||||
quint64 WalletManager::maximumAllowedAmount()
|
||||
{
|
||||
return Monero::Wallet::maximumAllowedAmount();
|
||||
}
|
||||
@@ -266,7 +266,7 @@ QString WalletManager::displayAmount(quint64 amount)
|
||||
return QString::fromStdString(Monero::Wallet::displayAmount(amount));
|
||||
}
|
||||
|
||||
quint64 WalletManager::amountFromString(const QString &amount) const
|
||||
quint64 WalletManager::amountFromString(const QString &amount)
|
||||
{
|
||||
return Monero::Wallet::amountFromString(amount.toStdString());
|
||||
}
|
||||
@@ -276,6 +276,17 @@ quint64 WalletManager::amountFromDouble(double amount) const
|
||||
return Monero::Wallet::amountFromDouble(amount);
|
||||
}
|
||||
|
||||
QString WalletManager::amountsSumFromStrings(const QVector<QString> &amounts)
|
||||
{
|
||||
quint64 sum = 0;
|
||||
for (const auto &amountString : amounts)
|
||||
{
|
||||
const quint64 amount = amountFromString(amountString);
|
||||
sum = sum + std::min(maximumAllowedAmount() - sum, amount);
|
||||
}
|
||||
return QString::number(sum);
|
||||
}
|
||||
|
||||
bool WalletManager::paymentIdValid(const QString &payment_id) const
|
||||
{
|
||||
return Monero::Wallet::paymentIdValid(payment_id.toStdString());
|
||||
|
||||
@@ -133,9 +133,10 @@ public:
|
||||
|
||||
//! since we can't call static method from QML, move it to this class
|
||||
Q_INVOKABLE static QString displayAmount(quint64 amount);
|
||||
Q_INVOKABLE quint64 amountFromString(const QString &amount) const;
|
||||
Q_INVOKABLE static quint64 amountFromString(const QString &amount);
|
||||
Q_INVOKABLE quint64 amountFromDouble(double amount) const;
|
||||
Q_INVOKABLE quint64 maximumAllowedAmount() const;
|
||||
Q_INVOKABLE static QString amountsSumFromStrings(const QVector<QString> &amounts);
|
||||
Q_INVOKABLE static quint64 maximumAllowedAmount();
|
||||
|
||||
// QML JS engine doesn't support unsigned integers
|
||||
Q_INVOKABLE QString maximumAllowedAmountAsString() const;
|
||||
|
||||
@@ -108,10 +108,6 @@ Q_IMPORT_PLUGIN(QTgaPlugin)
|
||||
Q_IMPORT_PLUGIN(QTiffPlugin)
|
||||
Q_IMPORT_PLUGIN(QWbmpPlugin)
|
||||
Q_IMPORT_PLUGIN(QWebpPlugin)
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
Q_IMPORT_PLUGIN(QtQmlPlugin)
|
||||
#endif
|
||||
Q_IMPORT_PLUGIN(QQmlDebuggerServiceFactory)
|
||||
Q_IMPORT_PLUGIN(QQmlInspectorServiceFactory)
|
||||
Q_IMPORT_PLUGIN(QLocalClientConnectionFactory)
|
||||
@@ -124,6 +120,10 @@ Q_IMPORT_PLUGIN(QQmlDebugServerFactory)
|
||||
Q_IMPORT_PLUGIN(QTcpServerConnectionFactory)
|
||||
Q_IMPORT_PLUGIN(QGenericEnginePlugin)
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
Q_IMPORT_PLUGIN(QtQmlPlugin)
|
||||
#endif
|
||||
Q_IMPORT_PLUGIN(QtQmlModelsPlugin)
|
||||
Q_IMPORT_PLUGIN(QtQuick2Plugin)
|
||||
Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin)
|
||||
Q_IMPORT_PLUGIN(QtGraphicalEffectsPlugin)
|
||||
@@ -191,6 +191,12 @@ int main(int argc, char *argv[])
|
||||
// Turn off colors in monerod log output.
|
||||
qputenv("TERM", "goaway");
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
QDir::setCurrent(QDir(MacOSHelper::bundlePath() + QDir::separator() + "..").canonicalPath());
|
||||
#endif
|
||||
|
||||
qputenv("QML_DISABLE_DISK_CACHE", "1");
|
||||
|
||||
MainApp app(argc, argv);
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -306,11 +312,6 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Desktop entry
|
||||
#ifdef Q_OS_LINUX
|
||||
registerXdgMime(app);
|
||||
#endif
|
||||
|
||||
IPC *ipc = new IPC(&app);
|
||||
QStringList posArgs = parser.positionalArguments();
|
||||
|
||||
@@ -327,10 +328,6 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw
|
||||
// start listening
|
||||
QTimer::singleShot(0, ipc, SLOT(bind()));
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
QDir::setCurrent(QDir(MacOSHelper::bundlePath() + QDir::separator() + "..").canonicalPath());
|
||||
#endif
|
||||
|
||||
// screen settings
|
||||
// Mobile is designed on 128dpi
|
||||
qreal ref_dpi = 128;
|
||||
@@ -455,8 +452,8 @@ Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by tw
|
||||
|
||||
// Exclude daemon manager from IOS
|
||||
#ifndef Q_OS_IOS
|
||||
DaemonManager * daemonManager = DaemonManager::instance();
|
||||
engine.rootContext()->setContextProperty("daemonManager", daemonManager);
|
||||
DaemonManager daemonManager;
|
||||
engine.rootContext()->setContextProperty("daemonManager", &daemonManager);
|
||||
#endif
|
||||
|
||||
engine.rootContext()->setContextProperty("isWindows", isWindows);
|
||||
|
||||
@@ -27,10 +27,16 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "oshelper.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QGuiApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QScreen>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryFile>
|
||||
#include <QWindow>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
@@ -46,13 +52,49 @@
|
||||
#endif
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
#include <X11/XKBlib.h>
|
||||
#undef Bool
|
||||
#undef KeyPress
|
||||
#undef KeyRelease
|
||||
#undef FocusIn
|
||||
#undef FocusOut
|
||||
// #undef those Xlib #defines that conflict with QEvent::Type enum
|
||||
#include "qt/utils.h"
|
||||
#endif
|
||||
|
||||
#include "QR-Code-scanner/Decoder.h"
|
||||
#include "qt/ScopeGuard.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
QPixmap screenshot()
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
return MacOSHelper::screenshot();
|
||||
#else
|
||||
std::unordered_set<QWindow *> hidden;
|
||||
const QWindowList windows = QGuiApplication::allWindows();
|
||||
for (QWindow *window : windows)
|
||||
{
|
||||
if (window->isVisible())
|
||||
{
|
||||
hidden.emplace(window);
|
||||
window->hide();
|
||||
}
|
||||
}
|
||||
const auto unhide = sg::make_scope_guard([&hidden]() {
|
||||
for (QWindow *window : hidden)
|
||||
{
|
||||
window->show();
|
||||
}
|
||||
});
|
||||
|
||||
return QGuiApplication::primaryScreen()->grabWindow(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
bool openFolderAndSelectItem(const QString &filePath)
|
||||
{
|
||||
@@ -85,11 +127,38 @@ OSHelper::OSHelper(QObject *parent) : QObject(parent)
|
||||
|
||||
}
|
||||
|
||||
void OSHelper::createDesktopEntry() const
|
||||
{
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
registerXdgMime();
|
||||
#endif
|
||||
}
|
||||
|
||||
QString OSHelper::downloadLocation() const
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||
}
|
||||
|
||||
QList<QString> OSHelper::grabQrCodesFromScreen() const
|
||||
{
|
||||
QList<QString> codes;
|
||||
|
||||
try
|
||||
{
|
||||
const QImage image = screenshot().toImage();
|
||||
const std::vector<std::string> decoded = QrDecoder().decode(image);
|
||||
std::for_each(decoded.begin(), decoded.end(), [&codes](const std::string &code) {
|
||||
codes.push_back(QString::fromStdString(code));
|
||||
});
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
qWarning() << e.what();
|
||||
}
|
||||
|
||||
return codes;
|
||||
}
|
||||
|
||||
bool OSHelper::openContainingFolder(const QString &filePath) const
|
||||
{
|
||||
QString canonicalFilePath = QFileInfo(filePath).canonicalFilePath();
|
||||
@@ -154,6 +223,7 @@ bool OSHelper::isCapsLock() const
|
||||
unsigned n;
|
||||
XkbGetIndicatorState(d, XkbUseCoreKbd, &n);
|
||||
caps_state = (n & 0x01) == 1;
|
||||
XCloseDisplay(d);
|
||||
}
|
||||
return caps_state;
|
||||
#elif defined(Q_OS_MAC)
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
#ifndef OSHELPER_H
|
||||
#define OSHELPER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
/**
|
||||
* @brief The OSHelper class - exports to QML some OS-related functions
|
||||
*/
|
||||
@@ -41,7 +43,9 @@ class OSHelper : public QObject
|
||||
public:
|
||||
explicit OSHelper(QObject *parent = 0);
|
||||
|
||||
Q_INVOKABLE void createDesktopEntry() const;
|
||||
Q_INVOKABLE QString downloadLocation() const;
|
||||
Q_INVOKABLE QList<QString> grabQrCodesFromScreen() 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;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
file(GLOB_RECURSE SOURCES *.cpp)
|
||||
file(GLOB_RECURSE HEADERS *.h)
|
||||
|
||||
find_library(GCRYPT_LIBRARY gcrypt)
|
||||
find_library(GPG_ERROR_LIBRARY gpg-error)
|
||||
find_library(GCRYPT_LIBRARY gcrypt REQUIRED)
|
||||
find_path(GCRYPT_INCLUDE_DIR gcrypt.h REQUIRED)
|
||||
|
||||
find_library(GPG_ERROR_LIBRARY gpg-error REQUIRED)
|
||||
|
||||
add_library(openpgp
|
||||
${SOURCES}
|
||||
@@ -10,7 +12,8 @@ add_library(openpgp
|
||||
|
||||
target_include_directories(openpgp
|
||||
PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include)
|
||||
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include
|
||||
${GCRYPT_INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(openpgp
|
||||
PUBLIC
|
||||
|
||||
@@ -179,13 +179,13 @@ bool MoneroSettings::portable() const
|
||||
return this->m_settings && this->m_settings->fileName() == portableFilePath();
|
||||
}
|
||||
|
||||
bool MoneroSettings::portableConfigExists() const
|
||||
bool MoneroSettings::portableConfigExists()
|
||||
{
|
||||
QFileInfo info(portableFilePath());
|
||||
return info.exists() && info.isFile();
|
||||
}
|
||||
|
||||
QString MoneroSettings::portableFilePath() const
|
||||
QString MoneroSettings::portableFilePath()
|
||||
{
|
||||
static QString filename(QDir(portableFolderName()).absoluteFilePath("settings.ini"));
|
||||
return filename;
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
Q_INVOKABLE void setWritable(bool enabled);
|
||||
|
||||
static QString portableFolderName();
|
||||
static bool portableConfigExists();
|
||||
|
||||
public slots:
|
||||
void _q_propertyChanged();
|
||||
@@ -84,8 +85,7 @@ private:
|
||||
void store();
|
||||
|
||||
bool portable() const;
|
||||
bool portableConfigExists() const;
|
||||
QString portableFilePath() const;
|
||||
static QString portableFilePath();
|
||||
std::unique_ptr<QSettings> portableSettings() const;
|
||||
std::unique_ptr<QSettings> unportableSettings() const;
|
||||
void swap(std::unique_ptr<QSettings> newSettings);
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#ifndef MACOSHELPER_H
|
||||
#define MACOSHELPER_H
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
class MacOSHelper
|
||||
{
|
||||
MacOSHelper() {}
|
||||
@@ -36,6 +38,7 @@ class MacOSHelper
|
||||
public:
|
||||
static bool isCapsLock();
|
||||
static bool openFolderAndSelectItem(const QUrl &path);
|
||||
static QPixmap screenshot();
|
||||
static QString bundlePath();
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +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.
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QtMac>
|
||||
@@ -37,6 +39,8 @@
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Availability.h>
|
||||
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
bool MacOSHelper::isCapsLock()
|
||||
{
|
||||
#ifdef __MAC_10_12
|
||||
@@ -56,6 +60,41 @@ bool MacOSHelper::openFolderAndSelectItem(const QUrl &path)
|
||||
return true;
|
||||
}
|
||||
|
||||
QPixmap MacOSHelper::screenshot()
|
||||
{
|
||||
std::unordered_set<uintptr_t> appWindowIds;
|
||||
for (NSWindow *window in [NSApp windows])
|
||||
{
|
||||
appWindowIds.insert((uintptr_t)[window windowNumber]);
|
||||
}
|
||||
|
||||
CFArrayRef onScreenWindows = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
|
||||
const auto onScreenWindowsClenaup = sg::make_scope_guard([&onScreenWindows]() {
|
||||
CFRelease(onScreenWindows);
|
||||
});
|
||||
|
||||
CFMutableArrayRef foreignWindows = CFArrayCreateMutable(NULL, CFArrayGetCount(onScreenWindows), NULL);
|
||||
const auto foreignWindowsClenaup = sg::make_scope_guard([&foreignWindows]() {
|
||||
CFRelease(foreignWindows);
|
||||
});
|
||||
|
||||
for (CFIndex index = 0, count = CFArrayGetCount(onScreenWindows); index < count; ++index)
|
||||
{
|
||||
const uintptr_t windowId = reinterpret_cast<const uintptr_t>(CFArrayGetValueAtIndex(onScreenWindows, index));
|
||||
if (appWindowIds.find(windowId) == appWindowIds.end())
|
||||
{
|
||||
CFArrayAppendValue(foreignWindows, reinterpret_cast<const void *>(windowId));
|
||||
}
|
||||
}
|
||||
|
||||
CGImageRef image = CGWindowListCreateImageFromArray(CGRectInfinite, foreignWindows, kCGWindowListOptionAll);
|
||||
const auto imageClenaup = sg::make_scope_guard([&image]() {
|
||||
CFRelease(image);
|
||||
});
|
||||
|
||||
return QtMac::fromCGImageRef(image);
|
||||
}
|
||||
|
||||
QString MacOSHelper::bundlePath()
|
||||
{
|
||||
NSBundle *main = [NSBundle mainBundle];
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <QtCore>
|
||||
#include <QApplication>
|
||||
#include <QCoreApplication>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "TailsOS.h"
|
||||
@@ -88,7 +88,7 @@ QString getAccountName(){
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QString xdgMime(QApplication &app){
|
||||
QString xdgMime(){
|
||||
return QString(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Monero GUI\n"
|
||||
@@ -105,32 +105,32 @@ QString xdgMime(QApplication &app){
|
||||
"StartupNotify=true\n"
|
||||
"X-GNOME-Bugzilla-Bugzilla=GNOME\n"
|
||||
"X-GNOME-UsesNotifications=true\n"
|
||||
).arg(app.applicationFilePath());
|
||||
"StartupWMClass=monero-wallet-gui\n"
|
||||
).arg(QCoreApplication::applicationFilePath());
|
||||
}
|
||||
|
||||
void registerXdgMime(QApplication &app){
|
||||
void registerXdgMime(){
|
||||
// Register desktop entry
|
||||
// - MacOS handled via Info.plist
|
||||
// - Windows handled in the installer by rbrunner7
|
||||
// - Linux written to `QStandardPaths::ApplicationsLocation`
|
||||
// - Tails written to persistent dotfiles
|
||||
QString mime = xdgMime(app);
|
||||
QString mime = xdgMime();
|
||||
QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
||||
QString filePath = QString("%1/monero-gui.desktop").arg(appPath);
|
||||
|
||||
if (TailsOS::detect() && TailsOS::detectDotPersistence() && TailsOS::usePersistence) {
|
||||
TailsOS::persistXdgMime(filePath, mime);
|
||||
return;
|
||||
if (TailsOS::detect())
|
||||
{
|
||||
if (TailsOS::detectDotPersistence() && TailsOS::usePersistence)
|
||||
{
|
||||
TailsOS::persistXdgMime(filePath, mime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QDir().mkpath(QFileInfo(filePath).path());
|
||||
fileWrite(filePath, mime);
|
||||
}
|
||||
|
||||
QFileInfo file(filePath);
|
||||
QDir().mkpath(file.path()); // ensure directory exists
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
qDebug() << "Writing xdg mime: " << filePath;
|
||||
#endif
|
||||
|
||||
fileWrite(filePath, mime);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ QByteArray fileOpen(QString path);
|
||||
bool fileWrite(QString path, QString data);
|
||||
QString getAccountName();
|
||||
#ifdef Q_OS_LINUX
|
||||
QString xdgMime(QApplication &app);
|
||||
void registerXdgMime(QApplication &app);
|
||||
QString xdgMime();
|
||||
void registerXdgMime();
|
||||
#endif
|
||||
const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)");
|
||||
QString randomUserAgent();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3907
translations/monero-core_az.ts
Normal file
3907
translations/monero-core_az.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user