Compare commits

..

294 Commits

Author SHA1 Message Date
luigi1111
3c69023135 Merge pull request #3037
67be96e Translations from weblate (Monero-Weblate)
2020-08-08 20:48:31 -05:00
luigi1111
fad8dab165 Merge pull request #3035
9ce88c6 build: prepare v0.16.0.3 (selsta)
2020-08-08 20:46:53 -05:00
Anonymous
67be96e6d5 Translations from weblate
Languages:

- German
Currently translated at 97.2% (669 of 688 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/de/

- Russian
Currently translated at 97.9% (673 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/ru/

- Romanian
Currently translated at 85.5% (588 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/ro/

- Czech
Currently translated at 54.1% (372 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/cs/

-  Chinese (Simplified)
Currently translated at 94.3% (648 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/zh_Hans/

- Norwegian Bokmål
Currently translated at 90.3% (621 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/nb_NO/

- Turkish
Currently translated at 82.3% (566 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/tr/

- Greek
Currently translated at 46.5% (320 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/el/

- Japanese
Currently translated at 86.8% (597 of 687 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/ja/

- Italian
Currently translated at 92.2% (632 of 685 strings)
Translate-URL: https://translate.getmonero.org/projects/monero/gui-wallet/it/

  All languages were updated:

    Found 690 source text(s) (12 new and 678 already existing)
    Removed 9 obsolete entries

  Translators:

* Italian

    * 8uDD4T
    * erciccione

* Romanian

    * Marius Tanislav

* Turkish

    * xmoreee

* Japanese

    * Lana

* French

    * Anonymous

* Czech

    * Michal Poupe
    * dskch83

* Chinese (Simplified)

    * anony_xmr
    * C3Pool
    * Muge Niu
    * Anonymous
    * razorshaman909

* Greek

    * Stavros
    * warraxxx
    * Georgios Apostolopoulos

* Norwegian Bokmål

    * bitbooz

Co-authored-by: GreenPiece <villa@posteo.net>
Co-authored-by: v1docq47 <chiptune@protonmail.ch>
Co-authored-by: Agent LvM <agent.lvm@gmail.com>
Co-authored-by: dskch83 <skarkadaniel@gmail.com>
Co-authored-by: razorshaman909 <work909ang@gmail.com>
Co-authored-by: Muge Niu <mugeniu12138@gmail.com>
Co-authored-by: C3Pool <hkfoan@gmail.com>
Co-authored-by: xmoreee <sskacarfun@gmail.com>
Co-authored-by: warraxxx <kpantelidis@hotmail.gr>
Co-authored-by: 8uDD4T <8uDD4T@protonmail.com>
2020-08-06 12:30:49 +02:00
selsta
9ce88c6aeb build: prepare v0.16.0.3 2020-08-05 14:24:27 +02:00
luigi1111
7eeda0a8f0 Merge pull request #3031
d1bde4f workflows: fix mac cmake (selsta)
2020-07-29 13:46:44 -05:00
luigi1111
9220937c4b Merge pull request #3027
0db4c36 QrScanThread: fix Qt 5.15 compability (selsta)
2020-07-29 13:35:36 -05:00
luigi1111
6aaf294f0b Merge pull request #3019
dcc040a WalletManager: instantiable QML type (xiphon)
2020-07-29 13:34:33 -05:00
luigi1111
721a0cbf91 Merge pull request #3018
352da92 QrCodeScanner: use parse_uri_to_object to parse scanned codes (xiphon)
2020-07-29 13:33:27 -05:00
selsta
d1bde4f8b2 workflows: fix mac cmake 2020-07-29 20:30:06 +02:00
selsta
0db4c36db0 QrScanThread: fix Qt 5.15 compability 2020-07-29 17:40:58 +02:00
xiphon
dcc040a119 WalletManager: instantiable QML type 2020-07-29 15:17:02 +00:00
luigi1111
63e32c4a83 Merge pull request #3017
99907e5 WalletManager: parse_uri_to_object extra parameters support (xiphon)
2020-07-29 09:53:31 -05:00
luigi1111
7972d8a219 Merge pull request #3015
e8bcabe cmake: fix static libraries linking (xiphon)
2020-07-29 09:50:05 -05:00
luigi1111
872b81fe39 Merge pull request #3014
829414e Transfer: fix 'Send' and 'Sweep' functionality (xiphon)
2020-07-29 09:49:04 -05:00
luigi1111
6136e88fcb Merge pull request #3013
085abb7 cmake: fix MacOS build (Qt installer) (xiphon)
2020-07-29 09:47:21 -05:00
luigi1111
c77637d141 Merge pull request #3012
410897c SettingsLog: add command history (rating89us)
2020-07-29 09:46:04 -05:00
luigi1111
0d047035fb Merge pull request #3007
13ee9b1 KeysFiles: fix WalletKeysFilesModel, should be instantiable QML type (xiphon)
2020-07-29 09:44:45 -05:00
luigi1111
b5aa659757 Merge pull request #3004
36262f0 version: use MONERO_VERSION_FULL const as monero core version (xiphon)
2020-07-29 09:42:24 -05:00
luigi1111
88d297d8cc Merge pull request #3002
f78c95b cmake: build bundle on macOS (selsta)
2020-07-29 09:38:55 -05:00
xiphon
352da92229 QrCodeScanner: use parse_uri_to_object to parse scanned codes 2020-07-16 17:30:00 +00:00
xiphon
99907e539a WalletManager: parse_uri_to_object extra parameters support 2020-07-16 17:28:00 +00:00
xiphon
e8bcabe66b cmake: fix static libraries linking 2020-07-15 23:26:14 +00:00
xiphon
829414ed01 Transfer: fix 'Send' and 'Sweep' functionality 2020-07-15 04:02:42 +00:00
xiphon
36262f029a version: use MONERO_VERSION_FULL const as monero core version 2020-07-15 03:45:03 +00:00
xiphon
13ee9b1f26 KeysFiles: fix WalletKeysFilesModel, should be instantiable QML type 2020-07-14 23:44:46 +00:00
xiphon
085abb798a cmake: fix MacOS build (Qt installer) 2020-07-14 23:36:30 +00:00
luigi1111
c137a6ea36 Merge pull request #3010
2fd3974 WalletManager: displayAmount should be static (xiphon)
2020-07-14 15:51:01 -05:00
luigi1111
2cea0fc669 Merge pull request #3009
7c50e1f PasswordDialog: focus on gui start (selsta)
2020-07-14 15:50:14 -05:00
luigi1111
aaa1cb47c0 Merge pull request #3006
dec5dcf KeysFiles: don't use WalletManager in findWallets (xiphon)
2020-07-14 15:49:19 -05:00
luigi1111
1577ce278b Merge pull request #3001
df885b6 cmake: set current monero-gui version (selsta)
2020-07-14 15:48:15 -05:00
luigi1111
9d48f96fc8 Merge pull request #2999
b22fb24 Donate to Monero feature (xmrdsc)
2020-07-14 15:47:29 -05:00
luigi1111
1a51e93e55 Merge pull request #2991
c9d36b1 cmake: translations deps handling, build translations as a library (xiphon)
d931022 cmake: implement MacOS 'release' build + CI support (xiphon)
2020-07-14 15:46:09 -05:00
luigi1111
b62b821002 Merge pull request #2989
8c8faf2 Support comma seperator for amount field, dynamically change to dot. Fixes #2951 (xmrdsc)
2020-07-14 15:45:01 -05:00
luigi1111
f3ee46175c Merge pull request #2935
f1e3926 WizardWalletInput: disallow (back)slash (selsta)
2020-07-14 15:43:58 -05:00
luigi1111
17a3c51a43 Merge pull request #2915
527adcb Utils: simplify for translations (selsta)
2020-07-14 15:43:08 -05:00
rating89us
410897ced7 SettingsLog: add command history 2020-07-14 22:38:54 +02:00
xiphon
2fd3974194 WalletManager: displayAmount should be static 2020-07-14 17:10:25 +00:00
selsta
7c50e1ff4b PasswordDialog: focus on gui start 2020-07-14 16:52:40 +02:00
xiphon
dec5dcf25c KeysFiles: don't use WalletManager in findWallets 2020-07-14 01:39:46 +00:00
xiphon
c9d36b1302 cmake: translations deps handling, build translations as a library 2020-07-09 16:42:32 +00:00
xiphon
d931022963 cmake: implement MacOS 'release' build + CI support 2020-07-09 15:23:04 +00:00
selsta
df885b6eaa cmake: set current monero-gui version 2020-07-09 08:13:08 +02:00
selsta
f78c95b73b cmake: build bundle on macOS 2020-07-09 08:12:36 +02:00
dsc
b22fb24e47 Donate to Monero feature 2020-07-08 22:50:48 +02:00
dsc
8c8faf29e4 Support comma seperator for amount field, dynamically change to dot. Fixes #2951 2020-07-08 22:14:28 +02:00
luigi1111
b7b1221221 Merge pull request #2997
f1b4eb6 history: remove trailing zeros in amount + fix amount display (rating89us)
2020-07-08 13:57:44 -05:00
luigi1111
38c3534ead Merge pull request #2996
d37c4dd history: fix search filter not working when there is a pending transaction (rating89us)
2020-07-08 13:56:35 -05:00
luigi1111
f631b75f0a Merge pull request #2994
50c22ca Mining: Indicate probability of finding a block in one day (glv2)
2020-07-08 13:55:27 -05:00
luigi1111
11fd19e0f8 Merge pull request #2988
2522ff1 Prevent textbox from turning active when window is not active. Fixes #2924 (xmrdsc)
2020-07-08 13:53:41 -05:00
luigi1111
1572b4cf79 Merge pull request #2978
fead82b RemoteNodeEdit: initialAddress, add square brackets on ipv6 input (xiphon)
2020-07-08 13:52:22 -05:00
luigi1111
10a47d783e Merge pull request #2977
f55bf48 cmake: fix git tag + commit detection and version.js generation (xiphon)
2020-07-08 13:51:19 -05:00
luigi1111
14cc1d096a Merge pull request #2976
a00ed6a cmake: implement translations support (xiphon)
2020-07-08 13:50:18 -05:00
luigi1111
840421fd7b Merge pull request #2974
7677103 docker: implement Docker Linux 'release-static' build (xiphon)
2020-07-08 13:49:07 -05:00
luigi1111
51739dfb22 Merge pull request #2949
1edc068 PasswordDialog: remove already defined z value (selsta)
2020-07-08 13:36:52 -05:00
luigi1111
be5e6772a4 Merge pull request #2948
53066ae QML: fix some Qt 5.15 warnings (selsta)
2020-07-08 13:35:55 -05:00
luigi1111
c869390272 Merge pull request #2940
c672d80 docs: add deploy readme (selsta)
2020-07-08 13:34:46 -05:00
Guillaume Le Vaillant
50c22ca300 Mining: Indicate probability of finding a block in one day 2020-07-08 19:30:27 +02:00
rating89us
f1b4eb6c56 history: remove trailing zeros in amount + fix amount display 2020-07-08 15:05:11 +02:00
rating89us
d37c4ddef2 history: fix search filter not working when there is a pending transaction 2020-07-08 15:01:19 +02:00
dsc
2522ff13fe Prevent textbox from turning active when window is not active. Fixes #2924 2020-07-07 15:33:54 +02:00
luigi1111
596b71b293 Merge pull request #2982
8159b75 cmake: fix monero-wallet-gui target dependencies (xiphon)
2020-07-05 23:45:33 -05:00
xiphon
8159b75be3 cmake: fix monero-wallet-gui target dependencies 2020-07-02 23:32:55 +00:00
xiphon
7677103f1b docker: implement Docker Linux 'release-static' build 2020-07-02 18:13:51 +00:00
xiphon
fead82b198 RemoteNodeEdit: initialAddress, add square brackets on ipv6 input 2020-07-02 13:39:56 +00:00
xiphon
f55bf48bad cmake: fix git tag + commit detection and version.js generation 2020-07-01 01:48:52 +00:00
luigi1111
e0faddf964 Merge pull request #2975
b54127e main: disable NetworkAccessBlockingFactory.h if Qt < 5.12 (selsta)
2020-06-30 14:43:32 -05:00
xiphon
a00ed6a75a cmake: implement translations support 2020-06-30 17:07:35 +00:00
selsta
b54127e997 main: disable NetworkAccessBlockingFactory.h if Qt < 5.12
Due to a potential Qt bug qrc:///lang/languages.xml gets
blocked resulting in broken translations.
2020-06-30 04:21:14 +02:00
selsta
c672d8016d docs: add deploy readme 2020-06-30 01:53:35 +02:00
luigi1111
ccd8eb1c3a Merge pull request #2967
1c62ede Revert 'main: update balance only when wallet is synced' (selsta)
2020-06-24 13:36:53 -05:00
luigi1111
d5b165bde2 Merge pull request #2966
34439af build: set submodule to v0.16.0.1 (selsta)
2020-06-24 13:34:33 -05:00
luigi1111
c978027933 Merge pull request #2964
e1e862b cmake: implement Linux 'release-static' build target (xiphon)
2020-06-24 13:10:35 -05:00
luigi1111
9deec4dad0 Merge pull request #2957
749460f README: update donation fund address (selsta)
2020-06-24 13:08:55 -05:00
luigi1111
e306992ce8 Merge pull request #2955
f64dcde simple mode: enable settings log tab (selsta)
2020-06-24 13:07:53 -05:00
luigi1111
19daa074ca Merge pull request #2909
66e7696 QML: disable networking (selsta)
2020-06-24 13:06:57 -05:00
selsta
1c62edeff4 Revert "main: update balance only when wallet is synced"
This reverts commit 333c9ee311.
2020-06-24 04:02:27 +02:00
selsta
34439af67e build: set submodule to v0.16.0.1 2020-06-24 03:59:45 +02:00
xiphon
e1e862bce4 cmake: implement Linux 'release-static' build target 2020-06-21 02:03:26 +00:00
luigi1111
0b2e74cdb5 Merge pull request #2963
a3fc675 cmake: fix x11 linking (xiphon)
2020-06-19 16:58:28 -05:00
luigi1111
3fee17e564 Merge pull request #2933
1e7d829 openpgp: fix gcc 5.4.0 compilation (xiphon)
2020-06-19 16:56:05 -05:00
xiphon
1e7d8293cb openpgp: fix gcc 5.4.0 compilation 2020-06-19 21:53:47 +00:00
xiphon
a3fc6754e0 cmake: fix x11 linking 2020-06-19 21:50:42 +00:00
luigi1111
8354c251c5 Merge pull request #2962
27532dc cmake: fix USE_DEVICE_TREZOR option (xiphon)
2020-06-19 16:16:57 -05:00
luigi1111
8f5053bd61 Merge pull request #2961
52c090b cmake: downgrade minimum required boost version to 1.58 (xiphon)
2020-06-19 16:16:08 -05:00
luigi1111
92b0a115f4 Merge pull request #2959
c0e0626 SettingsLayout: ask password for password relevant setting (selsta)
2020-06-19 16:15:20 -05:00
luigi1111
6a3e1aaf40 Merge pull request #2950
d18af7d LeftPanel: simpifly color binding (selsta)
2020-06-19 16:14:31 -05:00
luigi1111
c32e11d3e8 Merge pull request #2947
333c9ee main: update balance only when wallet is synced (rating89us)
2020-06-19 16:13:48 -05:00
luigi1111
9580c19da3 Merge pull request #2937
c6d5c5d workflows: update msys2 setup action v0 -> v1 (selsta)
2020-06-19 16:12:52 -05:00
xiphon
27532dc1bf cmake: fix USE_DEVICE_TREZOR option 2020-06-17 14:04:35 +00:00
selsta
c0e0626b84 SettingsLayout: ask password for password relevant setting 2020-06-17 10:01:42 +02:00
selsta
c6d5c5dc3a workflows: update msys2 setup action v0 -> v1 2020-06-15 23:07:48 +02:00
selsta
749460fb46 README: update donation fund address 2020-06-15 22:23:19 +02:00
luigi1111
c9ee76c2ee Merge pull request #2943
ef5d855 openpgp: implement public subkeys support (xiphon)
2020-06-15 15:11:56 -05:00
luigi1111
94dbf179d5 Merge pull request #2942
ff4de8e updater: use monero tools::sha256sum (xiphon)
2020-06-15 15:10:47 -05:00
luigi1111
7deecbfdf6 Merge pull request #2941
e6f3057 windeploy_helper: add libgcrypt and update 65 -> 67 (selsta)
2020-06-15 15:09:58 -05:00
luigi1111
29a742ba26 Merge pull request #2928
7f0c199 workflows: Windows CMake 'release-win64' target CI (xiphon)
2020-06-15 15:09:08 -05:00
luigi1111
47559e51be Merge pull request #2927
1580c3a DaemonManager: remove max-concurrency upper bound (selsta)
2020-06-15 15:07:59 -05:00
selsta
f64dcde600 simple mode: enable settings log tab 2020-06-15 13:17:42 +02:00
xiphon
52c090b82f cmake: downgrade minimum required boost version to 1.58 2020-06-14 12:30:40 +00:00
selsta
1edc068047 PasswordDialog: remove already defined z value
main already defines z
2020-06-13 04:47:17 +02:00
selsta
d18af7da72 LeftPanel: simpifly color binding 2020-06-13 03:34:16 +02:00
selsta
53066ae92b QML: fix some Qt 5.15 warnings 2020-06-12 03:10:32 +02:00
rating89us
333c9ee311 main: update balance only when wallet is synced 2020-06-12 00:08:04 +02:00
xiphon
ff4de8e8f7 updater: use monero tools::sha256sum 2020-06-10 15:11:15 +00:00
xiphon
ef5d855950 openpgp: implement public subkeys support 2020-06-10 12:48:55 +00:00
selsta
e6f30578c0 windeploy_helper: add libgcrypt and update 65 -> 67 2020-06-10 00:38:07 +02:00
selsta
f1e3926192 WizardWalletInput: disallow (back)slash 2020-06-04 18:33:07 +02:00
xiphon
7f0c19950b workflows: Windows CMake 'release-win64' target CI 2020-06-04 00:30:47 +00:00
selsta
1580c3a574 DaemonManager: remove max-concurrency upper bound 2020-05-29 13:58:53 +02:00
luigi1111
c8f4355e15 Merge pull request #2926
39561f8 cmake: workflows: implement 'release' Linux build target + CI (xiphon)
2020-05-28 12:55:08 -05:00
xiphon
39561f8ead cmake: workflows: implement 'release' Linux build target + CI 2020-05-28 14:51:52 +00:00
luigi1111
b15dbbb9b0 Merge pull request #2921
55cbc56 workflows: fix windows build (selsta)
2020-05-26 15:40:24 -05:00
luigi1111
a73a4363ec Update submodule to recent master (77a008f) 2020-05-26 13:10:17 -05:00
selsta
55cbc562b6 workflows: fix windows build 2020-05-24 23:34:31 +02:00
luigi1111
e6c4c32d01 Merge pull request #2919
c946905 OpenGL: Windows - fall back to software renderer if OpenGL < 2.1 (xiphon)
2020-05-21 12:42:00 -05:00
luigi1111
e36d4a918f Merge pull request #2917
43f0854 Translations from Weblate (Monero-Weblate)
2020-05-21 12:40:51 -05:00
luigi1111
0355ca2747 Merge pull request #2916
f38e460 build: set submodule to v0.16.0.0 (selsta)
2020-05-21 12:39:34 -05:00
xiphon
c946905907 OpenGL: Windows - fall back to software renderer if OpenGL < 2.1 2020-05-20 13:45:16 +00:00
luigi1111
a8bd2ab77e Merge pull request #2913
0de1ba9 Windows installer: Update for Nitrogen Nebula 0.16.0.0 (rbrunner7)
2020-05-18 12:30:09 -05:00
luigi1111
149e373367 Merge pull request #2912
1503885 cmake: always link libgcc, libstdc++ statically (xiphon)
2020-05-18 12:29:15 -05:00
luigi1111
fa3c8b5f89 Merge pull request #2911
754a968 build: release-win64 support (xiphon)
2020-05-18 12:28:20 -05:00
luigi1111
e0f6577afd Merge pull request #2910
efc9ad4 cmake: fix Qt 5.14+ build, link Qt5QmlModels if available (xiphon)
2020-05-18 12:26:49 -05:00
Anonymous
43f0854de9 Translations from Weblate
Credits:

* Ukrainian

    * reketen
    * TheFuzzStone

* Spanish

    * Michael Russo
    * kombometralla
    * CharlesCrisco
    * Anonymous
    * porokon7
    * Juanpaab

* Korean

    * enaSo97

* Croatian

    * TasmaniaKrama

* Finnish

    * vaa red

* Italian

    * 8uDD4T
    * Anonymous

* Chinese (Traditional)

    * Lafudoci

* Esperanto

    * Gilberto F da Silva

* Romanian

    * Marius Tanislav
    * Anonymous
    * Vlad G.

* Russian

    * Agent LvM
    * solevoy-psih
    * v1docq47
    * TheFuzzStone
    * Russian Bear
    * translator133

* Japanese

    * Scott Anecito

* Portuguese (Portugal)

    * Anonymous

* French

    * Anonymous
    * Viktor

* Czech

    * Anonymous

* Chinese (Simplified)

    * jindouyunz
    * anony_xmr
    * tan
    * Muge Niu
    * Anonymous
    * TE Scott
    * razorshaman909

* Bulgarian

    * Weblate Admin

* Arabic

    * Weblate Admin

* Slovenian

    * Matija Mazi

* Polish

    * To Ja
    * Anonymous

* Portuguese (Brazil)

    * ANMNQ
    * Nelson Renan
    * netrik182
    * Anonymous
    * Boçogrolho Tabúrcio Mendez
    * Asdrubal Petronidas Calhofos

* Norwegian Bokmål

    * bitbooz
    * Anonymous

* Dutch

    * Marcus

* Swedish

    * B
    * peter hermansson

* Danish

    * KforG
    * Poul
    * Anonymous

* German

    * Wobole
    * Anonymous
    * René Brunner
    * GreenPiece
2020-05-18 09:42:46 +00:00
selsta
f38e460842 build: set submodule to v0.16.0.0 2020-05-18 01:40:53 +02:00
selsta
527adcba17 Utils: simplify for translations 2020-05-16 18:11:39 +02:00
René Brunner
0de1ba9f51 Windows installer: Update for Nitrogen Nebula 0.16.0.0 2020-05-15 15:09:40 +02:00
selsta
66e769603c QML: disable networking 2020-05-15 06:21:08 +02:00
xiphon
15038850c2 cmake: always link libgcc, libstdc++ statically 2020-05-15 02:28:47 +00:00
xiphon
efc9ad45e4 cmake: fix Qt 5.14+ build, link Qt5QmlModels if available 2020-05-15 02:27:09 +00:00
xiphon
754a968706 build: release-win64 support 2020-05-15 02:24:14 +00:00
luigi1111
c20a0ef928 Merge pull request #2908
210248e cmake: disable '-pie' on Win with gcc (xiphon)
2020-05-14 16:06:39 -05:00
luigi1111
eab98e3a48 Merge pull request #2906
da4e0db cmake: rename monero-gui binary to monero-wallet-gui (xiphon)
2020-05-14 16:05:53 -05:00
luigi1111
2a8960bc2c Merge pull request #2905
f82948f cmake: drop '-std=c++0x' compiler flag (xiphon)
2020-05-14 16:05:04 -05:00
luigi1111
fe68f59763 Merge pull request #2903
9dd3f4f add isTrezor() function (rating89us)
2020-05-14 16:04:17 -05:00
luigi1111
80e8dd6aef Merge pull request #2901
72a3b34 Merchant: allow to exit when height < minHeight (selsta)
2020-05-14 16:03:22 -05:00
luigi1111
c82bd94bc3 Merge pull request #2899
abbe042 Filter out multiple blanks in seed input when counting words (rbrunner7)
2020-05-14 16:02:27 -05:00
luigi1111
28ca9503df Merge pull request #2897
d662e46 Merchant: display size warning when height < minHeight (rating89us)
2020-05-14 16:01:28 -05:00
luigi1111
73cc400ae8 Merge pull request #2896
bbc9ca8 Transfer: fix html paragraph tag (rating89us)
2020-05-14 16:00:43 -05:00
luigi1111
8b7438ace2 Merge pull request #2894
f7271d1 SettingsNode: add translation to Address and Port strings (rating89us)
2020-05-14 15:59:40 -05:00
xiphon
210248e6ef cmake: disable '-pie' on Win with gcc 2020-05-14 18:48:08 +00:00
xiphon
f82948f4b0 cmake: drop '-std=c++0x' compiler flag 2020-05-13 14:29:01 +00:00
xiphon
da4e0dbf0f cmake: rename monero-gui binary to monero-wallet-gui 2020-05-13 14:26:14 +00:00
rating89us
9dd3f4fecb add isTrezor() function 2020-05-13 13:22:49 +02:00
rbrunner7
abbe042c8a Filter out multiple blanks in seed input when counting words 2020-05-13 07:51:31 +02:00
selsta
72a3b346bb Merchant: allow to exit when height < minHeight 2020-05-12 20:43:46 +02:00
rating89us
d662e46146 Merchant: display size warning when height < minHeight 2020-05-11 00:42:45 +02:00
rating89us
bbc9ca8f08 Transfer: fix html paragraph tag 2020-05-10 23:48:48 +02:00
rating89us
f7271d1c7b SettingsNode: add translation to Address and Port strings 2020-05-09 19:31:36 +02:00
luigi1111
afe1ae9b9c Merge pull request #2892
b616a14 LanguageSidebar: fix white theme (selsta)
2020-05-08 12:27:37 -05:00
luigi1111
7f5b8ea0ad Merge pull request #2890
2f8c0ca Main: clear spendable funds message when closing wallet (rating89us)
2020-05-08 12:26:24 -05:00
luigi1111
aa5000f556 Merge pull request #2876
4354a76 StandardDropdown: drop Qt 5.8 workaround (xiphon)
2020-05-08 12:25:15 -05:00
luigi1111
8b6978b2a5 Merge pull request #2829
86d21a3 trezor: support new passphrase entry mechanism (ph4r05)
2020-05-08 12:21:59 -05:00
Dusan Klinec
86d21a34ba trezor: support new passphrase entry mechanism
- passphrase can be prompted also when wallet is running (thus added to the wallet listener)
- device/host decision is now made on the host
2020-05-08 18:40:17 +02:00
xiphon
4354a76df9 StandardDropdown: drop Qt 5.8 workaround 2020-05-07 21:55:44 +00:00
selsta
b616a14f88 LanguageSidebar: fix white theme 2020-05-07 21:04:03 +02:00
luigi1111
7536e922e9 Merge pull request #2885
8f70fb4 Transfer: improve warning message while connecting (selsta)
2020-05-07 10:49:44 -05:00
luigi1111
5bf0dd9684 Merge pull request #2884
c439d68 main: fix local node status check on closing uninitialized wallet (xiphon)
2020-05-07 10:31:35 -05:00
luigi1111
cdf4ce2d6f Merge pull request #2891
37a8bcb SimpleMode: use connection timer 'running' prop, fix daemon restart (xiphon)
2020-05-07 10:30:13 -05:00
xiphon
37a8bcb525 SimpleMode: use connection timer 'running' prop, fix daemon restart 2020-05-07 13:40:43 +00:00
rating89us
2f8c0ca499 Main: clear spendable funds message when closing wallet 2020-05-06 19:18:55 +02:00
luigi1111
38612c1285 Merge pull request #2881
487e706 Improved daemon sync progress bar (rbrunner7)
2020-05-06 00:41:41 -05:00
luigi1111
1d3a201077 Merge pull request #2879
ca79525 Slider component (xiphon)
503c1af SettingsLayout: implement autosave, enabled by default (10 minutes) (xiphon)
2020-05-06 00:40:36 -05:00
luigi1111
a91a4f51ab Merge pull request #2871
7db5de0 WizardAskPassword: drop custom password fields, use LineEdit (xiphon)
2020-05-06 00:39:26 -05:00
luigi1111
52fbbae484 Merge pull request #2883
f26f146 PasswordDialog: fix focus on open (selsta)
2020-05-06 00:38:20 -05:00
selsta
8f70fb4f79 Transfer: improve warning message while connecting 2020-05-05 13:12:10 +02:00
xiphon
c439d6814b main: fix local node status check on closing uninitialized wallet 2020-05-04 14:48:56 +00:00
selsta
f26f1469ca PasswordDialog: fix focus on open 2020-05-03 15:51:46 +02:00
rbrunner7
487e706f06 Improved daemon sync progress bar 2020-05-02 16:34:54 +02:00
xiphon
503c1af5f8 SettingsLayout: implement autosave, enabled by default (10 minutes) 2020-05-02 01:54:11 +00:00
xiphon
ca79525fdb Slider component 2020-05-02 01:54:05 +00:00
xiphon
7db5de082d WizardAskPassword: drop custom password fields, use LineEdit 2020-05-02 01:32:35 +00:00
luigi1111
ce6cc47afe Merge pull request #2845
7f9b28c WizardNav: use PageIndicator in wizardProgress (rating89us)
2020-05-01 15:51:48 -05:00
luigi1111
a10a94f51c Merge pull request #2866
5265e52 ProcessingSplash: new design (selsta)
2020-05-01 15:11:43 -05:00
luigi1111
1833c16e39 Merge pull request #2785
be954cc StandardDropdown: fix margins, text overlapping (xiphon)
2020-05-01 15:10:49 -05:00
luigi1111
d7207bfde3 Merge pull request #2874
387e643 Transfer: fix 'call method estimateTransactionFeeAsync of undefined' (xiphon)
2020-05-01 15:09:49 -05:00
selsta
5265e52b8b ProcessingSplash: new design 2020-05-01 22:09:27 +02:00
luigi1111
b6fdb709ba Merge pull request #2873
4a7ccd8 SettingsLayout: add check for updates checkbox (selsta)
2020-05-01 15:07:50 -05:00
luigi1111
9aef3bab33 Merge pull request #2870
2195c67 LineEdit: password mode, linking. PasswordDialog: use LineEdit (xiphon)
2020-05-01 15:06:46 -05:00
luigi1111
4b55197e18 Merge pull request #2848
c7f9ac9 LanguageSidebar: reenable (selsta)
3835387 wizard: add flags to language sidebar (mmbyday, selsta)
2020-05-01 15:04:55 -05:00
luigi1111
11617d2f76 Merge pull request #2846
2102e4b Merchant: fix confirmations (selsta)
b245d0a MerchantTrackingList: point out unconfirmed amount (selsta)
07ecca5 Merchant: fix payment URL (selsta)
3af99e9 Merchant: remove unused variables (selsta)
2020-05-01 15:03:04 -05:00
luigi1111
765e93cfd0 Merge pull request #2840
d31e661 Transfer: redesign advanced options (rating89us)
2020-05-01 14:58:56 -05:00
rating89us
d31e661cd1 Transfer: redesign advanced options 2020-05-01 11:48:53 +02:00
rating89us
7f9b28c05f WizardNav: use PageIndicator in wizardProgress 2020-05-01 11:40:47 +02:00
selsta
4a7ccd8d82 SettingsLayout: add check for updates checkbox 2020-05-01 03:46:58 +02:00
xiphon
be954cc2c4 StandardDropdown: fix margins, text overlapping 2020-05-01 01:30:40 +00:00
xiphon
387e643ae9 Transfer: fix 'call method estimateTransactionFeeAsync of undefined' 2020-04-29 22:33:08 +00:00
luigi1111
6f71d47806 Merge pull request #2868
5c13624 DaemonManager: drop unused 'initialized', 'm_has_daemon' members (xiphon)
2020-04-28 21:41:19 -05:00
luigi1111
135c970ad9 Merge pull request #2865
cfdba59 Wallet: implement async wallet storing (xiphon)
2020-04-28 21:40:17 -05:00
luigi1111
fd5d1f584e Merge pull request #2861
4b0dcb9 StandardDialog: reset button text on close (selsta)
2020-04-28 21:38:59 -05:00
xiphon
2195c67f58 LineEdit: password mode, linking. PasswordDialog: use LineEdit 2020-04-28 20:44:53 +00:00
xiphon
5c13624596 DaemonManager: drop unused 'initialized', 'm_has_daemon' members 2020-04-28 20:42:23 +00:00
xiphon
cfdba59584 Wallet: implement async wallet storing 2020-04-28 20:36:22 +00:00
luigi1111
4141832a4d Merge pull request #2869
3d24300 SettingsNode: forbid 'data-dir', 'bootstrap-daemon-address' flags (xiphon)
2020-04-28 15:24:11 -05:00
luigi1111
5f183da6e3 Merge pull request #2867
9748974 DaemonManager: fix memory leak (xiphon)
2020-04-28 15:23:03 -05:00
luigi1111
30c54b1c6e Merge pull request #2864
ce4cb65 WizardAskPassword: hide strength if getPasswordStrength is missing (xiphon)
2020-04-28 15:22:02 -05:00
luigi1111
c1da3d1c97 Merge pull request #2863
120b528 WizardDaemonSettings: bootstrap node address 'auto' special case (xiphon)
2020-04-28 15:21:19 -05:00
luigi1111
cbd03229dd Merge pull request #2862
33c1a6f repo: cleanup unused images (selsta)
2020-04-28 15:20:22 -05:00
luigi1111
2fc1b287dd Merge pull request #2860
fdb7f80 SettingsInfo: fix number string formatting (selsta)
2020-04-28 15:19:36 -05:00
luigi1111
7c8c6a116f Merge pull request #2856
a810bf3 CheckBox: add enabled property (selsta)
2020-04-28 15:18:51 -05:00
luigi1111
ef93139a80 Merge pull request #2855
b022735 main: add --disable-check-updates flag (selsta)
2020-04-28 15:17:57 -05:00
luigi1111
1b7844ec34 Merge pull request #2854
a99eef6 always use native directory separators in paths (xiphon)
2020-04-28 15:16:57 -05:00
luigi1111
8f63e8870f Merge pull request #2851
7ebdb88 README: list libgcrypt linux (selsta)
2020-04-28 15:16:10 -05:00
luigi1111
286c75aa5b Merge pull request #2850
9b98e0a checkUpates: installer support (Win), use GUI buildTag and version (xiphon)
2020-04-28 15:14:40 -05:00
luigi1111
0a4d65dd99 Merge pull request #2849
02eec35 src: fix qt 5.15 warnings (selsta)
2020-04-28 15:13:45 -05:00
luigi1111
83ccadb6a8 Merge pull request #2847
238b1b7 pages: remove legacy code (selsta)
2020-04-28 15:12:57 -05:00
luigi1111
eae7eff9db Merge pull request #2844
81d5dd1 Transfer: add offline transaction signing info in send button warning (rating89us)
2020-04-28 15:11:58 -05:00
selsta
2102e4be0d Merchant: fix confirmations 2020-04-28 19:30:22 +02:00
selsta
02eec351b9 src: fix qt 5.15 warnings 2020-04-28 19:00:53 +02:00
xiphon
ce4cb6512d WizardAskPassword: hide strength if getPasswordStrength is missing 2020-04-28 15:05:43 +00:00
xiphon
3d24300963 SettingsNode: forbid 'data-dir', 'bootstrap-daemon-address' flags 2020-04-27 13:18:23 +00:00
xiphon
9748974ce0 DaemonManager: fix memory leak 2020-04-27 12:46:15 +00:00
xiphon
120b5285fb WizardDaemonSettings: bootstrap node address "auto" special case 2020-04-26 01:14:29 +00:00
selsta
b022735506 main: add --disable-check-updates flag 2020-04-26 02:57:35 +02:00
selsta
33c1a6f4fc repo: cleanup unused images 2020-04-26 02:18:35 +02:00
selsta
4b0dcb95bf StandardDialog: reset button text on close 2020-04-25 21:10:25 +02:00
selsta
fdb7f806fa SettingsInfo: fix number string formatting 2020-04-25 20:55:32 +02:00
selsta
a810bf3eb7 CheckBox: add enabled property 2020-04-24 04:35:21 +02:00
xiphon
a99eef68f5 always use native directory separators in paths 2020-04-24 00:43:40 +00:00
selsta
7ebdb884a1 README: list libgcrypt linux 2020-04-23 23:31:43 +02:00
selsta
c7f9ac926b LanguageSidebar: reenable 2020-04-23 20:28:09 +02:00
xiphon
9b98e0a2f5 checkUpates: installer support (Win), use GUI buildTag and version 2020-04-23 16:08:10 +00:00
mmbyday
3835387eea wizard: add flags to language sidebar 2020-04-23 00:04:07 +02:00
selsta
238b1b777f pages: remove legacy code 2020-04-22 23:18:08 +02:00
selsta
3af99e91e4 Merchant: remove unused variables 2020-04-22 22:10:40 +02:00
selsta
07ecca5af4 Merchant: fix payment URL
This would previously get loaded on the Receive page.
2020-04-22 22:10:40 +02:00
selsta
b245d0af7a MerchantTrackingList: point out unconfirmed amount 2020-04-22 22:10:40 +02:00
luigi1111
585fb2810d Merge pull request #2843
5076757 SettingsLog: don't assign color to font family (selsta)
2020-04-22 13:37:34 -05:00
luigi1111
0784532001 Merge pull request #2842
1d5b940 Merchant: fix close button (selsta)
2020-04-22 13:36:37 -05:00
luigi1111
dcbdae0954 Merge pull request #2841
3563d44 Transfer: move address field to the top (rating89us)
2020-04-22 13:35:49 -05:00
luigi1111
2bef74fe8a Merge pull request #2838
ffceda9 SettingsLayout: add checkbox 'ask for password before sending a transaction' (rating89us)
2020-04-22 13:34:47 -05:00
rating89us
81d5dd1cae Transfer: add offline transaction signing info in send button warning 2020-04-21 20:57:55 +02:00
selsta
50767570f0 SettingsLog: don't assign color to font family 2020-04-21 20:02:23 +02:00
selsta
1d5b940349 Merchant: fix close button 2020-04-21 19:42:05 +02:00
rating89us
3563d44d99 Transfer: move address field to the top 2020-04-18 19:25:45 +02:00
rating89us
ffceda9159 SettingsLayout: add checkbox 'ask for password before sending a transaction' 2020-04-18 17:42:27 +02:00
luigi1111
a75a0fb8c5 Merge pull request #2839
fee81ba workflows: add trezor support ubuntu (selsta)
2020-04-17 22:10:21 -05:00
luigi1111
5fa64c34ec Merge pull request #2831
de6a9b6 Transfer: address book inline button, 2nd inline button, scan QR code button fixes (rating89us)
2020-04-17 22:09:18 -05:00
luigi1111
376421667a Merge pull request #2836
3f13a5c QRCodeScanner: don't load webcam on startup (mmbyday/selsta)
2020-04-17 20:53:46 -05:00
luigi1111
8a73fd241e Merge pull request #2835
a078705 QrScanThread: use sizeInBytes to fix warning (selsta)
2020-04-17 20:52:33 -05:00
luigi1111
9760886eff Merge pull request #2834
fb7470a build: find zbar header on macOS (selsta)
2020-04-17 20:49:18 -05:00
luigi1111
34089599cd Merge pull request #2833
e2c6ae6 AddressBook: remove leftover code (selsta)
2020-04-17 20:46:49 -05:00
luigi1111
4a7a98034e Merge pull request #2832
ea25b71 updater: fetch signed hashes from getmonero.org, verify downloads (xiphon)
2020-04-17 20:45:24 -05:00
luigi1111
79f78f48e2 Merge pull request #2830
5ebe3f5 Settings: move Merchant menu entry (tobtoht/selsta)
2020-04-17 20:43:40 -05:00
selsta
fee81ba210 workflows: add trezor support ubuntu 2020-04-16 22:53:33 +02:00
selsta
a078705ec6 QrScanThread: use sizeInBytes to fix warning 2020-04-16 19:11:29 +02:00
rating89us
de6a9b6779 Transfer: address book inline button, 2nd inline button, scan QR code button fixes
spacing fix
2020-04-16 13:25:28 +02:00
mmbyday
3f13a5c9a4 QRCodeScanner: don't load webcam on startup 2020-04-16 03:29:39 +02:00
selsta
fb7470a2a6 build: find zbar header on macOS 2020-04-16 03:13:33 +02:00
selsta
e2c6ae6472 AddressBook: remove leftover code 2020-04-16 02:08:32 +02:00
xiphon
ea25b71ca6 updater: fetch signed hashes from getmonero.org, verify downloads 2020-04-14 21:37:04 +00:00
thotbot
5ebe3f5092 Settings: move Merchant menu entry 2020-04-13 23:15:14 +02:00
luigi1111
8e4124f06a Merge pull request #2828
e1258e0 TitleBar: fix close icon fallback (selsta)
2020-04-13 15:49:17 -05:00
luigi1111
e47fd5f760 Merge pull request #2827
925cced README: add weblate's widget for translations status (erciccione)
2020-04-13 15:48:14 -05:00
luigi1111
8767c71107 Merge pull request #2825
94083e7 cmake: use monero core compilation and linking flags (xiphon)
2020-04-13 15:46:38 -05:00
luigi1111
2156a6533b Merge pull request #2824
5f27a45 '--verify-update', shasum support, OpenPGP signatures verification (xiphon)
2020-04-13 15:43:24 -05:00
luigi1111
7eac690d44 Merge pull request #2822
a49d579 build: fix MSYS2 folder detection (xiphon)
2020-04-13 15:40:42 -05:00
luigi1111
31adc6cfd6 Merge pull request #2821
6d7db13 Settings: rework wallet page (thotbot/xiphon)
2020-04-13 15:39:54 -05:00
luigi1111
d582fd338d Merge pull request #2819
6ed7fce UpdateDialog: implement update download functionality (xiphon)
2020-04-13 15:38:06 -05:00
luigi1111
eed51e3ffa Merge pull request #2772
48aab5c WalletManager: wallet recovery - seed offset passphrase support (xiphon)
2020-04-13 15:36:44 -05:00
luigi1111
6a889bdaa1 Merge pull request #2739
b5fafb5 Transfer: display estimated transaction fee, requires #6302 (xiphon)
2020-04-13 15:35:44 -05:00
xiphon
5f27a45910 '--verify-update', shasum support, OpenPGP signatures verification 2020-04-12 21:34:22 +00:00
selsta
e1258e0ada TitleBar: fix close icon fallback 2020-04-10 13:55:25 +02:00
erciccione
925cced7d7 README: add weblate's widget for translations status 2020-04-09 12:25:54 +02:00
xiphon
94083e746f cmake: use monero core compilation and linking flags 2020-04-08 02:31:16 +00:00
xiphon
a49d579bd3 build: fix MSYS2 folder detection 2020-04-06 17:22:34 +00:00
xiphon
6ed7fcec67 UpdateDialog: implement update download functionality 2020-04-06 16:59:33 +00:00
thotbot
6d7db135e7 Settings: rework wallet page 2020-04-05 13:07:11 +00:00
luigi1111
042400b83f Set submodule back to master 2020-04-04 14:30:32 -05:00
luigi1111
df54439972 Merge pull request #2818
a9fea24 workflows: retry on apt failure / increase timeout (selsta)
2020-04-04 13:22:37 -05:00
luigi1111
86252506f0 Merge pull request #2817
58987b2 network: provide common HTTP GET functionality with js callbacks (xiphon)
2020-04-04 13:21:23 -05:00
selsta
a9fea2462b workflows: retry on apt failure / increase timeout
Co-authored-by: xiphon <xiphon@protonmail.com>
2020-04-01 00:50:24 +02:00
xiphon
58987b2ec6 network: provide common HTTP GET functionality with js callbacks 2020-03-31 21:37:19 +00:00
luigi1111
aecd218d15 Merge pull request #2807
d07da76 MenuButton: add hover effect (xiphon)
2020-03-27 10:55:07 -05:00
luigi1111
856843b52a Merge pull request #2803
5872bc8 build: disable _FORTIFY_SOURCE when building without optimization (xiphon)
2020-03-27 10:53:46 -05:00
luigi1111
fc740a89ab Merge pull request #2801
dede10e Add QT Creator cache file to gitignore (tobtoht)
2020-03-27 10:19:31 -05:00
luigi1111
0643607ec3 Merge pull request #2792
d21c22b SettingsNode: blockchain location 'Reset' button (xiphon)
2020-03-27 10:16:58 -05:00
xiphon
5872bc8a2b build: disable _FORTIFY_SOURCE when building without optimization 2020-03-23 06:34:18 +00:00
xiphon
d07da76383 MenuButton: add hover effect 2020-03-14 01:19:08 +00:00
luigi1111
c092de97f2 Merge pull request #2805
c9e461d build: set submodule to v0.15.0.5 (selsta)
2020-03-10 18:58:00 -04:00
luigi1111
c84a8fb07d Merge pull request #2799
55b548f qt: drop Qt Network and Qt OpenSSL deps, use epee library instead (xiphon)
2020-03-10 18:56:04 -04:00
luigi1111
63b4566475 Merge pull request #2796
c7df74d README: add protobuf to linux instructions (selsta)
2020-03-10 18:50:54 -04:00
luigi1111
3699dc9f2e Merge pull request #2795
94a27b9 README: cleanup macOS instructions (selsta)
2020-03-10 18:49:22 -04:00
luigi1111
eee48ce74c Merge pull request #2797
b739cdd cmake: -static, -static-libgcc, -static-libstdc++ linker flags (xiphon)
2020-03-10 18:48:01 -04:00
luigi1111
9072f89a4e Merge pull request #2794
bf9b04b installers: fix warning (selsta)
2020-03-10 18:46:45 -04:00
selsta
c9e461dcb6 build: set submodule to v0.15.0.5 2020-03-09 20:50:16 +01:00
xiphon
55b548f31c qt: drop Qt Network and Qt OpenSSL deps, use epee library instead 2020-03-09 11:05:49 +00:00
thotbot
dede10ea1a Add QT Creator cache file to gitignore 2020-03-08 15:58:29 +00:00
xiphon
b739cdd52a cmake: -static, -static-libgcc, -static-libstdc++ linker flags 2020-03-06 11:54:16 +00:00
selsta
94a27b964a README: cleanup macOS instructions 2020-03-06 02:06:08 +01:00
selsta
c7df74dab7 README: add protobuf to linux instructions 2020-03-06 01:52:00 +01:00
selsta
bf9b04b126 installers: fix warning 2020-03-04 20:02:08 +01:00
xiphon
d21c22b444 SettingsNode: blockchain location 'Reset' button 2020-03-02 10:35:50 +00:00
xiphon
b5fafb55c9 Transfer: display estimated transaction fee, requires #6302 2020-02-16 11:55:36 +00:00
xiphon
48aab5c6e5 WalletManager: wallet recovery - seed offset passphrase support 2020-02-12 13:49:26 +00:00
181 changed files with 39073 additions and 26810 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
*

View File

@@ -6,47 +6,89 @@ jobs:
build-macos:
runs-on: macOS-latest
strategy:
fail-fast: false
matrix:
toolchain:
- name: "qmake"
cmd: "export PATH=$PATH:/usr/local/opt/qt/bin && ./build.sh"
- name: "cmake"
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release -j3"
name: build-macos-${{ matrix.toolchain.name }}
steps:
- uses: actions/checkout@v1
with:
submodules: recursive
- name: update brew and install dependencies
run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5
run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5 pkg-config
- name: build
run: export PATH=$PATH:/usr/local/opt/qt/bin && ./build.sh
run: ${{ matrix.toolchain.cmd }}
- name: test qml
run: build/release/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml
build-ubuntu:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain:
- name: "qmake"
cmd: "./build.sh"
- name: "cmake"
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release -j3"
name: build-ubuntu-${{ matrix.toolchain.name }}
steps:
- uses: actions/checkout@v1
with:
submodules: recursive
- name: remove bundled boost
run: sudo rm -rf /usr/local/share/boost
- name: set apt conf
run: |
echo "Acquire::Retries \"3\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
echo "Acquire::http::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
echo "Acquire::ftp::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
- name: update apt
run: sudo apt update
- name: install monero dependencies
run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev
run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler
- name: install monero gui dependencies
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev xvfb
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev libgcrypt20-dev xvfb
- name: build
run: ./build.sh
run: ${{ matrix.toolchain.cmd }}
- name: test qml
run: xvfb-run -a build/release/bin/monero-wallet-gui --test-qml
build-windows:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
toolchain:
- name: "qmake"
cmd: "./build.sh release"
- name: "cmake"
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release-win64 -j2"
name: build-windows-${{ matrix.toolchain.name }}
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v1
- uses: numworks/setup-msys2@v1
- name: update pacman
run: msys2do pacman -Syu --noconfirm
- name: install monero dependencies
run: msys2do pacman -S --noconfirm mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5
with:
submodules: recursive
- uses: eine/setup-msys2@v1
with:
update: true
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5 mingw-w64-x86_64-libgcrypt
- name: build
run: msys2do ./build.sh release
run: |
sed -i 's/CONFIG\ +=\ qtquickcompiler//' monero-wallet-gui.pro
${{ matrix.toolchain.cmd }}
- name: test qml
run: msys2do build/release/bin/monero-wallet-gui --test-qml
run: build/release/bin/monero-wallet-gui --test-qml

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ monero-wallet-gui_plugin_import.cpp
monero-wallet-gui_qml_plugin_import.cpp
*.qmlc
*.jsc
qml_qmlcache.qrc
### Vim ###
# Swap

View File

@@ -3,20 +3,24 @@ project(monero-gui)
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
set(VERSION_MAJOR "14")
set(VERSION_MAJOR "16")
set(VERSION_MINOR "0")
set(VERSION_REVISION "3")
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
set(VERSION "0.${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
# libwallet requires a static build, so we only allow static compilation
set(STATIC ON)
option(STATIC "Link libraries statically, requires static Qt")
option(USE_DEVICE_TREZOR ON)
option(USE_DEVICE_TREZOR "Trezor support compilation" ON)
option(ENABLE_PASS_STRENGTH_METER "Disable zxcvbn" OFF)
option(WITH_SCANNER "Enable webcam QR scanner" OFF)
option(DEV_MODE "Checkout latest monero master on build" OFF)
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(CheckLinkerFlag)
include(FindCcache)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
@@ -27,7 +31,6 @@ endif()
set(BUILD_GUI_DEPS ON)
set(ARCH "x86-64")
set(BUILD_64 ON)
set(INSTALL_VENDORED_LIBUNBOUND=ON)
function (add_c_flag_if_supported flag var)
string(REPLACE "-" "_" supported ${flag}_c)
@@ -78,6 +81,7 @@ endif()
add_subdirectory(monero)
set_property(TARGET wallet_merged PROPERTY FOLDER "monero")
get_directory_property(ARCH_WIDTH DIRECTORY "monero" DEFINITION ARCH_WIDTH)
get_directory_property(UNBOUND_LIBRARY DIRECTORY "monero" DEFINITION UNBOUND_LIBRARY)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DQT_NO_DEBUG)
@@ -135,6 +139,9 @@ monero_gui_add_library(gui_version SOURCES version.js DEPENDS genversiongui)
message(STATUS "${CMAKE_MODULE_PATH}")
# OpenSSL
if(APPLE AND NOT OPENSSL_ROOT_DIR)
execute_process(COMMAND brew --prefix openssl OUTPUT_VARIABLE OPENSSL_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
find_package(OpenSSL REQUIRED)
message(STATUS "OpenSSL: Version ${OPENSSL_VERSION}")
message(STATUS "OpenSSL: include dir at ${OPENSSL_INCLUDE_DIR}")
@@ -166,7 +173,10 @@ message(STATUS "libhidapi: libraries at ${HIDAPI_LIBRARIES}")
if(DEBUG)
set(Boost_DEBUG ON)
endif()
find_package(Boost 1.62 REQUIRED COMPONENTS
if(APPLE AND NOT BOOST_ROOT)
execute_process(COMMAND brew --prefix boost OUTPUT_VARIABLE BOOST_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
find_package(Boost 1.58 REQUIRED COMPONENTS
system
filesystem
thread
@@ -194,21 +204,6 @@ if(MINGW)
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
message(STATUS "MSYS location: ${msys2_install_path}")
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/Qt/labs/folderlistmodel")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/Qt/labs/settings")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtGraphicalEffects")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtGraphicalEffects/private")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtMultimedia")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick.2")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Controls")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Controls.2")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Dialogs")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Dialogs/Private")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Layouts")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/PrivateWidgets")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Templates.2")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Window.2")
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/XmlListModel")
# This is necessary because otherwise CMake will make Boost libraries -lfoo
# rather than a full path. Unfortunately, this makes the shared libraries get
# linked due to a bug in CMake which misses putting -static flags around the
@@ -221,35 +216,66 @@ endif()
set(QT5_LIBRARIES
Qt5Core
Qt5Quick
Qt5QuickControls2
Qt5Widgets
Qt5Gui
Qt5Network
Qt5Qml
Qt5Multimedia
Qt5Xml
Qt5XmlPatterns
Qt5Svg
Qt5Xml
)
if(WITH_SCANNER)
list(APPEND QT5_LIBRARIES Qt5Multimedia)
endif()
if(APPLE)
list(APPEND QT5_LIBRARIES Qt5MacExtras)
if(NOT CMAKE_PREFIX_PATH)
execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE QT5_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
list(APPEND CMAKE_PREFIX_PATH ${QT5_DIR})
endif()
if(CMAKE_PREFIX_PATH)
include_directories(${CMAKE_PREFIX_PATH}/include)
endif()
endif()
find_package(PkgConfig REQUIRED)
# TODO: drop this once we switch to Qt 5.14+
pkg_check_modules(Qt5QmlModels_PKG_CONFIG QUIET Qt5QmlModels)
if(Qt5QmlModels_PKG_CONFIG_FOUND)
list(APPEND QT5_LIBRARIES Qt5QmlModels)
endif()
# TODO: drop this once we switch to Qt 5.12+
find_package(Qt5XmlPatterns QUIET)
if(Qt5XmlPatterns_FOUND)
list(APPEND QT5_LIBRARIES Qt5XmlPatterns)
endif()
foreach(QT5_MODULE ${QT5_LIBRARIES})
find_package(${QT5_MODULE} REQUIRED)
include_directories(${${QT5_MODULE}_INCLUDE_DIRS})
endforeach()
find_package(PkgConfig)
if(PKGCONFIG_FOUND)
pkg_check_modules(QT5_PKG_CONFIG ${QT5_LIBRARIES})
pkg_check_modules(QT5_PKG_CONFIG REQUIRED ${QT5_LIBRARIES})
if(QT5_PKG_CONFIG_FOUND)
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
if(STATIC)
set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC")
endif()
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS})
link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS})
if(QT5_PKG_CONFIG_FOUND)
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
if(STATIC)
set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC")
endif()
if(APPLE)
list(JOIN ${QT5_PKG_CONFIG}_LDFLAGS_OTHER " " ${QT5_PKG_CONFIG}_LDFLAGS_OTHER)
list(JOIN ${QT5_PKG_CONFIG}_LIBRARIES " " ${QT5_PKG_CONFIG}_LIBRARIES)
endif()
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS})
link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS})
endif()
list(APPEND QT5_LIBRARIES
@@ -260,37 +286,95 @@ list(APPEND QT5_LIBRARIES
)
if(STATIC)
set(QT5_LIBRARIES
qtquickcontrols2plugin # has to be the first one, depends on Qt5QuickControls2
${QT5_LIBRARIES}
declarative_multimedia
set(QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/folderlistmodel)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/settings)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects/private)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtMultimedia)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick.2)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls.2)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs/Private)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Layouts)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/PrivateWidgets)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Templates.2)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Window.2)
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/XmlListModel)
set(QT5_EXTRA_LIBRARIES_LIST
qtquicktemplates2plugin
Qt5QuickTemplates2
qtquickcontrols2plugin
Qt5QuickControls2
dialogplugin
dialogsprivateplugin
qmlfolderlistmodelplugin
qmlsettingsplugin
qmlxmllistmodelplugin
qquicklayoutsplugin
Qt5EventDispatcherSupport
Qt5FontDatabaseSupport
Qt5MultimediaQuick_p
Qt5PacketProtocol
Qt5ThemeSupport
)
if(WITH_SCANNER)
list(APPEND QT5_EXTRA_LIBRARIES_LIST
declarative_multimedia
Qt5MultimediaQuick_p
)
endif()
list(APPEND QT5_EXTRA_LIBRARIES_LIST
qtgraphicaleffectsplugin
qtgraphicaleffectsprivate
qtquick2plugin
qtquickcontrolsplugin
qtquicktemplates2plugin
widgetsplugin
windowplugin
)
set(QT5_EXTRA_LIBRARIES)
foreach(LIBRARY ${QT5_EXTRA_LIBRARIES_LIST})
find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS} REQUIRED)
list(APPEND QT5_EXTRA_LIBRARIES ${${LIBRARY}_LIBRARY})
endforeach()
if(MINGW)
list(APPEND QT5_LIBRARIES qtfreetype)
list(APPEND QT5_EXTRA_LIBRARIES qtfreetype)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND QT5_LIBRARIES D3D11 Dwrite D2d1)
list(APPEND QT5_EXTRA_LIBRARIES D3D11 Dwrite D2d1)
endif()
endif()
set(QT5_LIBRARIES
${QT5_EXTRA_LIBRARIES}
${QT5_LIBRARIES}
)
set(QT5_INTEGRATION_LIBRARIES_LIST
Qt5EventDispatcherSupport
Qt5PacketProtocol
Qt5ThemeSupport
Qt5FontDatabaseSupport
)
if(UNIX AND NOT APPLE)
list(APPEND QT5_INTEGRATION_LIBRARIES_LIST
Qt5XcbQpa
xcb-static
Qt5ServiceSupport
Qt5GlxSupport
)
endif()
foreach(LIBRARY ${QT5_INTEGRATION_LIBRARIES_LIST})
find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS} REQUIRED)
list(APPEND QT5_LIBRARIES ${${LIBRARY}_LIBRARY})
endforeach()
if(UNIX AND NOT APPLE)
pkg_check_modules(X11XCB_XCBGLX_FONTCONFIG REQUIRED x11-xcb xcb-glx fontconfig)
list(APPEND QT5_LIBRARIES ${X11XCB_XCBGLX_FONTCONFIG_STATIC_LIBRARIES})
endif()
endif()
message(STATUS "Using Boost include dir at ${Boost_INCLUDE_DIRS}")
@@ -305,7 +389,9 @@ if(MINGW)
else()
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
endif()
elseif(APPLE OR OPENBSD OR ANDROID)
elseif(APPLE)
set(EXTRA_LIBRARIES "-framework AppKit")
elseif(OPENBSD OR ANDROID)
set(EXTRA_LIBRARIES "")
elseif(FREEBSD)
set(EXTRA_LIBRARIES execinfo)
@@ -369,7 +455,7 @@ if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VER
endif()
# linker
if (NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
if (NOT APPLE AND NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
# Windows binaries die on startup with PIE when compiled with GCC
add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS)
endif()
@@ -384,13 +470,36 @@ if (noexecheap_SUPPORTED)
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
endif()
# some windows linker bits
if (WIN32)
add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,--nxcompat LD_SECURITY_FLAGS)
add_linker_flag_if_supported(-Wl,--high-entropy-va LD_SECURITY_FLAGS)
endif()
if(STATIC)
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
if(MINGW)
add_linker_flag_if_supported(-static STATIC_FLAGS)
endif()
endif()
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
# is fixed in the code (Issue #847), force compiler to be conservative.
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fno-strict-aliasing CXX_SECURITY_FLAGS)
add_c_flag_if_supported(-fPIC C_SECURITY_FLAGS)
add_cxx_flag_if_supported(-fPIC CXX_SECURITY_FLAGS)
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_SECURITY_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_SECURITY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
if (APPLE)
@@ -408,8 +517,6 @@ if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
endif()
endif()
add_subdirectory(translations)
add_subdirectory(src)
# Required to make wallet_merged build before the gui
add_dependencies(monero-gui wallet_merged)

64
DEPLOY.md Normal file
View File

@@ -0,0 +1,64 @@
# macOS:
Use macOS 10.12 - 10.13 for better backwards compability.
1. `HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" brew install boost zmq libpgm miniupnpc libsodium expat libunwind-headers protobuf libgcrypt`
2. `HOMEBREW_OPTFLAGS="-march=core2" HOMEBREW_OPTIMIZATION_LEVEL="O0" brew install --HEAD hidapi`
3. Get the latest LTS from here: https://www.qt.io/offline-installers and install
4. `export PATH=$PATH:$HOME/Qt5.12.8/5.12.8/clang_64/bin`
5. `git clone https://github.com/monero-project/monero-gui`
6. `git checkout v0.X.Y.Z`
7. `sed -i '' s/ARCH=\"native\"/ARCH=\"x86-64\"/g get_libwallet_api.sh`
8. `sed -i '' s/-O2/-O0/g monero-wallet-gui.pro`
9. `./build.sh`
10. `cd build && make deploy`
11. `cd release/bin/monero-wallet-gui.app/Contents/PlugIns/imageformats/`
12. `cp ~/Qt5.12.8/5.12.8/clang_64/plugins/imageformats/libqsvg.dylib .`
13. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/clang_64/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
14. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/clang_64/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
15. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/lib/QtSvg.framework/Versions/5/QtSvg @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
16. `install_name_tool -change ~/Qt5.12.8/5.12.8/clang_64/clang_64/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui libqsvg.dylib`
17. Replace the `monerod` binary inside `monero-wallet-gui.app/Contents/MacOS/` with one built using deterministic builds / gitian.
## Codesigning and notarizing
1. Save the following text as `entitlements.plist`
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
</dict>
</plist>
```
2. `codesign --deep --force --verify --verbose --options runtime --timestamp --entitlements entitlements.plist --sign 'XXXXXXXXXX' monero-wallet-gui.app`
You can check if this step worked by using `codesign -dvvv monero-wallet-gui.app`
3. `hdiutil create -fs HFS+ -srcfolder monero-gui-v0.X.Y.Z -volname monero-wallet-gui monero-gui-mac-x64-v0.X.Y.Z.dmg`
4. `xcrun altool -t osx --file monero-gui-mac-x64-v0.X.Y.Z.dmg --primary-bundle-id org.monero-project.monero-wallet-gui.dmg --notarize-app --username email@address.org`
5. `xcrun altool --notarization-info aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee -u email@address.org`
6. `xcrun stapler staple -v monero-gui-mac-x64-v0.X.Y.Z.dmg`

185
Dockerfile Normal file
View File

@@ -0,0 +1,185 @@
FROM ubuntu:16.04
ARG THREADS=1
RUN apt update
RUN apt install -y automake git pkg-config python xutils-dev && \
git clone -b xorgproto-2020.1 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xorgproto && \
cd xorgproto && \
git reset --hard c62e8203402cafafa5ba0357b6d1c019156c9f36 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/proto/xcbproto && \
cd xcbproto && \
git reset --hard 6398e42131eedddde0d98759067dde933191f049 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y libtool-bin && \
git clone -b libXau-1.0.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxau && \
cd libxau && \
git reset --hard d9443b2c57b512cfb250b35707378654d86c7dea && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y libpthread-stubs0-dev && \
git clone -b 1.12 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxcb && \
cd libxcb && \
git reset --hard d34785a34f28fa6a00f8ce00d87e3132ff0f6467 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b v1.2.11 --depth 1 https://github.com/madler/zlib && \
cd zlib && \
git reset --hard cacf7f1d4e3d44d871b605da3b647f07d718623f && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --static && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b VER-2-10-2 --depth 1 https://git.sv.nongnu.org/r/freetype/freetype2.git && \
cd freetype2 && \
git reset --hard 132f19b779828b194b3fede187cee719785db4d8 && \
./autogen.sh && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --with-zlib=no && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b R_2_2_9 --depth 1 https://github.com/libexpat/libexpat && \
cd libexpat/expat && \
git reset --hard a7bc26b69768f7fb24f0c7976fae24b157b85b13 && \
./buildconf.sh && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y autopoint gettext gperf libpng12-dev && \
git clone -b 2.13.92 --depth 1 https://gitlab.freedesktop.org/fontconfig/fontconfig && \
cd fontconfig && \
git reset --hard b1df1101a643ae16cdfa1d83b939de2497b1bf27 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static --sysconfdir=/etc --localstatedir=/var && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b release-64-2 --depth 1 https://github.com/unicode-org/icu && \
cd icu/icu4c/source && \
git reset --hard e2d85306162d3a0691b070b4f0a73e4012433444 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-tests --disable-samples && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y wget && \
wget https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.gz && \
echo "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf boost_1_73_0.tar.gz" > hashsum.txt && \
sha256sum -c hashsum.txt && \
tar -xvzf boost_1_73_0.tar.gz && \
cd boost_1_73_0 && \
./bootstrap.sh && \
./b2 --with-atomic --with-system --with-filesystem --with-thread --with-date_time --with-chrono --with-regex --with-serialization --with-program_options --with-locale variant=release link=static runtime-link=static cflags='-fPIC' cxxflags='-fPIC' install -a --prefix=/usr
RUN wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz && \
echo "ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46 openssl-1.1.1g.tar.gz" > hashsum.txt && \
sha256sum -c hashsum.txt && \
tar -xzf openssl-1.1.1g.tar.gz && \
cd openssl-1.1.1g && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./config no-asm no-shared no-zlib-dynamic --openssldir=/usr && \
make -j$THREADS && \
make -j$THREADS install
RUN wget https://download.qt.io/archive/qt/5.9/5.9.7/single/qt-everywhere-opensource-src-5.9.7.tar.xz && \
echo "1c3852aa48b5a1310108382fb8f6185560cefc3802e81ecc099f4e62ee38516c qt-everywhere-opensource-src-5.9.7.tar.xz" > hashsum.txt && \
sha256sum -c hashsum.txt && \
tar -xf qt-everywhere-opensource-src-5.9.7.tar.xz
RUN apt install -y libgl1-mesa-dev libglib2.0-dev libxkbcommon-dev && \
cd qt-everywhere-opensource-src-5.9.7 && \
sed -ri s/\(Libs:.*\)/\\1\ -lexpat/ /usr/local/lib/pkgconfig/fontconfig.pc && \
sed -ri s/\(Libs:.*\)/\\1\ -lz/ /usr/local/lib/pkgconfig/freetype2.pc && \
sed -ri s/\(Libs:.*\)/\\1\ -lXau/ /usr/local/lib/pkgconfig/xcb.pc && \
./configure --prefix=/usr -platform linux-g++-64 -opensource -confirm-license -release -static -no-avx \
-opengl desktop -qpa xcb -system-freetype -fontconfig -glib \
-no-dbus -no-openssl -no-sql-sqlite -no-use-gold-linker \
-qt-harfbuzz -qt-libjpeg -qt-libpng -qt-pcre -qt-zlib \
-skip qt3d -skip qtandroidextras -skip qtcanvas3d -skip qtcharts -skip qtconnectivity -skip qtdatavis3d \
-skip qtdoc -skip qtgamepad -skip qtlocation -skip qtmacextras -skip qtnetworkauth -skip qtpurchasing \
-skip qtscript -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qttools \
-skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebsockets -skip qtwebview \
-skip qtwinextras -skip qtx11extras -skip gamepad -skip serialbus -skip location -skip webengine \
-nomake examples -nomake tests -nomake tools && \
make -j$THREADS && \
make -j$THREADS install
RUN cd qt-everywhere-opensource-src-5.9.7/qttools/src/linguist/lrelease && \
qmake && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y libudev-dev && \
git clone -b v1.0.23 --depth 1 https://github.com/libusb/libusb && \
cd libusb && \
git reset --hard e782eeb2514266f6738e242cdcb18e3ae1ed06fa && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b hidapi-0.9.0 --depth 1 https://github.com/libusb/hidapi && \
cd hidapi && \
git reset --hard 7da5cc91fc0d2dbe4df4f08cd31f6ca1a262418f && \
./bootstrap && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b libX11-1.6.9 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libx11 && \
cd libx11 && \
git reset --hard db7cca17ad7807e92a928da9d4c68a00f4836da2 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b libXext-1.3.4 --depth 1 https://gitlab.freedesktop.org/xorg/lib/libxext && \
cd libxext && \
git reset --hard ebb167f34a3514783966775fb12573c4ed209625 && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./autogen.sh --disable-shared --enable-static && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y libsodium-dev && \
git clone -b v4.3.2 --depth 1 https://github.com/zeromq/libzmq && \
cd libzmq && \
git reset --hard a84ffa12b2eb3569ced199660bac5ad128bff1f0 && \
./autogen.sh && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-libunwind --with-libsodium && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b libgpg-error-1.38 --depth 1 git://git.gnupg.org/libgpg-error.git && \
cd libgpg-error && \
git reset --hard 71d278824c5fe61865f7927a2ed1aa3115f9e439 && \
./autogen.sh && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-doc --disable-tests && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b libgcrypt-1.8.5 --depth 1 git://git.gnupg.org/libgcrypt.git && \
cd libgcrypt && \
git reset --hard 56606331bc2a80536db9fc11ad53695126007298 && \
./autogen.sh && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --disable-shared --enable-static --disable-doc && \
make -j$THREADS && \
make -j$THREADS install
RUN git clone -b v3.10.0 --depth 1 https://github.com/protocolbuffers/protobuf && \
cd protobuf && \
git reset --hard 6d4e7fd7966c989e38024a8ea693db83758944f1 && \
./autogen.sh && \
CFLAGS='-fPIC' CXXFLAGS='-fPIC' ./configure --enable-static --disable-shared && \
make -j$THREADS && \
make -j$THREADS install
RUN apt install -y cmake libusb-1.0-0-dev

View File

@@ -64,7 +64,6 @@ Rectangle {
signal addressBookClicked()
signal miningClicked()
signal signClicked()
signal merchantClicked()
signal accountClicked()
function selectItem(pos) {
@@ -72,7 +71,6 @@ Rectangle {
if(pos === "History") menuColumn.previousButton = historyButton
else if(pos === "Transfer") menuColumn.previousButton = transferButton
else if(pos === "Receive") menuColumn.previousButton = receiveButton
else if(pos === "Merchant") menuColumn.previousButton = merchantButton
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
else if(pos === "Mining") menuColumn.previousButton = miningButton
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
@@ -265,6 +263,10 @@ Rectangle {
anchors.leftMargin: 58
anchors.baseline: currencyLabel.baseline
color: MoneroComponents.Style.blackTheme ? "white" : "black"
Binding on color {
when: balancePart1MouseArea.containsMouse || balancePart2MouseArea.containsMouse
value: MoneroComponents.Style.orange
}
text: {
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
return balanceFiatString.split('.')[0] + "."
@@ -286,14 +288,6 @@ Rectangle {
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onEntered: {
balancePart1.color = MoneroComponents.Style.orange
balancePart2.color = MoneroComponents.Style.orange
}
onExited: {
balancePart1.color = Qt.binding(function() { return MoneroComponents.Style.blackTheme ? "white" : "black" })
balancePart2.color = Qt.binding(function() { return MoneroComponents.Style.blackTheme ? "white" : "black" })
}
onClicked: {
console.log("Copied to clipboard");
clipboard.setText(balancePart1.text + balancePart2.text);
@@ -307,7 +301,7 @@ Rectangle {
anchors.left: balancePart1.right
anchors.leftMargin: 2
anchors.baseline: currencyLabel.baseline
color: MoneroComponents.Style.blackTheme ? "white" : "black"
color: balancePart1.color
text: {
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
return balanceFiatString.split('.')[1]
@@ -317,11 +311,10 @@ Rectangle {
}
font.pixelSize: 16
MouseArea {
id: balancePart2MouseArea
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onEntered: balancePart1MouseArea.entered()
onExited: balancePart1MouseArea.exited()
onClicked: balancePart1MouseArea.clicked(mouse)
}
}
@@ -454,30 +447,6 @@ Rectangle {
anchors.leftMargin: 20
}
// ------------- Merchant tab ---------------
MoneroComponents.MenuButton {
id: merchantButton
visible: appWindow.walletMode >= 2
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Merchant") + translationManager.emptyString
symbol: qsTr("U") + translationManager.emptyString
under: receiveButton
onClicked: {
parent.previousButton.checked = false
parent.previousButton = merchantButton
panel.merchantClicked()
}
}
MoneroComponents.MenuButtonDivider {
visible: merchantButton.present && appWindow.walletMode >= 2
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 20
}
// ------------- History tab ---------------
MoneroComponents.MenuButton {

View File

@@ -28,13 +28,21 @@ clean:
scanner:
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},ON) -D WITH_SCANNER=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
release:
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
release-static:
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
debug-static-win64:
mkdir -p $(builddir)/debug && cd $(builddir)/debug && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
mkdir -p $(builddir)/debug && cd $(builddir)/debug && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
debug-static-mac64:
mkdir -p $(builddir)/debug
cd $(builddir)/debug && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" $(topdir) && $(MAKE)
release-static-win64:
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
release-win64:
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=OFF -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)

View File

@@ -50,7 +50,10 @@ Rectangle {
property alias contentHeight: mainFlickable.contentHeight
property alias flickable: mainFlickable
property Transfer transferView: Transfer { }
property Transfer transferView: Transfer {
onPaymentClicked: root.paymentClicked(address, paymentId, amount, mixinCount, priority, description)
onSweepUnmixableClicked: root.sweepUnmixableClicked()
}
property Receive receiveView: Receive { }
property Merchant merchantView: Merchant { }
property TxKey txkeyView: TxKey { }
@@ -260,18 +263,4 @@ Rectangle {
anchors.bottom: parent.bottom
anchors.left: borderLeft.right
}
/* connect "payment" click */
Connections {
ignoreUnknownSignals: false
target: transferView
onPaymentClicked : {
console.log("MiddlePanel: paymentClicked")
paymentClicked(address, paymentId, amount, mixinCount, priority, description)
}
onSweepUnmixableClicked : {
console.log("MiddlePanel: sweepUnmixableClicked")
sweepUnmixableClicked()
}
}
}

View File

@@ -36,7 +36,7 @@ As with many development projects, the repository on Github is considered to be
Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard.
The Monero donation address is: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`)
The Monero donation address is: `888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`)
The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H`
@@ -62,6 +62,11 @@ Do you speak a second language and would like to help translate the Monero GUI?
If you need help/support or any info you can contact the localization workgroup on the IRC channel #monero-translations (relayed on matrix/riot and MatterMost) or by email at translate[at]getmonero[dot]org. For more info about the Localization workgroup: [github.com/monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations)
Status of the translations:
<a href="https://translate.getmonero.org/engage/monero/?utm_source=widget">
<img src="https://translate.getmonero.org/widgets/monero/-/gui-wallet/horizontal-auto.svg" alt="Translation status" />
</a>
## Installing the Monero GUI from a package
Packages are available for
@@ -76,6 +81,28 @@ Packaging for your favorite distribution would be a welcome contribution!
*Note*: Qt 5.9.7 is the minimum version required to build the GUI.
### Building Linux static binaries with Docker (any OS)
1. Install Docker [https://docs.docker.com/engine/install/](https://docs.docker.com/engine/install/)
2. Clone the repository
```
git clone --recursive https://github.com/monero-project/monero-gui.git
```
3. Prepare build environment
```
cd monero-gui
docker build --tag monero:build-env-gui --build-arg THREADS=4 .
```
\* `4` - number of CPU threads to use
4. Build
```
docker run --rm -it -v <MONERO_GUI_DIR_FULL_PATH>:/monero-gui -w /monero-gui monero:build-env-gui sh -c 'USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release-static -j4'
```
\* `<MONERO_GUI_DIR_FULL_PATH>` - absolute path to `monero-gui` directory
\* `4` - number of CPU threads to use
5. Monero GUI Linux static binaries will be placed in `monero-gui/build/release/bin` directory
### On Linux:
(Tested on Ubuntu 17.10 x64, Ubuntu 18.04 x64 and Gentoo x64)
@@ -84,15 +111,15 @@ Packaging for your favorite distribution would be a welcome contribution!
- For Debian distributions (Debian, Ubuntu, Mint, Tails...)
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev`
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev`
- For Gentoo
`sudo emerge app-arch/xz-utils app-doc/doxygen dev-cpp/gtest dev-libs/boost dev-libs/expat dev-libs/openssl dev-util/cmake media-gfx/graphviz net-dns/unbound net-libs/ldns net-libs/miniupnpc net-libs/zeromq sys-libs/libunwind dev-libs/libsodium dev-libs/hidapi`
`sudo emerge app-arch/xz-utils app-doc/doxygen dev-cpp/gtest dev-libs/boost dev-libs/expat dev-libs/openssl dev-util/cmake media-gfx/graphviz net-dns/unbound net-libs/ldns net-libs/miniupnpc net-libs/zeromq sys-libs/libunwind dev-libs/libsodium dev-libs/hidapi dev-libs/libgcrypt`
- For Fedora
`sudo dnf install make automake cmake gcc-c++ boost-devel miniupnpc-devel graphviz doxygen unbound-devel libunwind-devel pkgconfig openssl-devel libcurl-devel hidapi-devel libusb-devel zeromq-devel`
`sudo dnf install make automake cmake gcc-c++ boost-devel miniupnpc-devel graphviz doxygen unbound-devel libunwind-devel pkgconfig openssl-devel libcurl-devel hidapi-devel libusb-devel zeromq-devel libgcrypt-devel`
2. Install Qt:
@@ -144,26 +171,12 @@ The executable can be found in the build/release/bin folder.
3. Install [monero](https://github.com/monero-project/monero) dependencies:
`brew install boost`
`brew install openssl` - to install openssl headers
`brew install pkgconfig`
`brew install cmake`
`brew install zeromq`
*Note*: If cmake can not find zmq.hpp file on OS X, installing `zmq.hpp` from https://github.com/zeromq/cppzmq to `/usr/local/include` should fix that error.
`brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf libgcrypt`
4. Install Qt:
`brew install qt5` (or download QT 5.9.7+ from [qt.io](https://www.qt.io/download-open-source/))
If you have an older version of Qt installed via homebrew, you can force it to use 5.x like so:
`brew link --force --overwrite qt5`
5. Add the Qt bin directory to your path
- Example for Qt: `export PATH=$PATH:$HOME/Qt/5.9.7/clang_64/bin`
@@ -183,18 +196,7 @@ The executable can be found in the build/release/bin folder.
The executable can be found in the `build/release/bin` folder.
**Note:** Workaround for "ERROR: Xcode not set up properly"
Edit `$HOME/Qt/5.9.7/clang_64/mkspecs/features/mac/default_pre.prf`
replace
`isEmpty($$list($$system("/usr/bin/xcrun -find xcrun 2>/dev/null")))`
with
`isEmpty($$list($$system("/usr/bin/xcrun -find xcodebuild 2>/dev/null")))`
More info: http://stackoverflow.com/a/35098040/1683164
For building an application bundle see `DEPLOY.md`.
### On Windows:
@@ -207,7 +209,7 @@ The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not off
3. Install MSYS2 packages for Monero dependencies; the needed 64-bit packages have `x86_64` in their names
```
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb mingw-w64-x86_64-libgcrypt
```
Optional : To build the flag `WITH_SCANNER`

View File

@@ -100,11 +100,8 @@ fi
# force version update
get_tag
echo "var GUI_VERSION = \"$TAGNAME\"" > version.js
pushd "$MONERO_DIR"
get_tag
popd
echo "var GUI_MONERO_VERSION = \"$TAGNAME\"" >> version.js
GUI_VERSION=$(echo "$TAGNAME" | sed 's/^v\([[:digit:]]\)/\1/')
echo "var GUI_VERSION = \"$GUI_VERSION\"" > version.js
cd build
if ! QMAKE=$(find_command qmake qmake-qt5); then

56
cmake/FindCcache.cmake Normal file
View File

@@ -0,0 +1,56 @@
# Copyright (c) 2014-2020, The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# - Try to find readline include dirs and libraries
#
# Automatically finds ccache build accelerator, if it's found in PATH.
#
# Usage of this module as follows:
#
# project(monero)
# include(FindCcache) # Include AFTER the project() macro to be able to reach the CMAKE_CXX_COMPILER variable
#
# Properties modified by this module:
#
# GLOBAL PROPERTY RULE_LAUNCH_COMPILE set to ccache, when ccache found
# GLOBAL PROPERTY RULE_LAUNCH_LINK set to ccache, when ccache found
find_program(CCACHE_FOUND ccache)
if (CCACHE_FOUND)
set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp")
file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }")
execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET)
if (${RET} EQUAL 0)
message("found usable ccache: ${CCACHE_FOUND}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}")
else()
message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}")
endif()
else()
message("ccache NOT found!")
endif()

View File

@@ -28,37 +28,40 @@
#
# Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
# Check what commit we're on
execute_process(COMMAND "${GIT}" rev-parse --short=9 HEAD RESULT_VARIABLE RET OUTPUT_VARIABLE COMMIT OUTPUT_STRIP_TRAILING_WHITESPACE)
function (git_get_version_tag git directory result_var)
execute_process(COMMAND "${git}" rev-parse --short HEAD
WORKING_DIRECTORY ${directory}
OUTPUT_VARIABLE COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT COMMIT)
message(WARNING "${directory}: cannot determine current commit. Make sure that you are building from a Git working tree")
set(${result_var} "unknown" PARENT_SCOPE)
return()
endif()
if(RET)
# Something went wrong, set the version tag to -unknown
message(WARNING "Cannot determine current commit. Make sure that you are building either from a Git working tree or from a source archive.")
set(VERSIONTAG "unknown")
configure_file("src/version.js.in" "${TO}")
else()
string(SUBSTRING ${COMMIT} 0 9 COMMIT)
message(STATUS "You are currently on commit ${COMMIT}")
# Get all the tags
execute_process(COMMAND "${GIT}" rev-list --tags --max-count=1 --abbrev-commit RESULT_VARIABLE RET OUTPUT_VARIABLE TAGGEDCOMMIT OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT TAGGEDCOMMIT)
message(WARNING "Cannot determine most recent tag. Make sure that you are building either from a Git working tree or from a source archive.")
set(VERSIONTAG "${COMMIT}")
else()
message(STATUS "The most recent tag was at ${TAGGEDCOMMIT}")
# Check if we're building that tagged commit or a different one
if(COMMIT STREQUAL TAGGEDCOMMIT)
message(STATUS "You are building a tagged release")
set(VERSIONTAG "release")
else()
message(STATUS "You are ahead of or behind a tagged release")
set(VERSIONTAG "${COMMIT}")
endif()
endif()
execute_process(COMMAND "${git}" describe --tags --exact-match
WORKING_DIRECTORY ${directory}
OUTPUT_VARIABLE TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(TAG)
message(STATUS "${directory}: building tagged release ${TAG}-${COMMIT}")
set(${result_var} "${TAG}-${COMMIT}" PARENT_SCOPE)
return()
endif()
configure_file("src/version.js.in" "${TO}")
endif()
execute_process(COMMAND "${git}" describe --tags --long
WORKING_DIRECTORY ${directory}
OUTPUT_VARIABLE MOST_RECENT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(MOST_RECENT_TAG)
message(STATUS "${directory}: ahead of or behind a tagged release, building ${MOST_RECENT_TAG}")
set(${result_var} "${MOST_RECENT_TAG}" PARENT_SCOPE)
return()
endif()
message(STATUS "${directory}: building ${COMMIT} commit")
set(${result_var} "${COMMIT}" PARENT_SCOPE)
endfunction()

View File

@@ -26,9 +26,8 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function (write_static_version_header hash)
set(VERSIONTAG "${hash}")
configure_file("${CMAKE_SOURCE_DIR}/version.js.in" "${CMAKE_SOURCE_DIR}/version.js")
function (write_static_version_header VERSION_TAG_GUI)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/version.js.in" "${CMAKE_CURRENT_SOURCE_DIR}/version.js")
endfunction ()
find_package(Git QUIET)
@@ -37,16 +36,14 @@ if ("$Format:$" STREQUAL "")
write_static_version_header("release")
elseif (GIT_FOUND OR Git_FOUND)
message(STATUS "Found Git: ${GIT_EXECUTABLE}")
add_custom_command(
OUTPUT "${CMAKE_SOURCE_DIR}/version.js"
COMMAND "${CMAKE_COMMAND}"
"-D" "GIT=${GIT_EXECUTABLE}"
"-D" "TO=${CMAKE_SOURCE_DIR}/version.js"
"-P" "cmake/GenVersionGui.cmake"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
include(GitGetVersionTag)
git_get_version_tag(${GIT_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR} VERSION_TAG_GUI)
STRING(REGEX REPLACE "^v([0-9])" "\\1" VERSION_TAG_GUI ${VERSION_TAG_GUI})
write_static_version_header(${VERSION_TAG_GUI})
else()
message(STATUS "WARNING: Git was not found!")
write_static_version_header("unknown")
endif ()
add_custom_target(genversiongui ALL
DEPENDS "${CMAKE_SOURCE_DIR}/version.js")
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/version.js")

View File

@@ -0,0 +1,106 @@
import QtQuick 2.9
import QtQuick.Layouts 1.1
import FontAwesome 1.0
import "../components" as MoneroComponents
RowLayout {
id: advancedOptionsItem
property alias title: title.text
property alias button1: button1
property alias button2: button2
property alias button3: button3
property alias helpTextLarge: helpTextLarge
property alias helpTextSmall: helpTextSmall
RowLayout {
id: titlecolumn
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
Layout.preferredWidth: 195
Layout.maximumWidth: 195
Layout.leftMargin: 10
MoneroComponents.Label {
id: title
fontSize: 14
}
MoneroComponents.Label {
id: iconLabel
fontSize: 12
text: FontAwesome.questionCircle
fontFamily: FontAwesome.fontFamily
opacity: 0.3
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: helpText.visible = !helpText.visible
onEntered: parent.opacity = 0.4
onExited: parent.opacity = 0.3
}
}
Rectangle {
id: separator
Layout.fillWidth: true
height: 10
color: "transparent"
}
}
ColumnLayout {
Layout.fillWidth: false
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
spacing: 4
RowLayout {
Layout.fillWidth: false
spacing: 12
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
StandardButton {
id: button1
small: true
visible: button1.text
}
StandardButton {
id: button2
small: true
visible: button2.text
}
StandardButton {
id: button3
small: true
visible: button3.text
}
}
ColumnLayout {
id: helpText
visible: false
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
MoneroComponents.TextPlain {
id: helpTextLarge
visible: helpTextLarge.text
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 13
color: MoneroComponents.Style.defaultFontColor
}
MoneroComponents.TextPlain {
id: helpTextSmall
visible: helpTextSmall.text
Layout.leftMargin: 5
textFormat: Text.RichText
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 12
color: MoneroComponents.Style.defaultFontColor
}
}
}
}

View File

@@ -74,7 +74,7 @@ Item {
visible: checkBox.border
anchors.fill: parent
radius: 3
color: "transparent"
color: checkBox.enabled ? "transparent" : MoneroComponents.Style.inputBoxBackgroundDisabled
border.color:
if(checkBox.checked){
return MoneroComponents.Style.inputBorderColorActive;

View File

@@ -117,7 +117,7 @@ Item {
Connections {
target: datePicker
onCurrentDateChanged: {
function onCurrentDateChanged() {
dateInput.setDate(datePicker.currentDate)
}
}

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.9
import "." as MoneroComponents
Item {
id: root
property var onAcceptedCallback
property var onWalletEntryCallback
property var onRejectedCallback
function open(canEnterOnDevice_) {
var canEnterOnDevice = canEnterOnDevice_ !== null ? canEnterOnDevice_ : canEnterOnDevice
root.visible = true;
if (canEnterOnDevice) {
entryChooserDialog.okText = qsTr("Hardware wallet")
entryChooserDialog.cancelText = qsTr("Computer")
entryChooserDialog.open()
} else {
openPassphraseDialog()
}
}
function openPassphraseDialog() {
root.visible = true
passphraseDialog.openPassphraseDialog()
}
function close() {
root.visible = false;
if (entryChooserDialog.visible)
entryChooserDialog.close()
if (passphraseDialog.visible)
passphraseDialog.close()
}
StandardDialog {
id: entryChooserDialog
title: qsTr("Hardware wallet passphrase") + translationManager.emptyString
text: qsTr("Please select where you want to enter passphrase.\nIt is recommended to enter passphrase on the hardware wallet for better security.") + translationManager.emptyString
onAccepted: {
if (onWalletEntryCallback){
onWalletEntryCallback()
}
}
onRejected: {
openPassphraseDialog()
}
onCloseCallback: {
root.close()
}
}
PasswordDialog {
id: passphraseDialog
anchors.fill: parent
passphraseDialogMode: true
onAcceptedPassphrase: {
if (onAcceptedCallback)
onAcceptedCallback(passphraseDialog.password);
}
onRejectedPassphrase: {
if (onRejectedCallback)
onRejectedCallback();
}
onCloseCallback: {
root.close()
}
}
}

View File

@@ -53,6 +53,7 @@ Item {
property alias fontPixelSize: inlineText.font.pixelSize
property alias fontFamily: inlineText.font.family
property alias buttonColor: rect.color
property alias buttonHeight: rect.height
signal clicked()
function doClick() {

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2014-2019, The Monero Project
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
@@ -53,7 +53,7 @@ Drawer {
y: titleBar.height
background: Rectangle {
color: "#0d0d0d"
color: MoneroComponents.Style.blackTheme ? "#0d0d0d" : "white"
width: parent.width
}
@@ -79,9 +79,24 @@ Drawer {
width: sideBar.width
height: 32
Rectangle {
id: flagRect
height: 24
width: 24
anchors.left: parent.left
anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter
color: "transparent"
Image {
anchors.fill: parent
source: flag
}
}
MoneroComponents.TextPlain {
anchors.left: parent.left
anchors.leftMargin: 16
anchors.leftMargin: 30
font.bold: true
font.pixelSize: 14
color: MoneroComponents.Style.defaultFontColor
@@ -118,8 +133,10 @@ Drawer {
// set wizard language settings
wizard.language_locale = locale;
wizard.language_wallet = wallet_language;
wizard.language_language = display_name + " (" + locale_spl[1] + ") ";
sideBar.close()
wizard.language_language = display_name;
appWindow.showStatusMessage(qsTr("Language changed."), 3);
appWindow.toggleLanguageView();
}
hoverEnabled: true
onEntered: {
@@ -134,15 +151,8 @@ Drawer {
}
}
ScrollIndicator.vertical: ScrollIndicator {
// @TODO: QT 5.9 introduces `policy: ScrollBar.AlwaysOn`
active: true
contentItem.opacity: 0.7
onActiveChanged: {
if (!active) {
active = true;
}
}
ScrollBar.vertical: ScrollBar {
onActiveChanged: if (!active && !isMac) active = true
}
}
}

View File

@@ -26,6 +26,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import FontAwesome 1.0
import QtQuick 2.9
import QtGraphicalEffects 1.0
@@ -36,6 +37,10 @@ Item {
property alias input: input
property alias text: input.text
property bool password: false
property bool passwordHidden: true
property var passwordLinked: null
property alias placeholderText: placeholderLabel.text
property bool placeholderCenter: false
property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name
@@ -48,7 +53,6 @@ Item {
property alias validator: input.validator
property alias readOnly : input.readOnly
property alias cursorPosition: input.cursorPosition
property alias echoMode: input.echoMode
property alias inlineButton: inlineButtonId
property alias inlineButtonText: inlineButtonId.text
property alias inlineIcon: inlineIcon.visible
@@ -109,6 +113,31 @@ Item {
}
}
function isPasswordHidden() {
if (password) {
return passwordHidden;
}
if (passwordLinked) {
return passwordLinked.passwordHidden;
}
return false;
}
function reset() {
text = "";
if (!passwordLinked) {
passwordHidden = true;
}
}
function passwordToggle() {
if (passwordLinked) {
passwordLinked.passwordHidden = !passwordLinked.passwordHidden;
} else {
passwordHidden = !passwordHidden;
}
}
MoneroComponents.TextPlain {
id: inputLabel
anchors.top: parent.top
@@ -210,6 +239,27 @@ Item {
onTextChanged: item.textUpdated()
topPadding: 10
bottomPadding: 10
echoMode: isPasswordHidden() ? TextInput.Password : TextInput.Normal
MoneroComponents.Label {
visible: password || passwordLinked
fontSize: 20
text: isPasswordHidden() ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: eyeMouseArea.containsMouse ? 0.9 : 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
id: eyeMouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: passwordToggle()
}
}
}
MoneroComponents.InlineButton {

View File

@@ -87,6 +87,8 @@ ColumnLayout {
property alias inlineButton: inlineButtonId
property bool inlineButtonVisible: false
property alias inlineButton2: inlineButton2Id
property bool inlineButton2Visible: false
signal labelButtonClicked();
signal inputLabelLinkActivated();
@@ -202,5 +204,12 @@ ColumnLayout {
anchors.right: parent.right
anchors.rightMargin: 8
}
MoneroComponents.InlineButton {
id: inlineButton2Id
visible: (inlineButton2Id.text || inlineButton2Id.icon) && inlineButton2Visible ? true : false
anchors.right: parent.right
anchors.rightMargin: inlineButtonVisible ? 48 : 8
}
}
}

View File

@@ -62,7 +62,7 @@ Rectangle {
height: present ? ((appWindow.height >= 800) ? 44 : 38 ) : 0
LinearGradient {
visible: isOpenGL && button.checked
visible: isOpenGL && (button.checked || buttonArea.containsMouse)
height: parent.height
width: 260
anchors.verticalCenter: parent.verticalCenter
@@ -75,13 +75,15 @@ Rectangle {
GradientStop { position: 0.0; color: MoneroComponents.Style.menuButtonGradientStart }
GradientStop { position: 1.0; color: MoneroComponents.Style.menuButtonGradientStop }
}
opacity: button.checked ? 1 : 0.3
}
// fallback hover effect when opengl is not available
Rectangle {
visible: !isOpenGL && button.checked
visible: !isOpenGL && (button.checked || buttonArea.containsMouse)
anchors.fill: parent
color: MoneroComponents.Style.menuButtonFallbackBackgroundColor
opacity: button.checked ? 1 : 0.3
}
// button decorations that are subject to leftMargin offsets

View File

@@ -41,9 +41,7 @@ import "../js/Utils.js" as Utils
Item {
id: root
visible: false
z: parent.z + 2
property bool isHidden: true
property alias password: passwordInput1.text
property string walletName
property string errorText
@@ -61,13 +59,11 @@ Item {
signal closeCallback()
function _openInit(walletName, errorText) {
isHidden = true
capsLockTextLabel.visible = oshelper.isCapsLock();
passwordInput1.echoMode = TextInput.Password
passwordInput2.echoMode = TextInput.Password
passwordInput1.text = ""
passwordInput2.text = ""
passwordInput1.forceActiveFocus();
passwordInput1.reset();
passwordInput2.reset();
if(!appWindow.currentWallet || appWindow.active)
passwordInput1.input.forceActiveFocus();
root.walletName = walletName ? walletName : ""
errorTextLabel.text = errorText ? errorText : "";
leftPanel.enabled = false
@@ -116,10 +112,29 @@ Item {
closeCallback();
}
function toggleIsHidden() {
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
isHidden = !isHidden;
function onOk() {
if (!passwordDialogMode && passwordInput1.text !== passwordInput2.text) {
return;
}
root.close()
if (passwordDialogMode) {
root.accepted()
} else if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
}
function onCancel() {
root.close()
if (passwordDialogMode) {
root.rejected()
} else if (newPasswordDialogMode) {
root.rejectedNewPassword()
} else if (passphraseDialogMode) {
root.rejectedPassphrase()
}
}
ColumnLayout {
@@ -184,15 +199,11 @@ Item {
text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
}
MoneroComponents.Input {
MoneroComponents.LineEdit {
id: passwordInput1
password: true
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
KeyNavigation.tab: {
if (passwordDialogMode) {
return okButton
@@ -200,81 +211,12 @@ Item {
return passwordInput2
}
}
implicitHeight: 50
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
background: Rectangle {
radius: 2
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
MoneroEffects.ColorTransition {
targetObj: parent
blackColor: "black"
whiteColor: "#A9FFFFFF"
}
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden();
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
}
}
Keys.enabled: root.visible
Keys.onEnterPressed: Keys.onReturnPressed(event)
Keys.onReturnPressed: {
if (!passwordDialogMode && passwordInput1.text !== passwordInput2.text) {
return;
}
root.close()
if (passwordDialogMode) {
root.accepted()
} else if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
}
Keys.onEscapePressed: {
root.close()
if (passwordDialogMode) {
root.rejected()
} else if (newPasswordDialogMode) {
root.rejectedNewPassword()
} else if (passphraseDialogMode) {
root.rejectedPassphrase()
}
}
Keys.onEnterPressed: root.onOk()
Keys.onReturnPressed: root.onOk()
Keys.onEscapePressed: root.onCancel()
}
// padding
@@ -298,81 +240,19 @@ Item {
color: MoneroComponents.Style.defaultFontColor
}
MoneroComponents.Input {
MoneroComponents.LineEdit {
id: passwordInput2
passwordLinked: passwordInput1
visible: !passwordDialogMode
Layout.topMargin: 6
Layout.fillWidth: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
font.family: MoneroComponents.Style.fontLight.name
font.pixelSize: 24
echoMode: TextInput.Password
KeyNavigation.tab: okButton
implicitHeight: 50
bottomPadding: 10
leftPadding: 10
topPadding: 10
color: MoneroComponents.Style.defaultFontColor
selectionColor: MoneroComponents.Style.textSelectionColor
selectedTextColor: MoneroComponents.Style.textSelectedColor
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
background: Rectangle {
radius: 2
border.color: MoneroComponents.Style.inputBorderColorInActive
border.width: 1
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
MoneroComponents.Label {
fontSize: 20
text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash
opacity: 0.7
fontFamily: FontAwesome.fontFamily
anchors.right: parent.right
anchors.rightMargin: 15
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 1
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
toggleIsHidden()
}
onEntered: {
parent.opacity = 0.9
parent.fontSize = 24
}
onExited: {
parent.opacity = 0.7
parent.fontSize = 20
}
}
}
}
Keys.enabled: root.visible
Keys.onEnterPressed: Keys.onReturnPressed(event)
Keys.onReturnPressed: {
if (passwordInput1.text === passwordInput2.text) {
root.close()
if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
}
}
Keys.onEscapePressed: {
root.close()
if (newPasswordDialogMode) {
root.rejectedNewPassword()
} else if (passphraseDialogMode) {
root.rejectedPassphrase()
}
}
Keys.onEnterPressed: root.onOk()
Keys.onReturnPressed: root.onOk()
Keys.onEscapePressed: root.onCancel()
}
// padding
@@ -397,16 +277,7 @@ Item {
small: true
text: qsTr("Cancel") + translationManager.emptyString
KeyNavigation.tab: passwordInput1
onClicked: {
root.close()
if (passwordDialogMode) {
root.rejected()
} else if (newPasswordDialogMode) {
root.rejectedNewPassword()
} else if (passphraseDialogMode) {
root.rejectedPassphrase()
}
}
onClicked: onCancel()
}
MoneroComponents.StandardButton {
@@ -415,16 +286,7 @@ Item {
text: qsTr("Ok") + translationManager.emptyString
KeyNavigation.tab: cancelButton
enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text
onClicked: {
root.close()
if (passwordDialogMode) {
root.accepted()
} else if (newPasswordDialogMode) {
root.acceptedNewPassword()
} else if (passphraseDialogMode) {
root.acceptedPassphrase()
}
}
onClicked: onOk()
}
}
}

View File

@@ -29,21 +29,23 @@
import QtQuick 2.9
import QtQuick.Window 2.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.1
import "../components" as MoneroComponents
Rectangle {
id: root
color: MoneroComponents.Style.blackTheme ? "white" : "transparent"
color: MoneroComponents.Style.blackTheme ? "black" : "white"
visible: false
radius: 10
border.color: MoneroComponents.Style.blackTheme ? Qt.rgba(255, 255, 255, 0.25) : Qt.rgba(0, 0, 0, 0.25)
border.width: 1
z: 11
property alias messageText: messageTitle.text
property alias heightProgressText : heightProgress.text
width: 200
height: 100
opacity: 0.7
width: 100
height: 50
function show() {
root.visible = true;
@@ -56,44 +58,55 @@ Rectangle {
ColumnLayout {
id: rootLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.centerIn: parent
anchors.leftMargin: 30
anchors.rightMargin: 30
spacing: 12
spacing: 21
BusyIndicator {
running: parent.visible
Item {
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.preferredHeight: 80
Image {
id: imgLogo
width: 60
height: 60
anchors.centerIn: parent
source: "qrc:///images/monero-vector.svg"
mipmap: true
}
BusyIndicator {
running: parent.visible
anchors.centerIn: imgLogo
style: BusyIndicatorStyle {
indicator: Image {
visible: control.running
source: "qrc:///images/busy-indicator.png"
RotationAnimator on rotation {
running: control.running
loops: Animation.Infinite
duration: 1000
from: 0
to: 360
}
}
}
}
}
MoneroComponents.TextPlain {
id: messageTitle
text: "Please wait..."
font {
pixelSize: 22
}
text: qsTr("Please wait...") + translationManager.emptyString
font.pixelSize: 24
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.fillWidth: true
themeTransition: false
color: "black"
}
MoneroComponents.TextPlain {
id: heightProgress
font {
pixelSize: 18
}
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.fillWidth: true
themeTransition: false
color: "black"
color: MoneroComponents.Style.defaultFontColor
}
}
}

View File

@@ -42,7 +42,7 @@ Rectangle {
function updateProgress(currentBlock,targetBlock, blocksToSync, statusTxt){
if(targetBlock > 0) {
var remaining = (currentBlock < targetBlock) ? targetBlock - currentBlock : 0
var progressLevel = (blocksToSync > 0 && blocksToSync != remaining) ? (100*(blocksToSync - remaining)/blocksToSync).toFixed(0) : (100*(currentBlock / targetBlock)).toFixed(0)
var progressLevel = (blocksToSync > 0 ) ? (100*(blocksToSync - remaining)/blocksToSync).toFixed(0) : 100
fillLevel = progressLevel
if(typeof statusTxt != "undefined" && statusTxt != "") {
progressText.text = statusTxt;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2014-2018, The Monero Project
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
@@ -53,6 +53,7 @@ Rectangle {
script: {
root.visible = true
camera.captureMode = Camera.CaptureStillImage
camera.cameraState = Camera.ActiveState
camera.start()
finder.enabled = true
}
@@ -65,6 +66,7 @@ Rectangle {
camera.stop()
root.visible = false
finder.enabled = false
camera.cameraState = Camera.UnloadedState
}
}
}
@@ -74,6 +76,7 @@ Rectangle {
id: camera
objectName: "qrCameraQML"
captureMode: Camera.CaptureStillImage
cameraState: Camera.UnloadedState
focus {
focusMode: Camera.FocusContinuous
@@ -83,9 +86,14 @@ Rectangle {
id : finder
objectName: "QrFinder"
onDecoded : {
root.qrcode_decoded(address, payment_id, amount, tx_description, recipient_name, extra_parameters)
root.state = "Stopped"
}
const parsed = walletManager.parse_uri_to_object(data);
if (!parsed.error) {
root.qrcode_decoded(parsed.address, parsed.payment_id, parsed.amount, parsed.tx_description, parsed.recipient_name, parsed.extra_parameters);
root.state = "Stopped";
} else {
onNotifyError(parsed.error);
}
}
onNotifyError : {
if( warning )
messageDialog.icon = StandardIcon.Critical

View File

@@ -43,6 +43,9 @@ GridLayout {
property alias daemonAddrLabelText: daemonAddr.labelText
property alias daemonPortLabelText: daemonPort.labelText
property string initialAddress: ""
property var initialHostPort: initialAddress.match(/^(.*?)(?:\:?(\d*))$/)
// TODO: LEGACY; remove these placeHolder variables when
// the wizards get redesigned to the black-theme
property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name
@@ -58,6 +61,9 @@ GridLayout {
property bool lineEditFontBold: false
property int lineEditFontSize: 15
// Author: David M. Syzdek https://github.com/syzdek https://gist.github.com/syzdek/6086792
readonly property var ipv6Regex: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/
signal editingFinished()
signal textChanged()
@@ -91,8 +97,12 @@ GridLayout {
fontColor: lineEditFontColor
fontBold: lineEditFontBold
fontSize: lineEditFontSize
onEditingFinished: root.editingFinished()
onEditingFinished: {
text = text.replace(ipv6Regex, "[$1]");
root.editingFinished();
}
onTextChanged: root.textChanged()
text: initialHostPort[1]
}
LineEdit {
@@ -114,5 +124,6 @@ GridLayout {
onEditingFinished: root.editingFinished()
onTextChanged: root.textChanged()
text: initialHostPort[2]
}
}

View File

@@ -1,41 +1,71 @@
import QtQuick 2.9
import QtQuick.Layouts 1.1
import FontAwesome 1.0
import "../components" as MoneroComponents
ColumnLayout {
property alias buttonText: button.text
property alias description: description.text
property alias title: title.text
id: settingsListItem
property alias iconText: iconLabel.text
property alias description: area.text
property alias title: header.text
property bool isLast: false
signal clicked()
id: settingsListItem
Layout.fillWidth: true
spacing: 0
Rectangle {
// divider
Layout.preferredHeight: 1
id: root
Layout.fillWidth: true
Layout.bottomMargin: 8
color: MoneroComponents.Style.dividerColor
opacity: MoneroComponents.Style.dividerOpacity
}
Layout.minimumHeight: 75
Layout.preferredHeight: rect.height + 15
color: "transparent"
RowLayout {
Layout.fillWidth: true
spacing: 0
Rectangle {
id: divider
anchors.topMargin: 0
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: MoneroComponents.Style.dividerColor
opacity: MoneroComponents.Style.dividerOpacity
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
Rectangle {
id: rect
width: parent.width
height: header.height + area.contentHeight
color: "transparent";
anchors.left: parent.left
anchors.bottomMargin: 4
anchors.topMargin: 4
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: icon
color: "transparent"
height: 32
width: 32
anchors.left: parent.left
anchors.leftMargin: 16
anchors.verticalCenter: parent.verticalCenter
MoneroComponents.Label {
id: iconLabel
fontSize: 32
fontFamily: FontAwesome.fontFamilySolid
anchors.centerIn: parent
fontColor: MoneroComponents.Style.defaultFontColor
styleName: "Solid"
}
}
MoneroComponents.TextPlain {
id: title
Layout.fillWidth: true
Layout.preferredHeight: 20
Layout.topMargin: 8
id: header
anchors.left: icon.right
anchors.leftMargin: 16
anchors.top: parent.top
color: MoneroComponents.Style.defaultFontColor
opacity: MoneroComponents.Style.blackTheme ? 1.0 : 0.8
font.bold: true
@@ -43,23 +73,43 @@ ColumnLayout {
font.pixelSize: 16
}
MoneroComponents.TextPlainArea {
id: description
Text {
id: area
anchors.top: header.bottom
anchors.topMargin: 4
anchors.left: icon.right
anchors.leftMargin: 16
color: MoneroComponents.Style.dimmedFontColor
colorBlackTheme: MoneroComponents.Style._b_dimmedFontColor
colorWhiteTheme: MoneroComponents.Style._w_dimmedFontColor
Layout.fillWidth: true
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 15
horizontalAlignment: TextInput.AlignLeft
wrapMode: Text.WordWrap;
leftPadding: 0
topPadding: 0
width: parent.width - (icon.width + icon.anchors.leftMargin + anchors.leftMargin)
}
}
MoneroComponents.StandardButton {
id: button
small: true
Rectangle {
id: bottomDivider
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 1
color: MoneroComponents.Style.dividerColor
opacity: MoneroComponents.Style.dividerOpacity
visible: settingsListItem.isLast
}
MouseArea {
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
hoverEnabled: true
onEntered: root.color = MoneroComponents.Style.titleBarButtonHoverColor
onExited: root.color = "transparent"
onClicked: {
settingsListItem.clicked()
}
width: 135
}
}
}

68
components/Slider.qml Normal file
View File

@@ -0,0 +1,68 @@
import QtQuick 2.9
import QtQuick.Controls 2.0 as QtQuickControls
import QtQuick.Layouts 1.1
import "../components" as MoneroComponents
ColumnLayout {
property alias from: slider.from
property alias stepSize: slider.stepSize
property alias to: slider.to
property alias value: slider.value
property alias text: label.text
signal moved()
spacing: 0
Text {
id: label
color: MoneroComponents.Style.defaultFontColor
font.pixelSize: 14
Layout.fillWidth: true
}
QtQuickControls.Slider {
id: slider
leftPadding: 0
snapMode: QtQuickControls.Slider.SnapAlways
background: Rectangle {
x: parent.leftPadding
y: parent.topPadding + parent.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: parent.availableWidth
height: implicitHeight
radius: 2
color: MoneroComponents.Style.progressBarBackgroundColor
Rectangle {
width: parent.visualPosition * parent.width
height: parent.height
color: MoneroComponents.Style.green
radius: 2
}
}
handle: Rectangle {
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
y: parent.topPadding + parent.availableHeight / 2 - height / 2
implicitWidth: 18
implicitHeight: 18
radius: 8
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
border.color: MoneroComponents.Style.grey
}
onMoved: parent.moved()
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
}
}
}

View File

@@ -33,11 +33,17 @@ import "../components" as MoneroComponents
Item {
id: button
property bool primary: true
property string rightIcon: ""
property string rightIconInactive: ""
property string textColor: button.enabled? MoneroComponents.Style.buttonTextColor: MoneroComponents.Style.buttonTextColorDisabled
property color textColor: !button.enabled
? MoneroComponents.Style.buttonTextColorDisabled
: primary
? MoneroComponents.Style.buttonTextColor
: MoneroComponents.Style.buttonSecondaryTextColor;
property bool small: false
property alias text: label.text
property alias fontBold: label.font.bold
property int fontSize: {
if(small) return 14;
else return 16;
@@ -70,7 +76,9 @@ Item {
when: buttonArea.containsMouse || button.focus
PropertyChanges {
target: buttonRect
color: MoneroComponents.Style.buttonBackgroundColorHover
color: primary
? MoneroComponents.Style.buttonBackgroundColorHover
: MoneroComponents.Style.buttonSecondaryBackgroundColorHover
}
},
State {
@@ -78,7 +86,9 @@ Item {
when: button.enabled
PropertyChanges {
target: buttonRect
color: MoneroComponents.Style.buttonBackgroundColor
color: primary
? MoneroComponents.Style.buttonBackgroundColor
: MoneroComponents.Style.buttonSecondaryBackgroundColor
}
},
State {

View File

@@ -90,6 +90,10 @@ Rectangle {
function close() {
root.visible = false;
// reset button text
okButton.text = qsTr("OK")
cancelButton.text = qsTr("Cancel")
closeCallback();
}

View File

@@ -58,11 +58,6 @@ Item {
onExpandedChanged: if(expanded) appWindow.currentItem = dropdown
// Workaroud for suspected memory leak in 5.8 causing malloc crash on app exit
function update() {
firstColText.text = columnid.currentIndex < repeater.model.rowCount() ? qsTr(repeater.model.get(columnid.currentIndex).column1) + translationManager.emptyString : ""
}
Item {
id: head
anchors.left: parent.left
@@ -80,15 +75,17 @@ Item {
}
MoneroComponents.TextPlain {
id: firstColText
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 12
anchors.right: dropIndicator.left
anchors.rightMargin: 12
elide: Text.ElideRight
font.family: MoneroComponents.Style.fontRegular.name
font.bold: dropdown.headerFontBold
font.pixelSize: dropdown.fontHeaderSize
color: dropdown.textColor
text: columnid.currentIndex < repeater.model.count ? qsTr(repeater.model.get(columnid.currentIndex).column1) + translationManager.emptyString : ""
}
Item {
@@ -96,7 +93,8 @@ Item {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 32
anchors.rightMargin: 12
width: dropdownIcon.width
Image {
id: dropdownIcon
@@ -214,7 +212,6 @@ Item {
popup.close()
columnid.currentIndex = index
changed();
dropdown.update()
}
}
}

View File

@@ -29,6 +29,7 @@ QtObject {
property string textSelectedColor: blackTheme ? _b_textSelectedColor : _w_textSelectedColor
property string inputBoxBackground: blackTheme ? _b_inputBoxBackground : _w_inputBoxBackground
property string inputBoxBackgroundDisabled: blackTheme ? _b_inputBoxBackgroundDisabled : _w_inputBoxBackgroundDisabled
property string inputBoxBackgroundError: blackTheme ? _b_inputBoxBackgroundError : _w_inputBoxBackgroundError
property string inputBoxColor: blackTheme ? _b_inputBoxColor : _w_inputBoxColor
property string legacy_placeholderFontColor: blackTheme ? _b_legacy_placeholderFontColor : _w_legacy_placeholderFontColor
@@ -43,6 +44,9 @@ QtObject {
property string buttonInlineBackgroundColor: blackTheme ? _b_buttonInlineBackgroundColor : _w_buttonInlineBackgroundColor
property string buttonTextColor: blackTheme ? _b_buttonTextColor : _w_buttonTextColor
property string buttonTextColorDisabled: blackTheme ? _b_buttonTextColorDisabled : _w_buttonTextColorDisabled
property string buttonSecondaryBackgroundColor: "#d9d9d9"
property string buttonSecondaryBackgroundColorHover: "#a6a6a6"
property string buttonSecondaryTextColor: "#4d4d4d"
property string dividerColor: blackTheme ? _b_dividerColor : _w_dividerColor
property real dividerOpacity: blackTheme ? _b_dividerOpacity : _w_dividerOpacity
@@ -85,6 +89,7 @@ QtObject {
property string _b_textSelectedColor: "white"
property string _b_inputBoxBackground: "black"
property string _b_inputBoxBackgroundDisabled: Qt.rgba(255, 255, 255, 0.10)
property string _b_inputBoxBackgroundError: "#FFDDDD"
property string _b_inputBoxColor: "white"
property string _b_legacy_placeholderFontColor: "#BABABA"
@@ -141,6 +146,7 @@ QtObject {
property string _w_textSelectedColor: "black"
property string _w_inputBoxBackground: "white"
property string _w_inputBoxBackgroundDisabled: Qt.rgba(0, 0, 0, 0.20)
property string _w_inputBoxBackgroundError: "#FFDDDD"
property string _w_inputBoxColor: "black"
property string _w_legacy_placeholderFontColor: "#BABABA"

View File

@@ -309,8 +309,8 @@ Rectangle {
width: 16
image: MoneroComponents.Style.titleBarCloseSource
color: MoneroComponents.Style.defaultFontColor
fontAwesomeFallbackIcon: FontAwesome.timesRectangle
fontAwesomeFallbackSize: 18
fontAwesomeFallbackIcon: FontAwesome.times
fontAwesomeFallbackSize: 21
fontAwesomeFallbackOpacity: MoneroComponents.Style.blackTheme ? 0.8 : 0.6
opacity: 0.75
}

203
components/UpdateDialog.qml Normal file
View File

@@ -0,0 +1,203 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.1
import moneroComponents.Downloader 1.0
import "../components" as MoneroComponents
Popup {
id: updateDialog
property bool active: false
property bool allowed: true
property string error: ""
property string filename: ""
property string hash: ""
property double progress: url && downloader.total > 0 ? downloader.loaded * 100 / downloader.total : 0
property string url: ""
property bool valid: false
property string version: ""
background: Rectangle {
border.color: MoneroComponents.Style.appWindowBorderColor
border.width: 1
color: MoneroComponents.Style.middlePanelBackgroundColor
}
closePolicy: Popup.NoAutoClose
padding: 20
visible: active && allowed
function show(version, url, hash) {
updateDialog.error = "";
updateDialog.hash = hash;
updateDialog.url = url;
updateDialog.valid = false;
updateDialog.version = version;
updateDialog.active = true;
}
ColumnLayout {
id: mainLayout
spacing: updateDialog.padding
Text {
color: MoneroComponents.Style.defaultFontColor
font.bold: true
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 18
text: qsTr("New Monero version v%1 is available.").arg(updateDialog.version)
}
Text {
id: errorText
color: "red"
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 18
text: updateDialog.error
visible: text
}
Text {
id: statusText
color: updateDialog.valid ? MoneroComponents.Style.green : MoneroComponents.Style.defaultFontColor
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 18
visible: !errorText.visible
text: {
if (!updateDialog.url) {
return qsTr("Please visit getmonero.org for details") + translationManager.emptyString;
}
if (downloader.active) {
return "%1 (%2%)"
.arg(qsTr("Downloading"))
.arg(updateDialog.progress.toFixed(1))
+ translationManager.emptyString;
}
if (updateDialog.valid) {
return qsTr("Update downloaded, signature verified") + translationManager.emptyString;
}
return qsTr("Do you want to download and verify new version?") + translationManager.emptyString;
}
}
Rectangle {
id: progressBar
color: MoneroComponents.Style.lightGreyFontColor
height: 3
Layout.fillWidth: true
visible: updateDialog.valid || downloader.active
Rectangle {
color: MoneroComponents.Style.buttonBackgroundColor
height: parent.height
width: parent.width * updateDialog.progress / 100
}
}
RowLayout {
Layout.alignment: Qt.AlignRight
spacing: parent.spacing
MoneroComponents.StandardButton {
id: cancelButton
fontBold: false
primary: !updateDialog.url
text: {
if (!updateDialog.url) {
return qsTr("Ok") + translationManager.emptyString;
}
if (updateDialog.valid || downloader.active || errorText.visible) {
return qsTr("Cancel") + translationManager.emptyString;
}
return qsTr("Download later") + translationManager.emptyString;
}
onClicked: {
downloader.cancel();
updateDialog.active = false;
}
}
MoneroComponents.StandardButton {
id: downloadButton
KeyNavigation.tab: cancelButton
fontBold: false
text: (updateDialog.error ? qsTr("Retry") : qsTr("Download")) + translationManager.emptyString
visible: updateDialog.url && !updateDialog.valid && !downloader.active
onClicked: {
updateDialog.error = "";
updateDialog.filename = updateDialog.url.replace(/^.*\//, '');
const downloadingStarted = downloader.get(updateDialog.url, updateDialog.hash, function(error) {
if (error) {
console.error("Download failed", error);
updateDialog.error = qsTr("Download failed") + translationManager.emptyString;
} else {
updateDialog.valid = true;
}
});
if (!downloadingStarted) {
updateDialog.error = qsTr("Failed to start download") + translationManager.emptyString;
}
}
}
MoneroComponents.StandardButton {
id: saveButton
KeyNavigation.tab: cancelButton
fontBold: false
onClicked: {
const fullPath = oshelper.openSaveFileDialog(
qsTr("Save as") + translationManager.emptyString,
oshelper.downloadLocation(),
updateDialog.filename);
if (!fullPath) {
return;
}
if (downloader.saveToFile(fullPath)) {
cancelButton.clicked();
oshelper.openContainingFolder(fullPath);
} else {
updateDialog.error = qsTr("Save operation failed") + translationManager.emptyString;
}
}
text: qsTr("Save to file") + translationManager.emptyString
visible: updateDialog.valid
}
}
}
Downloader {
id: downloader
}
}

View File

@@ -149,6 +149,7 @@ Object {
property string caretUp : "\uf0d8"
property string cartArrowDown : "\uf218"
property string cartPlus : "\uf217"
property string cashRegister: "\uf788"
property string cc : "\uf20a"
property string ccAmex : "\uf1f3"
property string ccDinersClub : "\uf24c"

View File

@@ -17,7 +17,7 @@ if [ ! -d $MONERO_DIR/src ]; then
fi
git submodule update --remote
git -C $MONERO_DIR fetch
git -C $MONERO_DIR checkout v0.15.0.1
git -C $MONERO_DIR checkout v0.16.0.3
# get monero core tag
pushd $MONERO_DIR

BIN
images/busy-indicator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 B

2
images/monero-vector.svg Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 6000 6000" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs><clipPath id="a"><path d="m0 4500h4500v-4500h-4500z"/></clipPath></defs><g transform="matrix(1.3333 0 0 -1.3333 0 6e3)"><g clip-path="url(#a)"><g transform="translate(4128 2250.2)"><path d="m0 0c0-1037.2-840.79-1878.1-1878.1-1878.1-1037.2 0-1878 840.88-1878 1878.1 0 1037.3 840.8 1878.1 1878 1878.1 1037.3 0 1878.1-840.79 1878.1-1878.1" fill="#fff"/></g><g transform="translate(2250 4128.2)"><path d="m0 0c-1036.9 0-1879.1-842.06-1877.8-1878 0.262-207.26 33.308-406.63 95.342-593.12h561.88v1579.9l1220.6-1220.6 1220.6 1220.6v-1579.9h561.96c62.117 186.48 95.008 385.85 95.369 593.12 1.809 1037-840.89 1877.8-1877.9 1877.8z" fill="#f36e36"/></g><g transform="translate(1969.3 1735.8)"><path d="m0 0-532.67 532.7v-994.14h-407.26l-384.29-0.07c329.63-540.8 925.35-902.56 1604.9-902.56 679.54 0 1275.3 361.85 1605 902.65l-384.44-0.013h-407.27v994.14l-813.3-813.31-280.62 280.61z" fill="#575757"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

View File

@@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="18" viewBox="0 0 10 18">
<g fill="none" fill-rule="evenodd" opacity="1">
<path fill="none" d="M-13-9h36v36h-36z" opacity="1"/>
<g fill="#000" fill-rule="nonzero">
<path d="M5 0C3.75 0 2.571.468 1.643 1.296A5.057 5.057 0 0 0 0 5.04c0 .396.321.72.714.72h.715a.72.72 0 0 0 .714-.72c0-.828.357-1.584.964-2.16A2.823 2.823 0 0 1 5 2.16c.107 0 .214 0 .321.036 1.322.144 2.358 1.224 2.5 2.52.143 1.188-.464 2.304-1.5 2.88-1.5.792-2.428 2.304-2.428 3.96v2.124c0 .396.321.72.714.72h.714a.72.72 0 0 0 .715-.72v-2.124c0-.828.5-1.62 1.285-2.052A4.98 4.98 0 0 0 9.93 4.5C9.714 2.16 7.857.288 5.57.036 5.393 0 5.18 0 5 0zM5.714 18H4.286a.358.358 0 0 1-.357-.36V16.2c0-.2.16-.36.357-.36h1.428c.198 0 .357.16.357.36v1.44c0 .2-.16.36-.357.36z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 848 B

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014-2019, The Monero Project
Copyright (c) 2014-2020, The Monero Project
All rights reserved.

View File

@@ -1,5 +1,5 @@
; Monero Carbon Chamaeleon GUI Wallet Installer for Windows
; Copyright (c) 2017-2019, The Monero Project
; Monero Nitrogen Nebula GUI Wallet Installer for Windows
; Copyright (c) 2017-2020, The Monero Project
; See LICENSE
#define GuiVersion GetFileVersion("bin\monero-wallet-gui.exe")
@@ -11,7 +11,7 @@ AppName=Monero GUI Wallet
AppVersion={#GuiVersion}
VersionInfoVersion={#GuiVersion}
DefaultDirName={pf}\Monero GUI Wallet
DefaultDirName={commonpf}\Monero GUI Wallet
DefaultGroupName=Monero GUI Wallet
UninstallDisplayIcon={app}\monero-wallet-gui.exe
PrivilegesRequired=admin
@@ -62,7 +62,6 @@ Name: "en"; MessagesFile: "compiler:Default.isl"
; .exe/.dll file possibly with version info).
;
; This is far more robust than relying on version info or on file dates (flag "comparetimestamp").
; As of version 0.15.0.0, the Monero .exe files do not carry version info anyway in their .exe headers.
; The only small drawback seems to be somewhat longer update times because each and every file is
; copied again, even if already present with correct file date and identical content.
;
@@ -71,17 +70,18 @@ Name: "en"; MessagesFile: "compiler:Default.isl"
Source: {#file AddBackslash(SourcePath) + "ReadMe.htm"}; DestDir: "{app}"; DestName: "ReadMe.htm"; Flags: ignoreversion
Source: "FinishImage.bmp"; Flags: dontcopy
Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion
; Monero GUI wallet exe and guide
Source: "bin\monero-wallet-gui.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-gui-wallet-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion
; Monero CLI wallet
Source: "bin\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-gen-trusted-multisig.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-gen-trusted-multisig.exe"; DestDir: "{app}"; Flags: ignoreversion
; Monero wallet RPC interface implementation
Source: "bin\monero-wallet-rpc.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-wallet-rpc.exe"; DestDir: "{app}"; Flags: ignoreversion
; Monero daemon
Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
@@ -90,16 +90,17 @@ Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "monero-daemon.bat"; DestDir: "{app}"; Flags: ignoreversion;
; Monero blockchain utilities
Source: "bin\monero-blockchain-export.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-mark-spent-outputs.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-usage.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-ancestry.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-depth.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-prune-known-spent-data.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-prune.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\monero-blockchain-stats.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-export.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-mark-spent-outputs.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-usage.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-ancestry.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-depth.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-prune-known-spent-data.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-prune.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-blockchain-stats.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "bin\extras\monero-gen-ssl-cert.exe"; DestDir: "{app}"; Flags: ignoreversion
; Qt Quick 2D Renderer fallback for systems / environments with "low-level graphics" i.e. without 3D support
Source: "bin\start-low-graphics-mode.bat"; DestDir: "{app}"; Flags: ignoreversion
@@ -202,7 +203,7 @@ begin
// Additional wizard page for entering a special blockchain location
blockChainDefaultDir := ExpandConstant('{commonappdata}\bitmonero');
s := 'The default folder to store the Monero blockchain is ' + blockChainDefaultDir;
s := s + '. As this will need more than 74 GB of free space, you may want to use a folder on a different drive.';
s := s + '. As this will need more than 90 GB of free space, you may want to use a folder on a different drive.';
s := s + ' If yes, specify that folder here.';
BlockChainDirPage := CreateInputDirPage(wpSelectDir,

View File

@@ -1,12 +1,12 @@
# Monero GUI Wallet Windows Installer #
Copyright (c) 2017-2019, The Monero Project
Copyright (c) 2017-2020, The Monero Project
## Introduction ##
This is a *Inno Setup* script `Monero.iss` plus some related files
that allows you to build a standalone Windows installer (.exe) for
the GUI wallet that comes with the Carbon Chamaeleon release of Monero.
the GUI wallet that comes with the Nitrogen Nebula release of Monero.
This turns the GUI wallet into a more or less standard Windows program,
by default installed into a subdirectory of `C:\Program Files`, a
@@ -18,7 +18,7 @@ Monero.
As the setup script in file [Monero.iss](Monero.iss) has to list many
files and directories of the GUI wallet package to install by name,
this version of the script only works with exactly the GUI wallet
for Monero release *Carbon Chamaeleon* that you find on
for Monero release *Nitrogen Nebula* that you find on
[the official download page](https://getmonero.org/downloads/).
It should however be easy to modify the script for future
@@ -32,15 +32,15 @@ See [LICENSE](LICENSE).
You can only build on Windows, and the result is always a
Windows .exe file that can act as a standalone installer for the
Carbon Chamaeleon GUI wallet.
Nitrogen Nebula GUI wallet.
Note that the installer build process is now reproducible / deterministic. For details check the file [Deterministic.md](Deterministic.md).
The build steps in detail:
1. Install *Inno Setup*. You can get it from [here](http://www.jrsoftware.org/isdl.php)
2. Get the Inno Setup script plus related files by cloning the whole [monero-gui GitHub repository](https://github.com/monero-project/monero-gui); you will only need the files in the installer directory `installers\windows` however. Depending on development state, additionally instead of simply using `master` you may have to checkout a specific branch, like `release-v0.15`.
3. The setup script is written to take the GUI wallet files from a subdirectory named `bin`; so create `installers\windows\bin`, get the zip file of the GUI wallet from [here](https://getmonero.org/downloads/), unpack it somewhere, and copy all the files and subdirectories in the single subdirectory there (currently named `monero-gui-0.15.0.0`) to this `bin` subdirectory
2. Get the Inno Setup script plus related files by cloning the whole [monero-gui GitHub repository](https://github.com/monero-project/monero-gui); you will only need the files in the installer directory `installers\windows` however. Depending on development state, additionally instead of simply using `master` you may have to checkout a specific branch, like `release-v0.16`.
3. The setup script is written to take the GUI wallet files from a subdirectory named `bin`; so create `installers\windows\bin`, get the zip file of the GUI wallet from [here](https://getmonero.org/downloads/), unpack it somewhere, and copy all the files and subdirectories in the single subdirectory there (currently named `monero-gui-0.16.0.0`) to this `bin` subdirectory
4. Start Inno Setup, load `Monero.iss` and compile it
5. The result i.e. the finished installer will be the file `mysetup.exe` in the `installers\windows\Output` subdirectory

View File

@@ -1,12 +1,12 @@
<html>
<head>
<title>Monero Carbon Chamaeleon GUI Wallet</title>
<title>Monero Nitrogen Nebula GUI Wallet</title>
</head>
<body style="font-family: Arial, Helvetica, sans-serif">
<h1>Monero Carbon Chamaeleon GUI Wallet</h1>
<h1>Monero Nitrogen Nebula GUI Wallet</h1>
<p>Copyright (c) 2014-2019, The Monero Project</p>
<p>Copyright (c) 2014-2020, The Monero Project</p>
<h2>Preface</h2>
@@ -22,7 +22,7 @@
<h2>Content of the Package</h2>
<p>You just installed the <i>Monero GUI wallet</i> for Windows, release Carbon Chamaeleon, version {#GuiVersion}.
<p>You just installed the <i>Monero GUI wallet</i> for Windows, release Nitrogen Nebula, version {#GuiVersion}.
The wallet enables you to send and receive Moneroj in a secure and very private way.
</p>
@@ -60,7 +60,7 @@
provides the most security and privacy possible for you.</p>
<p>However if your Internet access makes it difficult to run a full node, or if you have simply no room to store
the blockchain locally (somewhat over 74 GB in November 2019, and of course growing), you can compromise and try to connect
the blockchain locally (about 90 GB in May 2020, and of course growing), you can compromise and try to connect
to a remote node. One way of finding such a node is checking
<a href="https://moneroworld.com/#nodes">this page</a>.
</p>
@@ -104,7 +104,7 @@
<p>The Monero software and especially the GUI wallet are "work in progress", and sometimes things go wrong.</p>
<p>Please note that despite any technical problems that you may encounter your moneroj are almost always safe: You may
<p>Please note that despite any technical problems that you may encounter your Moneroj are almost always safe: You may
not be able to move them or you even may not see how many you currently have, but you most probably won't loose any.
But do remember that the seed needed to re-create the wallet <b>is</b> critical, however: <b>Never loose your
seed!</b></p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 440 KiB

After

Width:  |  Height:  |  Size: 440 KiB

View File

@@ -55,36 +55,14 @@ function ago(epoch) {
var now = new Date().getTime() / 1000;
var delta = now - epoch;
if(delta < 60) {
if (delta <= 1) {
return 1 + " " + qsTr("second ago")
} else {
return Math.floor(delta) + " " + qsTr("seconds ago")
}
} else if (delta >= 60 && delta <= 3600) {
if(delta >= 60 && delta < 120){
return 1 + " " + qsTr("minute ago")
} else {
return parseInt(Math.floor(delta / 60)) + " " + qsTr("minutes ago")
}
} else if (delta >= 3600 && delta <= 86400) {
if(delta >= 3600 && delta < 7200) {
return 1 + " " + qsTr("hour ago")
} else {
return parseInt(Math.floor(delta / 60 / 60)) + " " + qsTr("hours ago")
}
} else if (delta >= 86400){
if(delta >= 86400 && delta < 172800) {
return 1 + " " + qsTr("day ago")
} else {
var _delta = parseInt(Math.floor(delta / 24 / 60 / 60));
if(_delta === 1) {
return 1 + " " + qsTr("day ago")
} else {
return _delta + " " + qsTr("days ago")
}
}
}
if(delta < 60)
return qsTr("%n second(s) ago", "0", Math.floor(delta))
else if (delta >= 60 && delta <= 3600)
return qsTr("%n minute(s) ago", "0", Math.floor(delta / 60))
else if (delta >= 3600 && delta <= 86400)
return qsTr("%n hour(s) ago", "0", Math.floor(delta / 60 / 60))
else if (delta >= 86400)
return qsTr("%n day(s) ago", "0", Math.floor(delta / 24 / 60 / 60))
}
function netTypeToString(){
@@ -133,3 +111,7 @@ function capitalize(s){
if (typeof s !== 'string') return ''
return s.charAt(0).toUpperCase() + s.slice(1)
}
function removeTrailingZeros(value) {
return (value + '').replace(/(\.\d*[1-9])0+$/, '$1');
}

View File

@@ -58,11 +58,6 @@ function switchPage(next) {
}
function createWalletPath(isIOS, folder_path,account_name){
// Remove trailing slash - (default on windows and mac)
if (folder_path.substring(folder_path.length -1) === "/"){
folder_path = folder_path.substring(0,folder_path.length -1)
}
// Store releative path on ios.
if(isIOS)
folder_path = "";
@@ -102,10 +97,6 @@ function tr(text) {
return qsTr(text) + translationManager.emptyString
}
function lineBreaksToSpaces(text) {
return text.trim().replace(/(\r\n|\n|\r)/gm, " ");
}
function usefulName(path) {
// arbitrary "short enough" limit
if (path.length < 32)
@@ -115,7 +106,7 @@ function usefulName(path) {
function checkSeed(seed) {
console.log("Checking seed")
var wordsArray = lineBreaksToSpaces(seed).split(" ");
var wordsArray = seed.split(/\s+/);
return wordsArray.length === 25 || wordsArray.length === 24
}

323
main.qml
View File

@@ -34,6 +34,7 @@ import QtQuick.Dialogs 1.2
import QtGraphicalEffects 1.0
import moneroComponents.Wallet 1.0
import moneroComponents.WalletManager 1.0
import moneroComponents.PendingTransaction 1.0
import moneroComponents.NetworkType 1.0
import moneroComponents.Settings 1.0
@@ -45,6 +46,7 @@ import "pages/merchant" as MoneroMerchant
import "wizard"
import "js/Utils.js" as Utils
import "js/Windows.js" as Windows
import "version.js" as Version
ApplicationWindow {
id: appWindow
@@ -66,12 +68,14 @@ ApplicationWindow {
property bool walletSynced: false
property int maxWindowHeight: (isAndroid || isIOS)? screenHeight : (screenHeight < 900)? 720 : 800;
property bool daemonRunning: !persistentSettings.useRemoteNode && !disconnected
property bool daemonStartStopInProgress: false
property alias toolTip: toolTip
property string walletName
property bool viewOnly: false
property bool foundNewBlock: false
property bool qrScannerEnabled: (typeof builtWithScanner != "undefined") && builtWithScanner
property int blocksToSync: 1
property int firstBlockSeen
property bool isMining: false
property int walletMode: persistentSettings.walletMode
property var cameraUi
@@ -214,7 +218,7 @@ ApplicationWindow {
appWindow.viewState = prevState;
}
};
passwordDialog.open(usefulName(walletPath()));
passwordDialog.open(usefulName(persistentSettings.wallet_path));
}
function initialize() {
@@ -250,10 +254,9 @@ ApplicationWindow {
// enable timers
userInActivityTimer.running = true;
simpleModeConnectionTimer.running = true;
// wallet already opened with wizard, we just need to initialize it
var wallet_path = walletPath();
var wallet_path = persistentSettings.wallet_path;
if(isIOS)
wallet_path = moneroAccountsDir + wallet_path;
// console.log("opening wallet at: ", wallet_path, "with password: ", appWindow.walletPassword);
@@ -291,6 +294,7 @@ ApplicationWindow {
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
currentWallet.deviceButtonRequest.disconnect(onDeviceButtonRequest);
currentWallet.deviceButtonPressed.disconnect(onDeviceButtonPressed);
currentWallet.walletPassphraseNeeded.disconnect(onWalletPassphraseNeededWallet);
currentWallet.transactionCommitted.disconnect(onTransactionCommitted);
middlePanel.paymentClicked.disconnect(handlePayment);
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
@@ -358,6 +362,7 @@ ApplicationWindow {
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
currentWallet.walletPassphraseNeeded.connect(onWalletPassphraseNeededWallet);
currentWallet.transactionCommitted.connect(onTransactionCommitted);
middlePanel.paymentClicked.connect(handlePayment);
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
@@ -392,11 +397,6 @@ ApplicationWindow {
return !persistentSettings.useRemoteNode || persistentSettings.is_trusted_daemon;
}
function walletPath() {
var wallet_path = persistentSettings.wallet_path
return wallet_path;
}
function usefulName(path) {
// arbitrary "short enough" limit
if (path.length < 32)
@@ -474,9 +474,9 @@ ApplicationWindow {
console.log("Wallet connection status changed " + status)
middlePanel.updateStatus();
leftPanel.networkStatus.connected = status
// Update fee multiplier dropdown on transfer page
middlePanel.transferView.updatePriorityDropdown();
if (status == Wallet.ConnectionStatus_Disconnected) {
firstBlockSeen = 0;
}
// If wallet isnt connected, advanced wallet mode and no daemon is running - Ask
if (appWindow.walletMode >= 2 && !persistentSettings.useRemoteNode && !walletInitialized && disconnected) {
@@ -558,19 +558,32 @@ ApplicationWindow {
}
}
function onWalletPassphraseNeeded(){
function onWalletPassphraseNeededManager(on_device){
onWalletPassphraseNeeded(walletManager, on_device)
}
function onWalletPassphraseNeededWallet(on_device){
onWalletPassphraseNeeded(currentWallet, on_device)
}
function onWalletPassphraseNeeded(handler, on_device){
hideProcessingSplash();
console.log(">>> wallet passphrase needed: ")
passwordDialog.onAcceptedPassphraseCallback = function() {
walletManager.onPassphraseEntered(passwordDialog.password);
devicePassphraseDialog.onAcceptedCallback = function(passphrase) {
handler.onPassphraseEntered(passphrase, false, false);
appWindow.onWalletOpening();
}
passwordDialog.onRejectedPassphraseCallback = function() {
walletManager.onPassphraseEntered("", true);
devicePassphraseDialog.onWalletEntryCallback = function() {
handler.onPassphraseEntered("", true, false);
appWindow.onWalletOpening();
}
passwordDialog.openPassphraseDialog()
devicePassphraseDialog.onRejectedCallback = function() {
handler.onPassphraseEntered("", false, true);
appWindow.onWalletOpening();
}
devicePassphraseDialog.open(on_device)
}
function onWalletUpdate() {
@@ -613,18 +626,22 @@ ApplicationWindow {
currentDaemonAddress = localDaemonAddress
currentWallet.initAsync(currentDaemonAddress, isTrustedDaemon());
walletManager.setDaemonAddressAsync(currentDaemonAddress);
firstBlockSeen = 0;
}
function onHeightRefreshed(bcHeight, dCurrentBlock, dTargetBlock) {
// Daemon fully synced
// TODO: implement onDaemonSynced or similar in wallet API and don't start refresh thread before daemon is synced
// targetBlock = currentBlock = 1 before network connection is established.
if (firstBlockSeen == 0 && dTargetBlock != 1) {
firstBlockSeen = dCurrentBlock;
}
daemonSynced = dCurrentBlock >= dTargetBlock && dTargetBlock != 1
walletSynced = bcHeight >= dTargetBlock
// Update progress bars
if(!daemonSynced) {
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, dTargetBlock-dCurrentBlock);
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, dTargetBlock-firstBlockSeen);
leftPanel.progressBar.updateProgress(0,dTargetBlock, dTargetBlock, qsTr("Waiting for daemon to sync"));
} else {
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, 0, qsTr("Daemon is synchronized (%1)").arg(dCurrentBlock.toFixed(0)));
@@ -665,12 +682,11 @@ ApplicationWindow {
}
function startDaemon(flags){
daemonStartStopInProgress = true;
// Pause refresh while starting daemon
currentWallet.pauseRefresh();
// Pause simplemode connection timer
simpleModeConnectionTimer.stop();
appWindow.showProcessingSplash(qsTr("Waiting for daemon to start..."))
const noSync = appWindow.walletMode === 0;
const bootstrapNodeAddress = persistentSettings.walletMode < 2 ? "auto" : persistentSettings.bootstrapNodeAddress
@@ -678,8 +694,10 @@ ApplicationWindow {
}
function stopDaemon(callback){
daemonStartStopInProgress = true;
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."))
daemonManager.stopAsync(persistentSettings.nettype, function(result) {
daemonStartStopInProgress = false;
hideProcessingSplash();
callback(result);
});
@@ -687,13 +705,13 @@ ApplicationWindow {
function onDaemonStarted(){
console.log("daemon started");
daemonStartStopInProgress = false;
hideProcessingSplash();
currentWallet.connected(true);
// resume refresh
currentWallet.startRefresh();
// resume simplemode connection timer
appWindow.disconnectedEpoch = Utils.epoch();
simpleModeConnectionTimer.start();
}
function onDaemonStopped(){
currentWallet.connected(true);
@@ -701,6 +719,7 @@ ApplicationWindow {
function onDaemonStartFailure(error) {
console.log("daemon start failed");
daemonStartStopInProgress = false;
hideProcessingSplash();
// resume refresh
currentWallet.startRefresh();
@@ -976,7 +995,11 @@ ApplicationWindow {
informationPopup.open()
currentWallet.refresh()
currentWallet.disposeTransaction(transaction)
currentWallet.store();
currentWallet.storeAsync(function(success) {
if (!success) {
appWindow.showStatusMessage(qsTr("Failed to store the wallet"), 3);
}
});
}
// called on "getProof"
@@ -1076,7 +1099,6 @@ ApplicationWindow {
console.log("Displaying processing splash")
if (typeof message != 'undefined') {
splash.messageText = message
splash.heightProgressText = ""
}
leftPanel.enabled = false;
@@ -1103,14 +1125,15 @@ ApplicationWindow {
wizard.restart();
wizard.wizardState = "wizardHome";
rootItem.state = "wizard"
// reset balance
// reset balance, clear spendable funds message
clearMoneroCardLabelText();
leftPanel.minutesToUnlock = "";
// reset fields
middlePanel.addressBookView.clearFields();
middlePanel.transferView.clearFields();
middlePanel.receiveView.clearFields();
// disable timers
userInActivityTimer.running = false;
simpleModeConnectionTimer.running = false;
});
}
@@ -1135,9 +1158,9 @@ ApplicationWindow {
triggeredOnStart: false
}
function fiatApiParseTicker(resp, currency){
function fiatApiParseTicker(url, resp, currency){
// parse & validate incoming JSON
if(resp._url.startsWith("https://api.kraken.com/0/")){
if(url.startsWith("https://api.kraken.com/0/")){
if(resp.hasOwnProperty("error") && resp.error.length > 0 || !resp.hasOwnProperty("result")){
appWindow.fiatApiError("Kraken API has error(s)");
return;
@@ -1146,14 +1169,14 @@ ApplicationWindow {
var key = currency === "xmreur" ? "XXMRZEUR" : "XXMRZUSD";
var ticker = resp.result[key]["o"];
return ticker;
} else if(resp._url.startsWith("https://api.coingecko.com/api/v3/")){
} else if(url.startsWith("https://api.coingecko.com/api/v3/")){
var key = currency === "xmreur" ? "eur" : "usd";
if(!resp.hasOwnProperty("monero") || !resp["monero"].hasOwnProperty(key)){
appWindow.fiatApiError("Coingecko API has error(s)");
return;
}
return resp["monero"][key];
} else if(resp._url.startsWith("https://min-api.cryptocompare.com/data/")){
} else if(url.startsWith("https://min-api.cryptocompare.com/data/")){
var key = currency === "xmreur" ? "EUR" : "USD";
if(!resp.hasOwnProperty(key)){
appWindow.fiatApiError("cryptocompare API has error(s)");
@@ -1163,13 +1186,7 @@ ApplicationWindow {
}
}
function fiatApiGetCurrency(resp){
// map response to `appWindow.fiatPriceAPIs` object
if (!resp.hasOwnProperty('_url')){
appWindow.fiatApiError("invalid JSON");
return;
}
function fiatApiGetCurrency(url) {
var apis = appWindow.fiatPriceAPIs;
for (var api in apis){
if (!apis.hasOwnProperty(api))
@@ -1179,23 +1196,34 @@ ApplicationWindow {
if(!apis[api].hasOwnProperty(cur))
continue;
var url = apis[api][cur];
if(url === resp._url){
if (apis[api][cur] === url) {
return cur;
}
}
}
}
function fiatApiJsonReceived(resp){
function fiatApiJsonReceived(url, resp, error) {
if (error) {
appWindow.fiatApiError(error);
return;
}
try {
resp = JSON.parse(resp);
} catch (e) {
appWindow.fiatApiError("bad JSON: " + e);
return;
}
// handle incoming JSON, set ticker
var currency = appWindow.fiatApiGetCurrency(resp);
var currency = appWindow.fiatApiGetCurrency(url);
if(typeof currency == "undefined"){
appWindow.fiatApiError("could not get currency");
return;
}
var ticker = appWindow.fiatApiParseTicker(resp, currency);
var ticker = appWindow.fiatApiParseTicker(url, resp, currency);
if(ticker <= 0){
appWindow.fiatApiError("could not get ticker");
return;
@@ -1227,7 +1255,7 @@ ApplicationWindow {
}
var url = provider[userCurrency];
Prices.getJSON(url);
Network.getJSON(url, fiatApiJsonReceived);
}
function fiatApiCurrencySymbol() {
@@ -1283,9 +1311,8 @@ ApplicationWindow {
walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeededManager);
IPC.uriHandler.connect(onUriHandler);
Prices.priceJsonReceived.connect(appWindow.fiatApiJsonReceived);
if(typeof daemonManager != "undefined") {
daemonManager.daemonStarted.connect(onDaemonStarted);
@@ -1317,8 +1344,6 @@ ApplicationWindow {
openWallet("wizard");
}
checkUpdates();
if(persistentSettings.fiatPriceEnabled){
appWindow.fiatApiRefresh();
appWindow.fiatTimerStart();
@@ -1363,10 +1388,14 @@ ApplicationWindow {
property int segregationHeight: 0
property int kdfRounds: 1
property bool hideBalance: false
property bool askPasswordBeforeSending: true
property bool lockOnUserInActivity: true
property int walletMode: 2
property int lockOnUserInActivityInterval: 10 // minutes
property bool blackTheme: true
property bool checkForUpdates: true
property bool autosave: true
property int autosaveMinutes: 10
property bool fiatPriceEnabled: false
property bool fiatPriceToggle: false
@@ -1398,21 +1427,28 @@ ApplicationWindow {
z: parent.z + 1
id: transactionConfirmationPopup
onAccepted: {
var handleAccepted = function() {
// Save transaction to file if view only wallet
if (viewOnly) {
saveTxDialog.open();
} else {
handleTransactionConfirmed()
}
}
close();
passwordDialog.onAcceptedCallback = function() {
if(walletPassword === passwordDialog.password){
// Save transaction to file if view only wallet
if(viewOnly) {
saveTxDialog.open();
} else {
handleTransactionConfirmed()
}
handleAccepted()
} else {
passwordDialog.showError(qsTr("Wrong password") + translationManager.emptyString);
}
}
passwordDialog.onRejectedCallback = null;
passwordDialog.open()
if(!persistentSettings.askPasswordBeforeSending) {
handleAccepted()
} else {
passwordDialog.open()
}
}
}
@@ -1432,6 +1468,14 @@ ApplicationWindow {
}
}
MoneroComponents.UpdateDialog {
id: updateDialog
allowed: !passwordDialog.visible && !inputDialog.visible && !splash.visible
x: (parent.width - width) / 2
y: (parent.height - height) / 2
}
// Choose blockchain folder
FileDialog {
id: blockchainFileDialog
@@ -1461,7 +1505,6 @@ ApplicationWindow {
confirmationDialog.text += qsTr("Note: lmdb folder not found. A new folder will be created.") + "\n\n"
confirmationDialog.icon = StandardIcon.Question
confirmationDialog.cancelText = qsTr("Cancel")
// Continue
confirmationDialog.onAcceptedCallback = function() {
@@ -1481,12 +1524,10 @@ ApplicationWindow {
PasswordDialog {
id: passwordDialog
visible: false
z: parent.z + 1
z: parent.z + 2
anchors.fill: parent
property var onAcceptedCallback
property var onRejectedCallback
property var onAcceptedPassphraseCallback
property var onRejectedPassphraseCallback
onAccepted: {
if (onAcceptedCallback)
onAcceptedCallback();
@@ -1510,14 +1551,13 @@ ApplicationWindow {
informationPopup.open();
}
onRejectedNewPassword: {}
onAcceptedPassphrase: {
if (onAcceptedPassphraseCallback)
onAcceptedPassphraseCallback();
}
onRejectedPassphrase: {
if (onRejectedPassphraseCallback)
onRejectedPassphraseCallback();
}
}
DevicePassphraseDialog {
id: devicePassphraseDialog
visible: false
z: parent.z + 1
anchors.fill: parent
}
InputDialog {
@@ -1548,8 +1588,8 @@ ApplicationWindow {
ProcessingSplash {
id: splash
width: appWindow.width / 1.5
height: appWindow.height / 2
width: appWindow.width / 2
height: appWindow.height / 2.66
x: (appWindow.width - width) / 2
y: (appWindow.height - height) / 2
messageText: qsTr("Please wait...") + translationManager.emptyString
@@ -1607,12 +1647,6 @@ ApplicationWindow {
updateBalance();
}
onMerchantClicked: {
middlePanel.state = "Merchant";
middlePanel.flickable.contentY = 0;
updateBalance();
}
onTxkeyClicked: {
middlePanel.state = "TxKey";
middlePanel.flickable.contentY = 0;
@@ -1687,16 +1721,10 @@ ApplicationWindow {
anchors.fill: blurredArea
source: blurredArea
radius: 64
visible: passwordDialog.visible || inputDialog.visible || splash.visible
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible
}
WizardLang {
id: languageView
visible: false
anchors.fill: parent
}
property int minWidth: 326
property int minHeight: 400
MouseArea {
@@ -1794,16 +1822,10 @@ ApplicationWindow {
color: "#FFFFFF"
}
}
Notifier {
visible:false
id: notifier
}
}
function toggleLanguageView(){
middlePanel.visible = !middlePanel.visible;
languageView.visible = !languageView.visible
languageSidebar.isOpened ? languageSidebar.close() : languageSidebar.open();
resetLanguageFields()
// update after changing language from settings page
if (persistentSettings.language != wizard.language_language) {
@@ -1812,6 +1834,24 @@ ApplicationWindow {
}
}
Timer {
id: autosaveTimer
interval: persistentSettings.autosaveMinutes * 60 * 1000
repeat: true
running: persistentSettings.autosave
onTriggered: {
if (currentWallet) {
currentWallet.storeAsync(function(success) {
if (success) {
appWindow.showStatusMessage(qsTr("Autosaved the wallet"), 3);
} else {
appWindow.showStatusMessage(qsTr("Failed to autosave the wallet"), 3);
}
});
}
}
}
// TODO: Make the callback dynamic
Timer {
id: statusMessageTimer
@@ -1851,9 +1891,6 @@ ApplicationWindow {
}
function checkSimpleModeConnection(){
// auto-connection mechanism for simple mode
if(appWindow.walletMode >= 2) return;
const disconnectedTimeoutSec = 30;
const firstCheckDelaySec = 2;
@@ -1870,15 +1907,20 @@ ApplicationWindow {
}
if (appWindow.daemonRunning) {
appWindow.stopDaemon();
appWindow.stopDaemon(function() {
appWindow.startDaemon("")
});
} else {
appWindow.startDaemon("");
}
appWindow.startDaemon("");
}
Timer {
// Simple mode connection check timer
id: simpleModeConnectionTimer
interval: 2000; running: false; repeat: true
interval: 2000
running: appWindow.walletMode < 2 && currentWallet != undefined && !daemonStartStopInProgress
repeat: true
onTriggered: appWindow.checkSimpleModeConnection()
}
@@ -1952,14 +1994,26 @@ ApplicationWindow {
}
// If daemon is running - prompt user before exiting
if(typeof daemonManager != "undefined" && daemonRunning) {
if (appWindow.walletMode == 0) {
stopDaemon(closeAccepted);
} else {
showDaemonIsRunningDialog(closeAccepted);
}
} else {
if(daemonManager == undefined || persistentSettings.useRemoteNode) {
closeAccepted();
} else if (appWindow.walletMode == 0) {
stopDaemon(closeAccepted);
} else {
showProcessingSplash(qsTr("Checking local node status..."));
const handler = function(running) {
hideProcessingSplash();
if (running) {
showDaemonIsRunningDialog(closeAccepted);
} else {
closeAccepted();
}
};
if (currentWallet) {
handler(!currentWallet.disconnected);
} else {
daemonManager.runningAsync(persistentSettings.nettype, handler);
}
}
}
@@ -1971,34 +2025,42 @@ ApplicationWindow {
closeWallet(Qt.quit);
}
function onWalletCheckUpdatesComplete(update) {
if (update === "")
return
print("Update found: " + update)
var parts = update.split("|")
if (parts.length == 4) {
var version = parts[0]
var hash = parts[1]
var user_url = parts[2]
var msg = qsTr("New version of Monero v%1 is available.").arg(version)
if (isMac || isWindows || isLinux) {
msg += "<br><br>%1:<br>%2<br><br>%3:<br>%4".arg(qsTr("Download")).arg(user_url).arg(qsTr("SHA256 Hash")).arg(hash) + translationManager.emptyString
} else {
msg += " " + qsTr("Check out getmonero.org") + translationManager.emptyString
}
notifier.show(msg)
} else {
print("Failed to parse update spec")
function onWalletCheckUpdatesComplete(version, downloadUrl, hash, firstSigner, secondSigner) {
const alreadyAsked = updateDialog.url == downloadUrl && updateDialog.hash == hash;
if (!alreadyAsked)
{
updateDialog.show(version, isMac || isWindows || isLinux ? downloadUrl : "", hash);
}
}
function getBuildTag() {
if (isMac) {
return "mac-x64";
}
if (isWindows) {
return oshelper.installed ? "install-win-x64" : "win-x64";
}
if (isLinux) {
return "linux-x64";
}
return "source";
}
function checkUpdates() {
walletManager.checkUpdatesAsync("monero-gui", "gui")
const version = Version.GUI_VERSION.match(/\d+\.\d+\.\d+\.\d+/);
if (version) {
walletManager.checkUpdatesAsync("monero-gui", "gui", getBuildTag(), version[0]);
} else {
console.error("failed to parse version number", Version.GUI_VERSION);
}
}
Timer {
id: updatesTimer
interval: 3600*1000; running: true; repeat: true
interval: 3600 * 1000
repeat: true
running: !disableCheckUpdatesFlag && persistentSettings.checkForUpdates
triggeredOnStart: true
onTriggered: checkUpdates()
}
@@ -2078,7 +2140,7 @@ ApplicationWindow {
if (mode < 2) {
persistentSettings.useRemoteNode = false;
if (middlePanel.settingsView.settingsStateViewState === "Node" || middlePanel.settingsView.settingsStateViewState === "Log") {
if (middlePanel.settingsView.settingsStateViewState === "Node") {
middlePanel.settingsView.settingsStateViewState = "Wallet"
}
}
@@ -2098,6 +2160,11 @@ ApplicationWindow {
blackColor: "black"
whiteColor: "white"
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
}
}
// borders on white theme + linux
@@ -2165,8 +2232,12 @@ ApplicationWindow {
}
}
// @TODO: QML type 'Drawer' has issues with buildbot; debug after Qt 5.9 migration
// MoneroComponents.LanguageSidebar {
// id: languageSidebar
// }
MoneroComponents.LanguageSidebar {
id: languageSidebar
dragMargin: 0
}
WalletManager {
id: walletManager
}
}

2
monero

Submodule monero updated: 581994b61c...7bd1ed03dd

View File

@@ -43,7 +43,9 @@ INCLUDEPATH += $$WALLET_ROOT/include \
$$PWD/src/libwalletqt \
$$PWD/src/QR-Code-generator \
$$PWD/src \
$$WALLET_ROOT/src
$$WALLET_ROOT/src \
$$WALLET_ROOT/external/easylogging++ \
$$WALLET_ROOT/contrib/epee/include
HEADERS += \
src/main/filter.h \
@@ -51,6 +53,7 @@ HEADERS += \
src/main/oscursor.h \
src/libwalletqt/WalletManager.h \
src/libwalletqt/Wallet.h \
src/libwalletqt/PassphraseHelper.h \
src/libwalletqt/PendingTransaction.h \
src/libwalletqt/TransactionHistory.h \
src/libwalletqt/TransactionInfo.h \
@@ -74,11 +77,12 @@ HEADERS += \
src/libwalletqt/UnsignedTransaction.h \
src/main/Logger.h \
src/main/MainApp.h \
src/qt/downloader.h \
src/qt/FutureScheduler.h \
src/qt/ipc.h \
src/qt/KeysFiles.h \
src/qt/network.h \
src/qt/utils.h \
src/qt/prices.h \
src/qt/macoshelper.h \
src/qt/MoneroSettings.h \
src/qt/TailsOS.h
@@ -88,12 +92,15 @@ SOURCES += src/main/main.cpp \
src/main/clipboardAdapter.cpp \
src/main/oscursor.cpp \
src/libwalletqt/WalletManager.cpp \
src/libwalletqt/WalletListenerImpl.cpp \
src/libwalletqt/Wallet.cpp \
src/libwalletqt/PassphraseHelper.cpp \
src/libwalletqt/PendingTransaction.cpp \
src/libwalletqt/TransactionHistory.cpp \
src/libwalletqt/TransactionInfo.cpp \
src/libwalletqt/QRCodeImageProvider.cpp \
src/main/oshelper.cpp \
src/openpgp/openpgp.cpp \
src/TranslationManager.cpp \
src/model/TransactionHistoryModel.cpp \
src/model/TransactionHistorySortFilterModel.cpp \
@@ -110,11 +117,13 @@ SOURCES += src/main/main.cpp \
src/libwalletqt/UnsignedTransaction.cpp \
src/main/Logger.cpp \
src/main/MainApp.cpp \
src/qt/downloader.cpp \
src/qt/FutureScheduler.cpp \
src/qt/ipc.cpp \
src/qt/KeysFiles.cpp \
src/qt/network.cpp \
src/qt/updater.cpp \
src/qt/utils.cpp \
src/qt/prices.cpp \
src/qt/MoneroSettings.cpp \
src/qt/TailsOS.cpp
@@ -155,6 +164,8 @@ ios:arm64 {
}
LIBS_COMMON = \
-lgcrypt \
-lgpg-error \
-lwallet_merged \
-llmdb \
-lepee \
@@ -176,8 +187,10 @@ android {
QMAKE_CXXFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Werror -Wformat -Wformat-security
QMAKE_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Werror -Wformat -Wformat-security
QMAKE_CXXFLAGS += -Werror -Wformat -Wformat-security
QMAKE_CFLAGS += -Werror -Wformat -Wformat-security
QMAKE_CXXFLAGS_RELEASE += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2
QMAKE_CFLAGS_RELEASE += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2
ios {
message("Host is IOS")
@@ -221,6 +234,14 @@ CONFIG(WITH_SCANNER) {
LIBS += -lzbarjni -liconv
} else {
LIBS += -lzbar
macx {
ZBAR_DIR = $$system(brew --prefix zbar, lines, EXIT_CODE)
equals(EXIT_CODE, 0) {
INCLUDEPATH += $$ZBAR_DIR/include
} else {
INCLUDEPATH += /usr/local/include
}
}
}
} else {
message("Skipping camera scanner because of Incompatible Qt Version !")
@@ -372,11 +393,39 @@ macx {
# LIBS+= -Wl,-Bstatic
# }
OPENSSL_LIBRARY_DIRS = $$system(brew --prefix openssl, lines, EXIT_CODE)
OPENSSL_DIR = $$system(brew --prefix openssl, lines, EXIT_CODE)
!equals(EXIT_CODE, 0) {
OPENSSL_DIR = /usr/local/ssl
}
OPENSSL_LIBRARY_DIR = $$OPENSSL_DIR/lib
INCLUDEPATH += $$OPENSSL_DIR/include
BOOST_DIR = $$system(brew --prefix boost, lines, EXIT_CODE)
equals(EXIT_CODE, 0) {
OPENSSL_LIBRARY_DIRS = $$OPENSSL_LIBRARY_DIRS/lib
INCLUDEPATH += $$BOOST_DIR/include
} else {
OPENSSL_LIBRARY_DIRS = /usr/local/ssl/lib
INCLUDEPATH += /usr/local/include
}
GCRYPT_DIR = $$system(brew --prefix libgcrypt, lines, EXIT_CODE)
equals(EXIT_CODE, 0) {
INCLUDEPATH += $$GCRYPT_DIR/include
} else {
INCLUDEPATH += /usr/local/include
}
GPGP_ERROR_DIR = $$system(brew --prefix libgpg-error, lines, EXIT_CODE)
equals(EXIT_CODE, 0) {
INCLUDEPATH += $$GPGP_ERROR_DIR/include
} else {
INCLUDEPATH += /usr/local/include
}
SODIUM_DIR = $$system(brew --prefix libsodium, lines, EXIT_CODE)
equals(EXIT_CODE, 0) {
INCLUDEPATH += $$SODIUM_DIR/include
} else {
INCLUDEPATH += /usr/local/include
}
QT += macextras
@@ -386,7 +435,7 @@ macx {
LIBS+= -Wl,-bind_at_load
LIBS+= \
-L/usr/local/lib \
-L$$OPENSSL_LIBRARY_DIRS \
-L$$OPENSSL_LIBRARY_DIR \
-L/usr/local/opt/boost/lib \
-lboost_serialization \
-lboost_thread-mt \
@@ -518,7 +567,7 @@ DISTFILES += \
VERSION = $$cat('version.js', lines)
VERSION = $$find(VERSION, 'GUI_VERSION')
VERSION_LONG = $$replace(VERSION, '.*\"v(.*)\"', '\1')
VERSION_LONG = $$replace(VERSION, '.*\"(.*)\"', '\1')
VERSION = $$replace(VERSION, '.*(\d+\.\d+\.\d+\.\d+).*', '\1')
# windows application icon

View File

@@ -186,7 +186,7 @@ Rectangle {
delegate: Rectangle {
id: tableItem2
height: subaddressAccountListRow.subaddressAccountListItemHeight
width: parent.width
width: parent ? parent.width : undefined
Layout.fillWidth: true
color: "transparent"

View File

@@ -79,13 +79,6 @@ Rectangle {
topPadding: 0
text: qsTr("Save your most used addresses here") + translationManager.emptyString
width: parent.width
// @TODO: Legacy. Remove after Qt 5.8.
// https://stackoverflow.com/questions/41990013
MouseArea {
anchors.fill: parent
enabled: false
}
}
Text {
@@ -99,13 +92,6 @@ Rectangle {
topPadding: 0
text: qsTr("This makes it easier to send or receive Monero and reduces errors when typing in addresses manually.") + translationManager.emptyString
width: parent.width
// @TODO: Legacy. Remove after Qt 5.8.
// https://stackoverflow.com/questions/41990013
MouseArea {
anchors.fill: parent
enabled: false
}
}
MoneroComponents.StandardButton {
@@ -325,8 +311,6 @@ Rectangle {
if (!parsed.error) {
addressLine.text = parsed.address;
descriptionLine.text = parsed.tx_description;
} else {
addressLine.text = clipboardText;
}
}

View File

@@ -569,8 +569,8 @@ Rectangle {
delegate: Rectangle {
id: delegate
property bool collapsed: root.txDataCollapsed.indexOf(hash) >= 0 ? true : false
anchors.left: parent.left
anchors.right: parent.right
anchors.left: parent ? parent.left : undefined
anchors.right: parent ? parent.right : undefined
height: {
if(!collapsed) return 60;
if(isout && delegate.address !== "") return 320;
@@ -1394,7 +1394,7 @@ Rectangle {
txs.push(item);
} else if(item.address !== "" && item.address.toLowerCase().startsWith(root.sortSearchString.toLowerCase())){
txs.push(item);
} else if(item.blockheight.toString().startsWith(root.sortSearchString)) {
} else if(typeof item.blockheight !== "undefined" && item.blockheight.toString().startsWith(root.sortSearchString)) {
txs.push(item);
} else if(item.tx_note.toLowerCase().indexOf(root.sortSearchString.toLowerCase()) !== -1) {
txs.push(item);
@@ -1511,7 +1511,7 @@ Rectangle {
"i": i,
"isout": isout,
"amount": Number(amount),
"displayAmount": displayAmount + " XMR",
"displayAmount": Utils.removeTrailingZeros(displayAmount.toFixed(12)) + " XMR",
"hash": hash,
"paymentId": paymentId,
"address": address,

View File

@@ -251,7 +251,19 @@ Rectangle {
function updateStatusText() {
if (appWindow.isMining) {
statusText.text = qsTr("Mining at %1 H/s").arg(walletManager.miningHashRate()) + translationManager.emptyString;
var userHashRate = walletManager.miningHashRate();
if (userHashRate === 0) {
statusText.text = qsTr("Mining temporarily suspended.") + translationManager.emptyString;
}
else {
var blockTime = 120;
var blocksPerDay = 86400 / blockTime;
var globalHashRate = walletManager.networkDifficulty() / blockTime;
var probabilityFindNextBlock = userHashRate / globalHashRate;
var probabilityFindBlockDay = 1 - Math.pow(1 - probabilityFindNextBlock, blocksPerDay);
var chanceFindBlockDay = Math.round(1 / probabilityFindBlockDay);
statusText.text = qsTr("Mining at %1 H/s. It gives you a 1 in %2 daily chance of finding a block.").arg(userHashRate).arg(chanceFindBlockDay) + translationManager.emptyString;
}
}
else {
statusText.text = qsTr("Not mining") + translationManager.emptyString;

View File

@@ -105,7 +105,7 @@ Rectangle {
delegate: Rectangle {
id: tableItem2
height: subaddressListRow.subaddressListItemHeight
width: parent.width
width: parent ? parent.width : undefined
Layout.fillWidth: true
color: "transparent"

View File

@@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.9
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import moneroComponents.Clipboard 1.0
@@ -38,6 +39,7 @@ import "../components"
import "../components" as MoneroComponents
import "." 1.0
import "../js/TxUtils.js" as TxUtils
import "../js/Utils.js" as Utils
Rectangle {
@@ -54,8 +56,8 @@ Rectangle {
property string sendButtonWarning: {
// Currently opened wallet is not view-only
if (appWindow.viewOnly) {
return qsTr("Wallet is view-only and sends are not possible. Unless key images are imported, " +
"the balance reflects only incoming but not outgoing transactions.") + translationManager.emptyString;
return qsTr("Wallet is view-only and sends are only possible by using offline transaction signing. " +
"Unless key images are imported, the balance reflects only incoming but not outgoing transactions.") + translationManager.emptyString;
}
// There are sufficient unlocked funds available
@@ -116,7 +118,6 @@ Rectangle {
amountLine.text = ""
setDescription("");
priorityDropdown.currentIndex = 0
updatePriorityDropdown()
}
// Information dialog
@@ -162,96 +163,6 @@ Rectangle {
}
}
GridLayout {
columns: appWindow.walletMode < 2 ? 1 : 2
Layout.fillWidth: true
columnSpacing: 32
ColumnLayout {
Layout.fillWidth: true
Layout.minimumWidth: 200
// Amount input
LineEdit {
id: amountLine
Layout.fillWidth: true
inlineIcon: true
labelText: "<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
%1 <a href='#'>(%2)</a>".arg(qsTr("Amount")).arg(qsTr("Change account"))
+ translationManager.emptyString
copyButton: !isNaN(amountLine.text) && persistentSettings.fiatPriceEnabled
copyButtonText: fiatApiCurrencySymbol() + " ~" + fiatApiConvertToFiat(amountLine.text)
copyButtonEnabled: false
onLabelLinkActivated: {
middlePanel.accountView.selectAndSend = true;
appWindow.showPageRequest("Account")
}
placeholderText: "0.00"
width: 100
fontBold: true
inlineButtonText: qsTr("All") + translationManager.emptyString
inlineButton.onClicked: amountLine.text = "(all)"
onTextChanged: {
const match = amountLine.text.match(/^0+(\d.*)/);
if (match) {
const cursorPosition = amountLine.cursorPosition;
amountLine.text = match[1];
amountLine.cursorPosition = Math.max(cursorPosition, 1) - 1;
} else if(amountLine.text.indexOf('.') === 0){
amountLine.text = '0' + amountLine.text;
if (amountLine.text.length > 2) {
amountLine.cursorPosition = 1;
}
}
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
}
validator: RegExpValidator {
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
}
}
}
ColumnLayout {
visible: appWindow.walletMode >= 2
Layout.fillWidth: true
Label {
id: transactionPriority
Layout.topMargin: 12
text: qsTr("Transaction priority") + translationManager.emptyString
fontBold: false
fontSize: 16
}
// Note: workaround for translations in listElements
// ListElement: cannot use script for property value, so
// code like this wont work:
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
// For translations to work, the strings need to be listed in
// the file components/StandardDropdown.qml too.
// Priorites after v5
ListModel {
id: priorityModelV5
ListElement { column1: qsTr("Automatic") ; column2: ""; priority: 0}
ListElement { column1: qsTr("Slow (x0.2 fee)") ; column2: ""; priority: 1}
ListElement { column1: qsTr("Normal (x1 fee)") ; column2: ""; priority: 2 }
ListElement { column1: qsTr("Fast (x5 fee)") ; column2: ""; priority: 3 }
ListElement { column1: qsTr("Fastest (x200 fee)") ; column2: ""; priority: 4 }
}
StandardDropdown {
Layout.fillWidth: true
id: priorityDropdown
Layout.topMargin: 5
currentIndex: 0
}
}
// Make sure dropdown is on top
z: parent.z + 1
}
// recipient address input
RowLayout {
id: addressLineRow
@@ -260,10 +171,9 @@ Rectangle {
LineEditMulti {
id: addressLine
spacing: 0
inputPaddingRight: inlineButtonVisible && inlineButton2Visible ? 100 : 60
fontBold: true
labelText: qsTr("<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
%1 <a href='#'>(%2)</a>").arg(qsTr("Address")).arg(qsTr("Address book"))
+ translationManager.emptyString
labelText: qsTr("Address") + translationManager.emptyString
labelButtonText: qsTr("Resolve") + translationManager.emptyString
placeholderText: {
if(persistentSettings.nettype == NetworkType.MAINNET){
@@ -276,10 +186,6 @@ Rectangle {
}
wrapMode: Text.WrapAnywhere
addressValidation: true
onInputLabelLinkActivated: {
middlePanel.addressBookView.selectAndSend = true;
appWindow.showPageRequest("AddressBook");
}
onTextChanged: {
const parsed = walletManager.parse_uri_to_object(text);
if (!parsed.error) {
@@ -289,16 +195,27 @@ Rectangle {
setDescription(parsed.tx_description);
}
}
inlineButton.text: FontAwesome.qrcode
inlineButton.text: FontAwesome.addressBook
inlineButton.buttonHeight: 30
inlineButton.fontPixelSize: 22
inlineButton.fontFamily: FontAwesome.fontFamily
inlineButton.textColor: MoneroComponents.Style.defaultFontColor
inlineButton.buttonColor: MoneroComponents.Style.orange
inlineButton.onClicked: {
cameraUi.state = "Capture"
cameraUi.qrcode_decoded.connect(updateFromQrCode)
middlePanel.addressBookView.selectAndSend = true;
appWindow.showPageRequest("AddressBook");
}
inlineButtonVisible : appWindow.qrScannerEnabled && !addressLine.text
inlineButtonVisible: true
inlineButton2.text: FontAwesome.qrcode
inlineButton2.buttonHeight: 30
inlineButton2.fontPixelSize: 22
inlineButton2.fontFamily: FontAwesome.fontFamily
inlineButton2.textColor: MoneroComponents.Style.defaultFontColor
inlineButton2.onClicked: {
cameraUi.state = "Capture"
cameraUi.qrcode_decoded.connect(updateFromQrCode)
}
inlineButton2Visible: appWindow.qrScannerEnabled
}
}
@@ -348,6 +265,143 @@ Rectangle {
}
}
GridLayout {
columns: appWindow.walletMode < 2 ? 1 : 2
Layout.fillWidth: true
columnSpacing: 32
ColumnLayout {
Layout.fillWidth: true
Layout.minimumWidth: 200
// Amount input
LineEdit {
id: amountLine
Layout.fillWidth: true
inlineIcon: true
labelText: "<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
%1 <a href='#'>(%2)</a>".arg(qsTr("Amount")).arg(qsTr("Change account"))
+ translationManager.emptyString
copyButton: !isNaN(amountLine.text) && persistentSettings.fiatPriceEnabled
copyButtonText: "~%1 %2".arg(fiatApiConvertToFiat(amountLine.text)).arg(fiatApiCurrencySymbol())
copyButtonEnabled: false
onLabelLinkActivated: {
middlePanel.accountView.selectAndSend = true;
appWindow.showPageRequest("Account")
}
placeholderText: "0.00"
width: 100
fontBold: true
inlineButtonText: qsTr("All") + translationManager.emptyString
inlineButton.onClicked: amountLine.text = "(all)"
onTextChanged: {
amountLine.text = amountLine.text.replace(",", ".");
const match = amountLine.text.match(/^0+(\d.*)/);
if (match) {
const cursorPosition = amountLine.cursorPosition;
amountLine.text = match[1];
amountLine.cursorPosition = Math.max(cursorPosition, 1) - 1;
} else if(amountLine.text.indexOf('.') === 0){
amountLine.text = '0' + amountLine.text;
if (amountLine.text.length > 2) {
amountLine.cursorPosition = 1;
}
}
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
}
validator: RegExpValidator {
regExp: /^(\d{1,8})?([\.,]\d{1,12})?$/
}
}
MoneroComponents.TextPlain {
id: feeLabel
Layout.alignment: Qt.AlignRight
Layout.topMargin: 12
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 14
color: MoneroComponents.Style.defaultFontColor
property bool estimating: false
property var estimatedFee: null
property string estimatedFeeFiat: {
if (!persistentSettings.fiatPriceEnabled || estimatedFee == null) {
return "";
}
const fiatFee = fiatApiConvertToFiat(estimatedFee);
return " (%1 %3)".arg(fiatFee < 0.01 ? "<0.01" : "~" + fiatFee).arg(fiatApiCurrencySymbol());
}
property var fee: {
estimatedFee = null;
estimating = sendButton.enabled;
if (!sendButton.enabled || !currentWallet) {
return;
}
currentWallet.estimateTransactionFeeAsync(
addressLine.text,
walletManager.amountFromString(amountLine.text),
priorityModelV5.get(priorityDropdown.currentIndex).priority,
function (amount) {
estimatedFee = Utils.removeTrailingZeros(amount);
estimating = false;
});
}
text: {
if (!sendButton.enabled || estimatedFee == null) {
return ""
}
return "%1: ~%2 XMR".arg(qsTr("Fee")).arg(estimatedFee) +
estimatedFeeFiat +
translationManager.emptyString;
}
BusyIndicator {
anchors.right: parent.right
running: feeLabel.estimating
height: parent.height
}
}
}
ColumnLayout {
visible: appWindow.walletMode >= 2
Layout.alignment: Qt.AlignTop
Label {
id: transactionPriority
Layout.topMargin: 0
text: qsTr("Transaction priority") + translationManager.emptyString
fontBold: false
fontSize: 16
}
// Note: workaround for translations in listElements
// ListElement: cannot use script for property value, so
// code like this wont work:
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
// For translations to work, the strings need to be listed in
// the file components/StandardDropdown.qml too.
// Priorites after v5
ListModel {
id: priorityModelV5
ListElement { column1: qsTr("Automatic") ; column2: ""; priority: 0}
ListElement { column1: qsTr("Slow (x0.2 fee)") ; column2: ""; priority: 1}
ListElement { column1: qsTr("Normal (x1 fee)") ; column2: ""; priority: 2 }
ListElement { column1: qsTr("Fast (x5 fee)") ; column2: ""; priority: 3 }
ListElement { column1: qsTr("Fastest (x200 fee)") ; column2: ""; priority: 4 }
}
StandardDropdown {
Layout.preferredWidth: 200
id: priorityDropdown
Layout.topMargin: 5
currentIndex: 0
dataModel: priorityModelV5
}
}
}
MoneroComponents.WarningBox {
text: qsTr("Description field contents match long payment ID format. \
Please don't paste long payment ID into description field, your funds might be lost.") + translationManager.emptyString;
@@ -461,10 +515,9 @@ Rectangle {
id: advancedLayout
anchors.top: pageRoot.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 20
anchors.topMargin: 32
spacing: 26
spacing: 10
enabled: !viewOnly || pageRoot.enabled
RowLayout {
@@ -479,84 +532,88 @@ Rectangle {
}
}
GridLayout {
AdvancedOptionsItem {
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
columns: 6
StandardButton {
id: sweepUnmixableButton
text: qsTr("Sweep Unmixable") + translationManager.emptyString
enabled : pageRoot.enabled
small: true
onClicked: {
console.log("Transfer: sweepUnmixableClicked")
root.sweepUnmixableClicked()
}
title: qsTr("Key images") + translationManager.emptyString
button1.text: qsTr("Export") + translationManager.emptyString
button1.enabled: !appWindow.viewOnly
button1.onClicked: {
console.log("Transfer: export key images clicked")
exportKeyImagesDialog.open();
}
StandardButton {
id: saveTxButton
text: qsTr("Create tx file") + translationManager.emptyString
visible: appWindow.viewOnly
enabled: pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)
small: true
onClicked: {
console.log("Transfer: saveTx Clicked")
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
console.log("priority: " + priority)
console.log("amount: " + amountLine.text)
addressLine.text = addressLine.text.trim()
setPaymentId(paymentIdLine.text.trim());
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
}
button2.text: qsTr("Import") + translationManager.emptyString
button2.enabled: appWindow.viewOnly && appWindow.isTrustedDaemon()
button2.onClicked: {
console.log("Transfer: import key images clicked")
importKeyImagesDialog.open();
}
helpTextLarge.text: qsTr("Required for view-only wallets to display the real balance") + translationManager.emptyString
helpTextSmall.text: {
var errorMessage = "";
if (appWindow.viewOnly && !appWindow.isTrustedDaemon()){
errorMessage = "<p class='orange'>" + qsTr("* To import, you must connect to a local node or a trusted remote node") + "</p>";
}
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:#ffffff;} p.orange{color:#ff9323;}</style>" +
"<p>" + qsTr("1. Using cold wallet, export the key images into a file") + "</p>" +
"<p>" + qsTr("2. Using view-only wallet, import the key images file") + "</p>" +
errorMessage + translationManager.emptyString
}
helpTextSmall.themeTransition: false
}
AdvancedOptionsItem {
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
title: qsTr("Offline transaction signing") + translationManager.emptyString
button1.text: qsTr("Create") + translationManager.emptyString
button1.enabled: appWindow.viewOnly && pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)
button1.onClicked: {
console.log("Transfer: saveTx Clicked")
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
console.log("priority: " + priority)
console.log("amount: " + amountLine.text)
addressLine.text = addressLine.text.trim()
setPaymentId(paymentIdLine.text.trim());
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
}
button2.text: qsTr("Sign (offline)") + translationManager.emptyString
button2.enabled: !appWindow.viewOnly
button2.onClicked: {
console.log("Transfer: sign tx clicked")
signTxDialog.open();
}
button3.text: qsTr("Submit") + translationManager.emptyString
button3.enabled: appWindow.viewOnly
button3.onClicked: {
console.log("Transfer: submit tx clicked")
submitTxDialog.open();
}
helpTextLarge.text: qsTr("Spend XMR from a cold (offline) wallet") + translationManager.emptyString
helpTextSmall.text: {
var errorMessage = "";
if (appWindow.viewOnly && !pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)){
errorMessage = "<p class='orange'>" + qsTr("* To create a transaction file, please enter address and amount above") + "</p>";
}
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:#ffffff;} p.orange{color:#ff9323;}</style>" +
"<p>" + qsTr("1. Using view-only wallet, export the outputs into a file") + "</p>" +
"<p>" + qsTr("2. Using cold wallet, import the outputs file and export the key images") + "</p>" +
"<p>" + qsTr("3. Using view-only wallet, import the key images file and create a transaction file") + "</p>" +
errorMessage +
"<p>" + qsTr("4. Using cold wallet, sign your transaction file") + "</p>" +
"<p>" + qsTr("5. Using view-only wallet, submit your signed transaction") + "</p>" + translationManager.emptyString
}
helpTextSmall.themeTransition: false
}
StandardButton {
id: signTxButton
text: qsTr("Sign tx file") + translationManager.emptyString
small: true
visible: !appWindow.viewOnly
onClicked: {
console.log("Transfer: sign tx clicked")
signTxDialog.open();
}
}
StandardButton {
id: submitTxButton
text: qsTr("Submit tx file") + translationManager.emptyString
small: true
visible: appWindow.viewOnly
enabled: pageRoot.enabled
onClicked: {
console.log("Transfer: submit tx clicked")
submitTxDialog.open();
}
}
StandardButton {
id: exportKeyImagesButton
text: qsTr("Export key images") + translationManager.emptyString
small: true
visible: !appWindow.viewOnly
enabled: pageRoot.enabled
onClicked: {
console.log("Transfer: export key images clicked")
exportKeyImagesDialog.open();
}
}
StandardButton {
id: importKeyImagesButton
text: qsTr("Import key images") + translationManager.emptyString
small: true
enabled: appWindow.viewOnly && appWindow.isTrustedDaemon()
onClicked: {
console.log("Transfer: import key images clicked")
importKeyImagesDialog.open();
}
AdvancedOptionsItem {
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
title: qsTr("Unmixable outputs") + translationManager.emptyString
button1.text: qsTr("Sweep") + translationManager.emptyString
button1.enabled : pageRoot.enabled
button1.onClicked: {
console.log("Transfer: sweepUnmixableClicked")
root.sweepUnmixableClicked()
}
helpTextLarge.text: qsTr("Create a transaction that spends old unmovable outputs") + translationManager.emptyString
}
}
@@ -689,12 +746,6 @@ Rectangle {
function onPageCompleted() {
console.log("transfer page loaded")
updateStatus();
updatePriorityDropdown()
}
function updatePriorityDropdown() {
priorityDropdown.dataModel = priorityModelV5;
priorityDropdown.update()
}
//TODO: Add daemon sync status
@@ -717,6 +768,8 @@ Rectangle {
switch (currentWallet.connected()) {
case Wallet.ConnectionStatus_Connecting:
root.warningContent = qsTr("Wallet is connecting to daemon.")
break
case Wallet.ConnectionStatus_Disconnected:
root.warningContent = messageNotConnected;
break

View File

@@ -25,6 +25,7 @@ Item {
anchors.margins: 0
property int minWidth: 900
property int minHeight: 600
property int qrCodeSize: 220
property bool enableTracking: false
property string trackingError: "" // setting this will show a message @ tracking table
@@ -33,6 +34,9 @@ Item {
property var hiddenAmounts: []
function onPageCompleted() {
if (appWindow.currentWallet) {
appWindow.current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0)
}
// prepare tracking
trackingCheckbox.checked = root.enableTracking
root.update();
@@ -67,7 +71,7 @@ Item {
ColumnLayout {
id: mainLayout
visible: parent.width >= root.minWidth
visible: parent.width >= root.minWidth && appWindow.height >= root.minHeight
spacing: 0
// emulates max-width + center for container
@@ -544,7 +548,7 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: appWindow.showPageRequest("Receive")
onClicked: appWindow.showPageRequest("Settings")
}
}
}
@@ -553,7 +557,7 @@ Item {
Rectangle {
// Shows when the window is too small
visible: parent.width < root.minWidth
visible: parent.width < root.minWidth || appWindow.height < root.minHeight
anchors.top: parent.top
anchors.topMargin: 100;
anchors.horizontalCenter: parent.horizontalCenter
@@ -570,6 +574,13 @@ Item {
text: qsTr("The merchant page requires a larger window") + translationManager.emptyString
themeTransition: false
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: appWindow.showPageRequest("Settings")
}
}
function update() {
@@ -589,9 +600,7 @@ Item {
var model = appWindow.currentWallet.historyModel
var count = model.rowCount()
var totalAmount = 0
var nTransactions = 0
var blockchainHeight = null
var txs = []
// Currently selected subaddress as per Receive page
@@ -607,8 +616,6 @@ Item {
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == current_subaddress_table_index) {
var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole);
totalAmount = walletManager.addi(totalAmount, amount)
nTransactions += 1
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
@@ -616,21 +623,17 @@ Item {
var in_txpool = false;
var confirmations = 0;
var displayAmount = 0;
var displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
if (blockHeight == 0) {
if (blockHeight === undefined) {
in_txpool = true;
} else {
if (blockchainHeight == null)
blockchainHeight = walletManager.blockchainHeight()
confirmations = blockchainHeight - blockHeight - 1
displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
confirmations = model.data(idx, TransactionHistoryModel.TransactionConfirmationsRole);
}
txs.push({
"amount": displayAmount,
"confirmations": confirmations,
"blockheight": blockHeight,
"in_txpool": in_txpool,
"txid": txid,
"time_epoch": timeEpoch,
@@ -650,9 +653,7 @@ Item {
txs.forEach(function(tx){
trackingModel.append({
"amount": tx.amount,
"blockheight": tx.blockheight,
"confirmations": tx.confirmations,
"blockheight": tx.blockHeight,
"in_txpool": tx.in_txpool,
"txid": tx.txid,
"time_epoch": tx.time_epoch,

View File

@@ -125,7 +125,7 @@ ListView {
font.pixelSize: 14
font.bold: true
color: hide_amount ? "#707070" : "#009F1E"
text: hide_amount ? '-' : '+' + amount
text: hide_amount ? '-' : '+' + amount + (in_txpool ? ' (%1)'.arg(qsTr('unconfirmed')) : '')
selectionColor: MoneroComponents.Style.textSelectionColor
selectByMouse: true
readOnly: true

View File

@@ -273,7 +273,6 @@ Rectangle {
// LOG
id: navLog
property bool isActive: settingsStateView.state === "Log"
visible: appWindow.walletMode >= 2
Layout.preferredWidth: navLogText.width + grid.textMargin
Layout.preferredHeight: 32
Layout.minimumWidth: 72

View File

@@ -103,7 +103,7 @@ Rectangle {
MoneroComponents.TextBlock {
font.pixelSize: 14
color: MoneroComponents.Style.dimmedFontColor
text: Version.GUI_MONERO_VERSION + translationManager.emptyString
text: moneroVersion
}
Rectangle {
@@ -131,10 +131,11 @@ Rectangle {
}
MoneroComponents.TextBlock {
id: walletLocation
Layout.fillWidth: true
color: MoneroComponents.Style.dimmedFontColor
font.pixelSize: 14
property string walletPath: (isIOS ? moneroAccountsDir : "") + appWindow.walletPath()
property string walletPath: (isIOS ? moneroAccountsDir : "") + persistentSettings.wallet_path
text: "\
<style type='text/css'>\
a {cursor:pointer;text-decoration: none; color: #FF6C3C}\
@@ -212,7 +213,6 @@ Rectangle {
+ "The old wallet cache file will be renamed and can be restored later.\n"
);
confirmationDialog.icon = StandardIcon.Question
confirmationDialog.cancelText = qsTr("Cancel")
confirmationDialog.onAcceptedCallback = function() {
appWindow.closeWallet(function() {
walletManager.clearWalletCache(persistentSettings.wallet_path);
@@ -230,7 +230,7 @@ Rectangle {
appWindow.showStatusMessage(qsTr("Invalid restore height specified. Must be a number or a date formatted YYYY-MM-DD"),3);
}
inputDialog.onRejectedCallback = null;
inputDialog.open(currentWallet ? currentWallet.walletCreationHeight : "0")
inputDialog.open(currentWallet ? currentWallet.walletCreationHeight.toFixed(0) : "0")
}
MouseArea {
@@ -381,32 +381,38 @@ Rectangle {
}
}
// Copy info to clipboard
MoneroComponents.StandardButton {
small: true
text: qsTr("Copy to clipboard") + translationManager.emptyString
onClicked: {
var data = "";
data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")";
data += "\nEmbedded Monero version: " + Version.GUI_MONERO_VERSION;
data += "\nWallet path: ";
RowLayout {
spacing: 20;
var wallet_path = walletPath();
if(isIOS)
wallet_path = moneroAccountsDir + wallet_path;
data += wallet_path;
MoneroComponents.StandardButton {
small: true
text: qsTr("Copy to clipboard") + translationManager.emptyString
onClicked: {
var data = "";
data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")";
data += "\nEmbedded Monero version: " + moneroVersion;
data += "\nWallet path: " + walletLocation.walletPath;
data += "\nWallet creation height: ";
if(currentWallet)
data += currentWallet.walletCreationHeight;
data += "\nWallet creation height: ";
if(currentWallet)
data += currentWallet.walletCreationHeight;
data += "\nWallet log path: " + walletLogPath;
data += "\nWallet mode: " + walletModeString;
data += "\nGraphics: " + isOpenGL ? "OpenGL" : "Low graphics mode";
data += "\nWallet log path: " + walletLogPath;
data += "\nWallet mode: " + walletModeString;
data += "\nGraphics: " + isOpenGL ? "OpenGL" : "Low graphics mode";
console.log("Copied to clipboard");
clipboard.setText(data);
appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3);
console.log("Copied to clipboard");
clipboard.setText(data);
appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3);
}
}
MoneroComponents.StandardButton {
small: true
text: qsTr("Donate to Monero") + translationManager.emptyString
onClicked: {
middlePanel.sendTo("888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", "", "Donation to Monero Core Team");
}
}
}
}

View File

@@ -58,6 +58,14 @@ Rectangle {
text: qsTr("Custom decorations") + translationManager.emptyString
}
MoneroComponents.CheckBox {
id: checkForUpdatesCheckBox
enabled: !disableCheckUpdatesFlag
checked: persistentSettings.checkForUpdates && !disableCheckUpdatesFlag
onClicked: persistentSettings.checkForUpdates = !persistentSettings.checkForUpdates
text: qsTr("Check for updates periodically") + translationManager.emptyString
}
MoneroComponents.CheckBox {
id: hideBalanceCheckBox
checked: persistentSettings.hideBalance
@@ -78,6 +86,46 @@ Rectangle {
persistentSettings.blackTheme = MoneroComponents.Style.blackTheme;
}
}
MoneroComponents.CheckBox {
checked: persistentSettings.askPasswordBeforeSending
text: qsTr("Ask for password before sending a transaction") + translationManager.emptyString
toggleOnClick: false
onClicked: {
if (persistentSettings.askPasswordBeforeSending) {
passwordDialog.onAcceptedCallback = function() {
if (appWindow.walletPassword === passwordDialog.password){
persistentSettings.askPasswordBeforeSending = false;
} else {
passwordDialog.showError(qsTr("Wrong password"));
}
}
passwordDialog.onRejectedCallback = null;
passwordDialog.open()
} else {
persistentSettings.askPasswordBeforeSending = true;
}
}
}
MoneroComponents.CheckBox {
checked: persistentSettings.autosave
onClicked: persistentSettings.autosave = !persistentSettings.autosave
text: qsTr("Autosave") + translationManager.emptyString
}
MoneroComponents.Slider {
Layout.fillWidth: true
Layout.leftMargin: 35
Layout.topMargin: 6
visible: persistentSettings.autosave
from: 1
stepSize: 1
to: 60
value: persistentSettings.autosaveMinutes
text: "%1 %2 %3".arg(qsTr("Every")).arg(value).arg(qsTr("minute(s)")) + translationManager.emptyString
onMoved: persistentSettings.autosaveMinutes = value
}
MoneroComponents.CheckBox {
id: userInActivityCheckbox
@@ -86,70 +134,20 @@ Rectangle {
text: qsTr("Lock wallet on inactivity") + translationManager.emptyString
}
ColumnLayout {
MoneroComponents.Slider {
visible: userInActivityCheckbox.checked
Layout.fillWidth: true
Layout.topMargin: 6
Layout.leftMargin: 42
spacing: 0
Text {
color: MoneroComponents.Style.defaultFontColor
font.pixelSize: 14
Layout.fillWidth: true
text: {
var val = userInactivitySlider.value;
var minutes = val > 1 ? qsTr("minutes") : qsTr("minute");
qsTr("After ") + val + " " + minutes + translationManager.emptyString;
}
}
Slider {
id: userInactivitySlider
from: 1
value: persistentSettings.lockOnUserInActivityInterval
to: 60
leftPadding: 0
stepSize: 1
snapMode: Slider.SnapAlways
background: Rectangle {
x: parent.leftPadding
y: parent.topPadding + parent.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: parent.availableWidth
height: implicitHeight
radius: 2
color: MoneroComponents.Style.progressBarBackgroundColor
Rectangle {
width: parent.visualPosition * parent.width
height: parent.height
color: MoneroComponents.Style.green
radius: 2
}
}
handle: Rectangle {
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
y: parent.topPadding + parent.availableHeight / 2 - height / 2
implicitWidth: 18
implicitHeight: 18
radius: 8
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
border.color: MoneroComponents.Style.grey
}
onMoved: persistentSettings.lockOnUserInActivityInterval = userInactivitySlider.value;
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
}
Layout.leftMargin: 35
from: 1
stepSize: 1
to: 60
value: persistentSettings.lockOnUserInActivityInterval
text: {
var minutes = value > 1 ? qsTr("minutes") : qsTr("minute");
return qsTr("After ") + value + " " + minutes + translationManager.emptyString;
}
onMoved: persistentSettings.lockOnUserInActivityInterval = value
}
//! Manage pricing
@@ -212,6 +210,7 @@ Rectangle {
MoneroComponents.StandardDropdown {
id: fiatPriceCurrencyDropdown
Layout.fillWidth: true
currentIndex: persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1
dataModel: fiatPriceCurrencyModel
onChanged: {
var obj = dataModel.get(currentIndex);
@@ -297,10 +296,6 @@ Rectangle {
i += 1;
}
fiatPriceProviderDropDown.update();
fiatPriceCurrencyDropdown.currentIndex = persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1;
fiatPriceCurrencyDropdown.update();
console.log('SettingsLayout loaded');
}
}

View File

@@ -159,7 +159,7 @@ Rectangle {
textFormat: TextEdit.RichText
selectByMouse: true
selectByKeyboard: true
font.family: MoneroComponents.Style.defaultFontColor
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 14
wrapMode: TextEdit.Wrap
readOnly: true
@@ -213,9 +213,27 @@ Rectangle {
MoneroComponents.LineEdit {
id: sendCommandText
Layout.fillWidth: true
property var lastCommands: []
property int currentCommandIndex
fontBold: false
placeholderText: qsTr("command + enter (e.g 'help' or 'status')") + translationManager.emptyString
placeholderFontSize: 16
Keys.onUpPressed: {
if (currentCommandIndex != 0) {
sendCommandText.text = lastCommands[currentCommandIndex - 1]
currentCommandIndex = currentCommandIndex - 1
}
}
Keys.onDownPressed: {
if (currentCommandIndex == lastCommands.length - 1) {
currentCommandIndex = lastCommands.length;
return text = "";
}
if (currentCommandIndex != lastCommands.length) {
sendCommandText.text = lastCommands[currentCommandIndex + 1]
currentCommandIndex = currentCommandIndex + 1
}
}
onAccepted: {
if(text.length > 0) {
consoleArea.logCommand(">>> " + text)
@@ -225,15 +243,14 @@ Rectangle {
}
});
}
lastCommands.push(text);
currentCommandIndex = lastCommands.length;
text = ""
}
}
}
Component.onCompleted: {
logLevelDropdown.currentIndex = appWindow.persistentSettings.logLevel;
logLevelDropdown.update();
if(typeof daemonManager != "undefined")
daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated)
}

View File

@@ -130,13 +130,6 @@ Rectangle{
topPadding: 0
text: qsTr("The blockchain is downloaded to your computer. Provides higher security and requires more local storage.") + translationManager.emptyString
width: parent.width - (localNodeIcon.width + localNodeIcon.anchors.leftMargin + anchors.leftMargin)
// @TODO: Legacy. Remove after Qt 5.8.
// https://stackoverflow.com/questions/41990013
MouseArea {
anchors.fill: parent
enabled: false
}
}
}
@@ -229,13 +222,6 @@ Rectangle{
topPadding: 0
text: qsTr("Uses a third-party server to connect to the Monero network. Less secure, but easier on your computer.") + translationManager.emptyString
width: parent.width - (remoteNodeIcon.width + remoteNodeIcon.anchors.leftMargin + anchors.leftMargin)
// @TODO: Legacy. Remove after Qt 5.8.
// https://stackoverflow.com/questions/41990013
MouseArea {
anchors.fill: parent
enabled: false
}
}
MouseArea {
@@ -279,12 +265,10 @@ Rectangle{
Layout.minimumWidth: 100
placeholderFontSize: 15
daemonAddrLabelText: qsTr("Address")
daemonPortLabelText: qsTr("Port")
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
property var rna: persistentSettings.remoteNodeAddress
daemonAddrText: rna.search(":") != -1 ? rna.split(":")[0].trim() : ""
daemonPortText: rna.search(":") != -1 ? (rna.split(":")[1].trim() == "") ? appWindow.getDefaultDaemonRpcPort(persistentSettings.nettype) : rna.split(":")[1] : ""
initialAddress: persistentSettings.remoteNodeAddress
onEditingFinished: {
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
console.log("setting remote node to " + persistentSettings.remoteNodeAddress);
@@ -318,7 +302,7 @@ Rectangle{
labelText: qsTr("Daemon password") + translationManager.emptyString
text: persistentSettings.daemonPassword
placeholderText: qsTr("Password") + translationManager.emptyString
echoMode: TextInput.Password
password: true
placeholderFontSize: 15
labelFontSize: 14
fontSize: 15
@@ -382,14 +366,12 @@ Rectangle{
labelFontSize: 14
property string style: "<style type='text/css'>a {cursor:pointer;text-decoration: none; color: #FF6C3C}</style>"
labelText: qsTr("Blockchain location") + style + " <a href='#'> (%1)</a>".arg(qsTr("Change")) + translationManager.emptyString
labelButtonText: qsTr("Reset") + translationManager.emptyString
labelButtonVisible: text
placeholderText: qsTr("(default)") + translationManager.emptyString
placeholderFontSize: 15
readOnly: true
text: {
if(persistentSettings.blockchainDataDir.length > 0){
return persistentSettings.blockchainDataDir;
} else { return "" }
}
text: persistentSettings.blockchainDataDir
addressValidation: false
onInputLabelLinkActivated: {
//mouse.accepted = false
@@ -399,6 +381,7 @@ Rectangle{
blockchainFileDialog.open();
blockchainFolder.focus = true;
}
onLabelButtonClicked: persistentSettings.blockchainDataDir = ""
}
}
@@ -413,7 +396,12 @@ Rectangle{
placeholderFontSize: 15
text: persistentSettings.daemonFlags
addressValidation: false
onEditingFinished: persistentSettings.daemonFlags = daemonFlags.text;
error: text.match(/(^|\s)--(data-dir|bootstrap-daemon-address)/)
onEditingFinished: {
if (!daemonFlags.error) {
persistentSettings.daemonFlags = daemonFlags.text;
}
}
}
RowLayout {
@@ -427,17 +415,9 @@ Rectangle{
Layout.minimumWidth: 100
Layout.bottomMargin: 20
daemonAddrLabelText: qsTr("Bootstrap Address")
daemonPortLabelText: qsTr("Bootstrap Port")
daemonAddrText: persistentSettings.bootstrapNodeAddress.split(":")[0].trim()
daemonPortText: {
var node_split = persistentSettings.bootstrapNodeAddress.split(":");
if(node_split.length == 2){
(node_split[1].trim() == "") ? appWindow.getDefaultDaemonRpcPort(persistentSettings.nettype) : node_split[1];
} else {
return ""
}
}
daemonAddrLabelText: qsTr("Bootstrap Address") + translationManager.emptyString
daemonPortLabelText: qsTr("Bootstrap Port") + translationManager.emptyString
initialAddress: persistentSettings.bootstrapNodeAddress
onEditingFinished: {
if (daemonAddrText == "auto") {
persistentSettings.bootstrapNodeAddress = daemonAddrText;

View File

@@ -30,6 +30,7 @@ import QtQuick 2.9
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2
import FontAwesome 1.0
import "../../js/Utils.js" as Utils
import "../../components" as MoneroComponents
@@ -47,10 +48,10 @@ Rectangle {
anchors.right: parent.right
anchors.margins: 20
anchors.topMargin: 0
spacing: 8
spacing: 0
MoneroComponents.SettingsListItem {
buttonText: qsTr("Close wallet") + translationManager.emptyString
iconText: FontAwesome.signOutAlt
description: qsTr("Logs out of this wallet.") + translationManager.emptyString
title: qsTr("Close this wallet") + translationManager.emptyString
@@ -58,7 +59,7 @@ Rectangle {
}
MoneroComponents.SettingsListItem {
buttonText: qsTr("Create wallet") + translationManager.emptyString
iconText: FontAwesome.eye
description: qsTr("Creates a new wallet that can only view and initiate transactions, but requires a spendable wallet to sign transactions before sending.") + translationManager.emptyString
title: qsTr("Create a view-only wallet") + translationManager.emptyString
visible: !appWindow.viewOnly
@@ -80,7 +81,7 @@ Rectangle {
}
MoneroComponents.SettingsListItem {
buttonText: qsTr("Show seed") + translationManager.emptyString
iconText: FontAwesome.key
description: qsTr("Store this information safely to recover your wallet in the future.") + translationManager.emptyString
title: qsTr("Show seed & keys") + translationManager.emptyString
@@ -90,7 +91,7 @@ Rectangle {
}
MoneroComponents.SettingsListItem {
buttonText: qsTr("Rescan") + translationManager.emptyString
iconText: FontAwesome.repeat
description: qsTr("Use this feature if you think the shown balance is not accurate.") + translationManager.emptyString
title: qsTr("Rescan wallet balance") + translationManager.emptyString
visible: appWindow.walletMode >= 2
@@ -114,7 +115,7 @@ Rectangle {
}
MoneroComponents.SettingsListItem {
buttonText: qsTr("Change password") + translationManager.emptyString
iconText: FontAwesome.ellipsisH
description: qsTr("Change the password of your wallet.") + translationManager.emptyString
title: qsTr("Change wallet password") + translationManager.emptyString
@@ -135,6 +136,19 @@ Rectangle {
passwordDialog.open()
}
}
MoneroComponents.SettingsListItem {
iconText: FontAwesome.cashRegister
isLast: true
description: qsTr("Receive Monero for your business, easily.") + translationManager.emptyString
title: qsTr("Enter merchant mode") + translationManager.emptyString
onClicked: {
middlePanel.state = "Merchant";
middlePanel.flickable.contentY = 0;
updateBalance();
}
}
}
Component.onCompleted: {

20
qml.qrc
View File

@@ -5,10 +5,14 @@
<file>MiddlePanel.qml</file>
<file>components/Label.qml</file>
<file>components/SettingsListItem.qml</file>
<file>components/Slider.qml</file>
<file>components/UpdateDialog.qml</file>
<file>images/whatIsIcon.png</file>
<file>images/whatIsIcon@2x.png</file>
<file>images/lockIcon.png</file>
<file>components/MenuButton.qml</file>
<file>monero/utils/gpg_keys/binaryfate.asc</file>
<file>monero/utils/gpg_keys/fluffypony.asc</file>
<file>monero/utils/gpg_keys/luigi1111.asc</file>
<file>pages/Account.qml</file>
<file>pages/Transfer.qml</file>
<file>pages/History.qml</file>
@@ -23,7 +27,7 @@
<file>components/TipItem.qml</file>
<file>images/tip.png</file>
<file>components/MenuButtonDivider.qml</file>
<file>images/moneroIcon.png</file>
<file>images/monero-vector.svg</file>
<file>components/StandardDropdown.qml</file>
<file>images/whiteDropIndicator.png</file>
<file>images/whiteDropIndicator@2x.png</file>
@@ -34,7 +38,6 @@
<file>images/prevMonth.png</file>
<file>images/prevMonth@2x.png</file>
<file>components/TitleBar.qml</file>
<file>images/moneroLogo2.png</file>
<file>images/resize.png</file>
<file>images/resize@2x.png</file>
<file>images/resizeHovered.png</file>
@@ -96,11 +99,11 @@
<file>components/ProcessingSplash.qml</file>
<file>components/ProgressBar.qml</file>
<file>components/StandardDialog.qml</file>
<file>components/DevicePassphraseDialog.qml</file>
<file>pages/Sign.qml</file>
<file>components/DaemonManagerDialog.qml</file>
<file>version.js</file>
<file>components/QRCodeScanner.qml</file>
<file>components/Notifier.qml</file>
<file>components/TextBlock.qml</file>
<file>components/RemoteNodeEdit.qml</file>
<file>pages/Keys.qml</file>
@@ -110,8 +113,6 @@
<file>images/card-background-white.png</file>
<file>images/card-background-white@2x.png</file>
<file>images/moneroLogo_white.png</file>
<file>images/question.png</file>
<file>images/question@2x.png</file>
<file>images/titlebarLogo.png</file>
<file>images/titlebarLogo@2x.png</file>
<file>pages/merchant/MerchantTitlebar.qml</file>
@@ -188,7 +189,6 @@
<file>wizard/WizardHeader.qml</file>
<file>wizard/WizardHome.qml</file>
<file>wizard/WizardLanguage.qml</file>
<file>wizard/WizardLang.qml</file>
<file>wizard/WizardNav.qml</file>
<file>wizard/WizardWalletInput.qml</file>
<file>wizard/WizardRestoreWallet1.qml</file>
@@ -204,7 +204,6 @@
<file>js/Wizard.js</file>
<file>components/LanguageSidebar.qml</file>
<file>images/world-flags-globe.png</file>
<file>images/langFlagGrey.png</file>
<file>images/restore-wallet-from-hardware@2x.png</file>
<file>images/restore-wallet-from-hardware.png</file>
<file>images/open-wallet-from-file@2x.png</file>
@@ -219,7 +218,6 @@
<file>images/local-node@2x.png</file>
<file>images/local-node-full.png</file>
<file>images/local-node-full@2x.png</file>
<file>wizard/WizardNavProgressDot.qml</file>
<file>wizard/WizardOpenWallet1.qml</file>
<file>images/arrow-right-in-circle.png</file>
<file>images/arrow-right-in-circle@2x.png</file>
@@ -233,7 +231,6 @@
<file>images/themes/white/close.svg</file>
<file>images/themes/white/fullscreen.svg</file>
<file>images/themes/white/minimize.svg</file>
<file>images/themes/white/question.svg</file>
<file>components/effects/ColorTransition.qml</file>
<file>components/effects/GradientBackground.qml</file>
<file>images/check-white.svg</file>
@@ -241,5 +238,8 @@
<file>images/edit.svg</file>
<file>images/arrow-right-in-circle-outline-medium-white.svg</file>
<file>images/tails-grey.png</file>
<file>components/AdvancedOptionsItem.qml</file>
<file>images/busy-indicator.png</file>
<file>images/busy-indicator@2x.png</file>
</qresource>
</RCC>

View File

@@ -3,6 +3,7 @@ add_subdirectory(QR-Code-scanner)
add_subdirectory(daemon)
add_subdirectory(libwalletqt)
add_subdirectory(model)
add_subdirectory(openpgp)
add_subdirectory(zxcvbn-c)
qt5_add_resources(RESOURCES ../qml.qrc)
@@ -14,7 +15,9 @@ file(GLOB SOURCE_FILES
"main/*.h"
"main/*.cpp"
"libwalletqt/WalletManager.cpp"
"libwalletqt/WalletListenerImpl.cpp"
"libwalletqt/Wallet.cpp"
"libwalletqt/PassphraseHelper.cpp"
"libwalletqt/PendingTransaction.cpp"
"libwalletqt/TransactionHistory.cpp"
"libwalletqt/TransactionInfo.cpp"
@@ -28,6 +31,7 @@ file(GLOB SOURCE_FILES
"libwalletqt/UnsignedTransaction.cpp"
"libwalletqt/WalletManager.h"
"libwalletqt/Wallet.h"
"libwalletqt/PassphraseHelper.h"
"libwalletqt/PendingTransaction.h"
"libwalletqt/TransactionHistory.h"
"libwalletqt/TransactionInfo.h"
@@ -40,13 +44,16 @@ file(GLOB SOURCE_FILES
"libwalletqt/Subaddress.h"
"libwalletqt/SubaddressAccount.h"
"libwalletqt/UnsignedTransaction.h"
"daemon/*.h"
"daemon/*.cpp"
"model/*.h"
"model/*.cpp"
"qt/*.h"
"daemon/*.h"
"daemon/*.cpp"
"model/*.h"
"model/*.cpp"
"qt/*.h"
"qt/*.cpp"
)
if(APPLE)
list(APPEND SOURCE_FILES "qt/macoshelper.mm")
endif()
if(ENABLE_PASS_STRENGTH_METER)
file(GLOB PASS_STRENGTH_FILES
@@ -76,27 +83,40 @@ if(MINGW)
list(APPEND RESOURCES ${ICON_RES})
endif()
add_executable(monero-gui ${EXECUTABLE_FLAG} main/main.cpp
if(APPLE)
set(ICON ${PROJECT_SOURCE_DIR}/images/appicon.icns)
set_source_files_properties(${ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
list(APPEND RESOURCES ${ICON})
endif()
add_executable(monero-wallet-gui ${EXECUTABLE_FLAG} main/main.cpp
${SOURCE_FILES}
${PASS_STRENGTH_FILES}
${QR_CODE_FILES}
${RESOURCES}
)
set_property(TARGET monero-gui PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set_target_properties(monero-wallet-gui PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/share/Info.plist"
)
# OpenGL
target_include_directories(monero-gui PUBLIC ${OPENGL_INCLUDE_DIR})
target_include_directories(monero-wallet-gui PUBLIC ${OPENGL_INCLUDE_DIR})
message(STATUS "OpenGL: include dir at ${OPENGL_INCLUDE_DIR}")
message(STATUS "OpenGL: libraries at ${OPENGL_LIBRARIES}")
target_include_directories(monero-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
target_include_directories(monero-wallet-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
file(GLOB_RECURSE SRC_SOURCES *.cpp)
file(GLOB_RECURSE SRC_HEADERS *.h)
target_include_directories(monero-gui PUBLIC
target_include_directories(monero-wallet-gui PUBLIC
${CMAKE_SOURCE_DIR}/monero/include
${CMAKE_SOURCE_DIR}/monero/src
${CMAKE_SOURCE_DIR}/monero/external/easylogging++
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/daemon
${CMAKE_CURRENT_SOURCE_DIR}/libwalletqt
@@ -112,33 +132,24 @@ target_include_directories(monero-gui PUBLIC
${ZBAR_INCLUDE_DIR}
)
target_compile_definitions(monero-gui
target_compile_definitions(monero-wallet-gui
PUBLIC
${Qt5Widgets_DEFINITIONS}
${Qt5Qml_DEFINITIONS}
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
if(X11_FOUND)
target_link_libraries(monero-gui ${X11_LIBRARIES} pthread dl Xt xcb X11)
endif()
if(DEVICE_TREZOR_READY)
target_link_libraries(monero-gui ${TREZOR_DEP_LIBS})
endif()
target_link_libraries(monero-gui
${CMAKE_BINARY_DIR}/lib/libwallet_merged.a
target_link_libraries(monero-wallet-gui
wallet_merged
${LMDB_LIBRARY}
${CMAKE_BINARY_DIR}/monero/contrib/epee/src/libepee.a
${CMAKE_BINARY_DIR}/monero/external/unbound/libunbound.a
epee
${UNBOUND_LIBRARY}
${SODIUM_LIBRARY}
${CMAKE_BINARY_DIR}/monero/external/easylogging++/libeasylogging.a
${CMAKE_BINARY_DIR}/monero/src/blockchain_db/libblockchain_db.a
${CMAKE_BINARY_DIR}/monero/external/randomx/librandomx.a
${CMAKE_BINARY_DIR}/monero/src/hardforks/libhardforks.a
easylogging
blockchain_db
randomx
hardforks
${Boost_LIBRARIES}
${OPENSSL_LIBRARIES}
${CMAKE_DL_LIBS}
@@ -147,10 +158,20 @@ target_link_libraries(monero-gui
${QT5_LIBRARIES}
${EXTRA_LIBRARIES}
${ICU_LIBRARIES}
openpgp
translations
)
if(DEVICE_TREZOR_READY)
target_link_libraries(monero-wallet-gui ${TREZOR_DEP_LIBS})
endif()
if(X11_FOUND)
target_link_libraries(monero-wallet-gui ${X11_LIBRARIES})
endif()
if(WITH_SCANNER)
target_link_libraries(monero-gui
target_link_libraries(monero-wallet-gui
${ZBAR_LIBRARIES}
jpeg
v4l2
@@ -159,6 +180,6 @@ if(WITH_SCANNER)
)
endif()
install(TARGETS monero-gui
install(TARGETS monero-wallet-gui
DESTINATION ${CMAKE_INSTALL_PREFIX}
)

View File

@@ -40,7 +40,7 @@ QrCodeScanner::QrCodeScanner(QObject *parent)
m_probe = new QVideoProbe(this);
m_thread = new QrScanThread(this);
m_thread->start();
QObject::connect(m_thread, SIGNAL(decoded(int,QString)), this, SLOT(processCode(int,QString)));
QObject::connect(m_thread, SIGNAL(decoded(int, QString)), this, SIGNAL(decoded(int, QString)));
QObject::connect(m_thread, SIGNAL(notifyError(const QString &, bool)), this, SIGNAL(notifyError(const QString &, bool)));
connect(m_probe, SIGNAL(videoFrameProbed(QVideoFrame)), this, SLOT(processFrame(QVideoFrame)));
}
@@ -48,37 +48,6 @@ void QrCodeScanner::setSource(QCamera *camera)
{
m_probe->setSource(camera);
}
void QrCodeScanner::processCode(int type, const QString &data)
{
if (! m_enabled) return;
qDebug() << "decoded - type: " << type << " data: " << data;
QString address, payment_id, tx_description, recipient_name, error;
QVector<QString> unknown_parameters;
uint64_t amount(0);
if( ! WalletManager::instance()->parse_uri(data, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error) )
{
qDebug() << "Failed to parse_uri : " << error;
emit notifyError(error);
return;
}
QVariantMap parsed_unknown_parameters;
if(unknown_parameters.size() > 0)
{
qDebug() << "unknown parameters " << unknown_parameters;
foreach(const QString &item, unknown_parameters )
{
QStringList parsed_item = item.split("=");
if(parsed_item.size() == 2) {
parsed_unknown_parameters.insert(parsed_item[0], parsed_item[1]);
}
}
emit notifyError(error, true);
}
qDebug() << "Parsed URI : " << address << " " << payment_id << " " << amount << " " << tx_description << " " << recipient_name << " " << error;
QString s_amount = WalletManager::instance()->displayAmount(amount);
qDebug() << "Amount passed " << s_amount ;
emit decoded(address, payment_id, s_amount, tx_description, recipient_name, parsed_unknown_parameters);
}
void QrCodeScanner::processFrame(QVideoFrame frame)
{
if(frame.isValid()){

View File

@@ -51,13 +51,12 @@ public:
void setEnabled(bool enabled);
public Q_SLOTS:
void processCode(int type, const QString &data);
void processFrame(QVideoFrame);
Q_SIGNALS:
void enabledChanged();
void decoded(const QString &address, const QString &payment_id, const QString &amount, const QString &tx_description, const QString &recipient_name, const QVariantMap &extra_parameters);
void decoded(int type, const QString &data);
void decode(int type, const QString &data);
void notifyError(const QString &error, bool warning = false);

View File

@@ -30,13 +30,8 @@
#include <QtGlobal>
#include <QDebug>
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
extern QImage qt_imageFromVideoFrame(const QVideoFrame &f);
#else
QImage qt_imageFromVideoFrame(const QVideoFrame &f){
Q_ASSERT_X(0 != 0, "qt_imageFromVideoFrame", "Should have been managed in .pro");
return QImage();
}
#endif
QrScanThread::QrScanThread(QObject *parent)
@@ -80,7 +75,11 @@ bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
unsigned int bpl( qimg.bytesPerLine() ), width( bpl / 4), height( qimg.height());
dst.set_size(width, height);
dst.set_format("BGR4");
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
unsigned long datalen = qimg.sizeInBytes();
#else
unsigned long datalen = qimg.byteCount();
#endif
dst.set_data(qimg.bits(), datalen);
if((width * 4 != bpl) || (width * height * 4 > datalen)){
emit notifyError(QString("QImage to Zbar::Image failed !"));
@@ -104,7 +103,11 @@ void QrScanThread::processQImage(const QImage &qimg)
void QrScanThread::processVideoFrame(const QVideoFrame &frame)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
processQImage( qt_imageFromVideoFrame(frame) );
#else
processQImage(frame.image());
#endif
}
void QrScanThread::stop()

View File

@@ -29,6 +29,7 @@
#include "DaemonManager.h"
#include <QElapsedTimer>
#include <QFile>
#include <QMutexLocker>
#include <QThread>
#include <QFileInfo>
#include <QDir>
@@ -113,8 +114,8 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
arguments << "--check-updates" << "disabled";
// --max-concurrency based on threads available. max: 6
int32_t concurrency = qBound(1, QThread::idealThreadCount() / 2, 6);
// --max-concurrency based on threads available.
int32_t concurrency = qMax(1, QThread::idealThreadCount() / 2);
if(!flags.contains("--max-concurrency", Qt::CaseSensitive)){
arguments << "--max-concurrency" << QString::number(concurrency);
@@ -123,18 +124,19 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
qDebug() << "starting monerod " + m_monerod;
qDebug() << "With command line arguments " << arguments;
m_daemon = new QProcess();
initialized = true;
QMutexLocker locker(&m_daemonMutex);
m_daemon.reset(new QProcess());
// Connect output slots
connect (m_daemon, SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
connect (m_daemon, SIGNAL(readyReadStandardError()), this, SLOT(printError()));
connect(m_daemon.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
connect(m_daemon.get(), SIGNAL(readyReadStandardError()), this, SLOT(printError()));
// Start monerod
bool started = m_daemon->startDetached(m_monerod, arguments);
// add state changed listener
connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState)));
connect(m_daemon.get(), SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(stateChanged(QProcess::ProcessState)));
if (!started) {
qDebug() << "Daemon start error: " + m_daemon->errorString();
@@ -200,9 +202,9 @@ bool DaemonManager::stopWatcher(NetworkType::Type nettype) const
if(counter >= 5) {
qDebug() << "Killing it! ";
#ifdef Q_OS_WIN
QProcess::execute("taskkill /F /IM monerod.exe");
QProcess::execute("taskkill", {"/F", "/IM", "monerod.exe"});
#else
QProcess::execute("pkill monerod");
QProcess::execute("pkill", {"monerod"});
#endif
}
@@ -223,7 +225,10 @@ void DaemonManager::stateChanged(QProcess::ProcessState state)
void DaemonManager::printOutput()
{
QByteArray byteArray = m_daemon->readAllStandardOutput();
QByteArray byteArray = [this]() {
QMutexLocker locker(&m_daemonMutex);
return m_daemon->readAllStandardOutput();
}();
QStringList strLines = QString(byteArray).split("\n");
foreach (QString line, strLines) {
@@ -234,7 +239,10 @@ void DaemonManager::printOutput()
void DaemonManager::printError()
{
QByteArray byteArray = m_daemon->readAllStandardError();
QByteArray byteArray = [this]() {
QMutexLocker locker(&m_daemonMutex);
return m_daemon->readAllStandardError();
}();
QStringList strLines = QString(byteArray).split("\n");
foreach (QString line, strLines) {
@@ -351,7 +359,6 @@ DaemonManager::DaemonManager(QObject *parent)
if (m_monerod.length() == 0) {
qCritical() << "no daemon binary defined for current platform";
m_has_daemon = false;
}
}

View File

@@ -29,6 +29,9 @@
#ifndef DAEMONMANAGER_H
#define DAEMONMANAGER_H
#include <memory>
#include <QMutex>
#include <QObject>
#include <QUrl>
#include <QProcess>
@@ -78,10 +81,9 @@ private:
static DaemonManager * m_instance;
static QStringList m_clArgs;
QProcess *m_daemon;
bool initialized = false;
std::unique_ptr<QProcess> m_daemon;
QMutex m_daemonMutex;
QString m_monerod;
bool m_has_daemon = true;
bool m_app_exit = false;
bool m_noSync = false;

View File

@@ -0,0 +1,70 @@
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "PassphraseHelper.h"
#include <QMutexLocker>
#include <QDebug>
Monero::optional<std::string> PassphraseHelper::onDevicePassphraseRequest(bool & on_device)
{
qDebug() << __FUNCTION__;
QMutexLocker locker(&m_mutex_pass);
m_passphrase_on_device = true;
m_passphrase_abort = false;
if (m_prompter != nullptr){
m_prompter->onWalletPassphraseNeeded(on_device);
}
m_cond_pass.wait(&m_mutex_pass);
if (m_passphrase_abort)
{
throw std::runtime_error("Passphrase entry abort");
}
on_device = m_passphrase_on_device;
if (!on_device) {
auto tmpPass = m_passphrase.toStdString();
m_passphrase = QString();
return Monero::optional<std::string>(tmpPass);
} else {
return Monero::optional<std::string>();
}
}
void PassphraseHelper::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
{
qDebug() << __FUNCTION__;
QMutexLocker locker(&m_mutex_pass);
m_passphrase = passphrase;
m_passphrase_abort = entry_abort;
m_passphrase_on_device = enter_on_device;
m_cond_pass.wakeAll();
}

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef MONERO_GUI_PASSPHRASEHELPER_H
#define MONERO_GUI_PASSPHRASEHELPER_H
#include <QtGlobal>
#include <wallet/api/wallet2_api.h>
#include <QMutex>
#include <QPointer>
#include <QWaitCondition>
#include <QMutex>
/**
* Implements component responsible for showing entry prompt to the user,
* typically Wallet / Wallet manager.
*/
class PassprasePrompter {
public:
virtual void onWalletPassphraseNeeded(bool onDevice) = 0;
};
/**
* Implements receiver of the passphrase responsible for passing it back to the wallet,
* typically wallet listener.
*/
class PassphraseReceiver {
public:
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) = 0;
};
class PassphraseHelper {
public:
PassphraseHelper(PassprasePrompter * prompter=nullptr): m_prompter(prompter) {};
PassphraseHelper(const PassphraseHelper & h): PassphraseHelper(h.m_prompter) {};
Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device);
void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort);
private:
PassprasePrompter * m_prompter;
QWaitCondition m_cond_pass;
QMutex m_mutex_pass;
QString m_passphrase;
bool m_passphrase_abort;
bool m_passphrase_on_device;
};
#endif //MONERO_GUI_PASSPHRASEHELPER_H

View File

@@ -34,6 +34,7 @@
#include <QDebug>
#include <QReadLocker>
#include <QWriteLocker>
#include <QtGlobal>
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
@@ -58,7 +59,11 @@ bool TransactionHistory::transaction(int index, std::function<void (TransactionI
void TransactionHistory::refresh(quint32 accountIndex)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay();
#else
QDateTime firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
#endif
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
emit refreshStarted();
@@ -143,7 +148,11 @@ bool TransactionHistory::TransactionHistory::locked() const
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent)
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
m_firstDateTime = QDate(2014, 4, 18).startOfDay();
#else
m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
#endif
m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
}

View File

@@ -61,14 +61,14 @@ quint64 TransactionInfo::atomicAmount() const
QString TransactionInfo::displayAmount() const
{
return WalletManager::instance()->displayAmount(m_amount);
return WalletManager::displayAmount(m_amount);
}
QString TransactionInfo::fee() const
{
if(m_fee == 0)
return "";
return WalletManager::instance()->displayAmount(m_fee);
return WalletManager::displayAmount(m_fee);
}
quint64 TransactionInfo::blockHeight() const
@@ -132,7 +132,7 @@ QString TransactionInfo::destinations_formatted() const
for (auto const& t: m_transfers) {
if (!destinations.isEmpty())
destinations += "<br> ";
destinations += WalletManager::instance()->displayAmount(t->amount()) + ": " + t->address();
destinations += WalletManager::displayAmount(t->amount()) + ": " + t->address();
}
return destinations;
}

View File

@@ -48,7 +48,6 @@
#include <QtConcurrent/QtConcurrent>
#include <QList>
#include <QVector>
#include <QMutex>
#include <QMutexLocker>
namespace {
@@ -59,66 +58,6 @@ namespace {
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
}
class WalletListenerImpl : public Monero::WalletListener
{
public:
WalletListenerImpl(Wallet * w)
: m_wallet(w)
{
}
virtual void moneySpent(const std::string &txId, uint64_t amount) override
{
qDebug() << __FUNCTION__;
emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
}
virtual void moneyReceived(const std::string &txId, uint64_t amount) override
{
qDebug() << __FUNCTION__;
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
}
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override
{
qDebug() << __FUNCTION__;
emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
}
virtual void newBlock(uint64_t height) override
{
// qDebug() << __FUNCTION__;
emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
}
virtual void updated() override
{
emit m_wallet->updated();
}
// called when wallet refreshed by background thread or explicitly
virtual void refreshed() override
{
qDebug() << __FUNCTION__;
emit m_wallet->refreshed();
}
virtual void onDeviceButtonRequest(uint64_t code) override
{
emit m_wallet->deviceButtonRequest(code);
}
virtual void onDeviceButtonPressed() override
{
emit m_wallet->deviceButtonPressed();
}
private:
Wallet * m_wallet;
};
Wallet::Wallet(QObject * parent)
: Wallet(nullptr, parent)
{
@@ -227,12 +166,22 @@ QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const
QString Wallet::path() const
{
return QString::fromStdString(m_walletImpl->path());
return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->path()));
}
bool Wallet::store(const QString &path)
void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */)
{
return m_walletImpl->store(path.toStdString());
const auto future = m_scheduler.run(
[this, path] {
QMutexLocker locker(&m_storeMutex);
return QJSValueList({m_walletImpl->store(path.toStdString())});
},
callback);
if (!future.first)
{
QJSValue(callback).call(QJSValueList({false}));
}
}
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
@@ -290,6 +239,11 @@ bool Wallet::isLedger() const
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Ledger;
}
bool Wallet::isTrezor() const
{
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Trezor;
}
//! create a view only wallet
bool Wallet::createViewOnly(const QString &path, const QString &password) const
{
@@ -600,6 +554,19 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
delete t;
}
void Wallet::estimateTransactionFeeAsync(const QString &destination,
quint64 amount,
PendingTransaction::Priority priority,
const QJSValue &callback)
{
m_scheduler.run([this, destination, amount, priority] {
const uint64_t fee = m_walletImpl->estimateTransactionFee(
{std::make_pair(destination.toStdString(), amount)},
static_cast<Monero::PendingTransaction::Priority>(priority));
return QJSValueList({QString::fromStdString(Monero::Wallet::displayAmount(fee))});
}, callback);
}
TransactionHistory *Wallet::history() const
{
return m_history;
@@ -999,6 +966,19 @@ void Wallet::keyReuseMitigation2(bool mitigation)
m_walletImpl->keyReuseMitigation2(mitigation);
}
void Wallet::onWalletPassphraseNeeded(bool on_device)
{
emit this->walletPassphraseNeeded(on_device);
}
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
{
if (m_walletListener != nullptr)
{
m_walletListener->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
}
}
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
: QObject(parent)
, m_walletImpl(w)

View File

@@ -41,6 +41,8 @@
#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here;
#include "UnsignedTransaction.h"
#include "NetworkType.h"
#include "PassphraseHelper.h"
#include "WalletListenerImpl.h"
namespace Monero {
struct Wallet; // forward declaration
@@ -57,7 +59,7 @@ class SubaddressModel;
class SubaddressAccount;
class SubaddressAccountModel;
class Wallet : public QObject
class Wallet : public QObject, public PassprasePrompter
{
Q_OBJECT
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
@@ -144,7 +146,7 @@ public:
//! saves wallet to the file by given path
//! empty path stores in current location
Q_INVOKABLE bool store(const QString &path = "");
Q_INVOKABLE void storeAsync(const QJSValue &callback, const QString &path = "");
//! initializes wallet asynchronously
Q_INVOKABLE void initAsync(const QString &daemonAddress, bool trustedDaemon = false, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0);
@@ -185,6 +187,7 @@ public:
//! hw-device backed wallets
Q_INVOKABLE bool isHwBacked() const;
Q_INVOKABLE bool isLedger() const;
Q_INVOKABLE bool isTrezor() const;
//! returns if view only wallet
Q_INVOKABLE bool viewOnly() const;
@@ -250,6 +253,11 @@ public:
//! deletes unsigned transaction and frees memory
Q_INVOKABLE void disposeTransaction(UnsignedTransaction * t);
Q_INVOKABLE void estimateTransactionFeeAsync(const QString &destination,
quint64 amount,
PendingTransaction::Priority priority,
const QJSValue &callback);
//! returns transaction history
TransactionHistory * history() const;
@@ -343,6 +351,10 @@ public:
Q_INVOKABLE void segregationHeight(quint64 height);
Q_INVOKABLE void keyReuseMitigation2(bool mitigation);
// Passphrase entry for hardware wallets
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
virtual void onWalletPassphraseNeeded(bool on_device) override;
// TODO: setListenter() when it implemented in API
signals:
// emitted on every event happened with wallet
@@ -362,6 +374,7 @@ signals:
void walletCreationHeightChanged();
void deviceButtonRequest(quint64 buttonCode);
void deviceButtonPressed();
void walletPassphraseNeeded(bool onDevice);
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
void deviceShowAddressShowed();
@@ -427,8 +440,9 @@ private:
bool m_connectionStatusRunning;
QString m_daemonUsername;
QString m_daemonPassword;
Monero::WalletListener *m_walletListener;
WalletListenerImpl *m_walletListener;
FutureScheduler m_scheduler;
QMutex m_storeMutex;
};

View File

@@ -0,0 +1,97 @@
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "WalletListenerImpl.h"
#include "Wallet.h"
WalletListenerImpl::WalletListenerImpl(Wallet * w)
: m_wallet(w)
, m_phelper(w)
{
}
void WalletListenerImpl::moneySpent(const std::string &txId, uint64_t amount)
{
qDebug() << __FUNCTION__;
emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
}
void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount)
{
qDebug() << __FUNCTION__;
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
}
void WalletListenerImpl::unconfirmedMoneyReceived(const std::string &txId, uint64_t amount)
{
qDebug() << __FUNCTION__;
emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
}
void WalletListenerImpl::newBlock(uint64_t height)
{
// qDebug() << __FUNCTION__;
emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
}
void WalletListenerImpl::updated()
{
emit m_wallet->updated();
}
// called when wallet refreshed by background thread or explicitly
void WalletListenerImpl::refreshed()
{
qDebug() << __FUNCTION__;
emit m_wallet->refreshed();
}
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
{
qDebug() << __FUNCTION__;
emit m_wallet->deviceButtonRequest(code);
}
void WalletListenerImpl::onDeviceButtonPressed()
{
qDebug() << __FUNCTION__;
emit m_wallet->deviceButtonPressed();
}
void WalletListenerImpl::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
{
qDebug() << __FUNCTION__;
m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
}
Monero::optional<std::string> WalletListenerImpl::onDevicePassphraseRequest(bool & on_device)
{
qDebug() << __FUNCTION__;
return m_phelper.onDevicePassphraseRequest(on_device);
}

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef MONERO_GUI_WALLETLISTENERIMPL_H
#define MONERO_GUI_WALLETLISTENERIMPL_H
#include "wallet/api/wallet2_api.h"
#include "PassphraseHelper.h"
class Wallet;
class WalletListenerImpl : public Monero::WalletListener, public PassphraseReceiver
{
public:
WalletListenerImpl(Wallet * w);
virtual void moneySpent(const std::string &txId, uint64_t amount) override;
virtual void moneyReceived(const std::string &txId, uint64_t amount) override;
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override;
virtual void newBlock(uint64_t height) override;
virtual void updated() override;
// called when wallet refreshed by background thread or explicitly
virtual void refreshed() override;
virtual void onDeviceButtonRequest(uint64_t code) override;
virtual void onDeviceButtonPressed() override;
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override;
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override;
private:
Wallet * m_wallet;
PassphraseHelper m_phelper;
};
#endif //MONERO_GUI_WALLETLISTENERIMPL_H

View File

@@ -41,10 +41,13 @@
#include <QMutexLocker>
#include <QString>
class WalletPassphraseListenerImpl : public Monero::WalletListener
#include "qt/updater.h"
#include "qt/ScopeGuard.h"
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
{
public:
WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_wallet(nullptr) {}
WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_phelper(mgr) {}
virtual void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
virtual void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
@@ -53,56 +56,35 @@ public:
virtual void updated() override {};
virtual void refreshed() override {};
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool on_device) override
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override
{
qDebug() << __FUNCTION__;
if (on_device) return Monero::optional<std::string>();
m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
}
m_mgr->onWalletPassphraseNeeded(m_wallet);
if (m_mgr->m_passphrase_abort)
{
throw std::runtime_error("Passphrase entry abort");
}
auto tmpPass = m_mgr->m_passphrase.toStdString();
m_mgr->m_passphrase = QString();
return Monero::optional<std::string>(tmpPass);
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override
{
qDebug() << __FUNCTION__;
return m_phelper.onDevicePassphraseRequest(on_device);
}
virtual void onDeviceButtonRequest(uint64_t code) override
{
emit m_mgr->deviceButtonRequest(code);
qDebug() << __FUNCTION__;
emit m_mgr->deviceButtonRequest(code);
}
virtual void onDeviceButtonPressed() override
{
emit m_mgr->deviceButtonPressed();
}
virtual void onSetWallet(Monero::Wallet * wallet) override
{
qDebug() << __FUNCTION__;
m_wallet = wallet;
emit m_mgr->deviceButtonPressed();
}
private:
WalletManager * m_mgr;
Monero::Wallet * m_wallet;
PassphraseHelper m_phelper;
};
WalletManager * WalletManager::m_instance = nullptr;
WalletManager *WalletManager::instance()
{
if (!m_instance) {
m_instance = new WalletManager;
}
return m_instance;
}
Wallet *WalletManager::createWallet(const QString &path, const QString &password,
const QString &language, NetworkType::Type nettype, quint64 kdfRounds)
{
@@ -121,6 +103,13 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
{
QMutexLocker locker(&m_mutex);
WalletPassphraseListenerImpl tmpListener(this);
m_mutex_passphraseReceiver.lock();
m_passphraseReceiver = &tmpListener;
m_mutex_passphraseReceiver.unlock();
const auto cleanup = sg::make_scope_guard([this]() noexcept {
QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
this->m_passphraseReceiver = nullptr;
});
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
@@ -151,14 +140,14 @@ void WalletManager::openWalletAsync(const QString &path, const QString &password
}
Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
Wallet *WalletManager::recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
{
QMutexLocker locker(&m_mutex);
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
delete m_currentWallet;
}
Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", memo.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds);
Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", seed.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds, seed_offset.toStdString());
m_currentWallet = new Wallet(w);
return m_currentWallet;
}
@@ -184,6 +173,13 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
{
QMutexLocker locker(&m_mutex);
WalletPassphraseListenerImpl tmpListener(this);
m_mutex_passphraseReceiver.lock();
m_passphraseReceiver = &tmpListener;
m_mutex_passphraseReceiver.unlock();
const auto cleanup = sg::make_scope_guard([this]() noexcept {
QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
this->m_passphraseReceiver = nullptr;
});
if (m_currentWallet) {
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
@@ -265,7 +261,7 @@ QString WalletManager::maximumAllowedAmountAsString() const
return WalletManager::displayAmount(WalletManager::maximumAllowedAmount());
}
QString WalletManager::displayAmount(quint64 amount) const
QString WalletManager::displayAmount(quint64 amount)
{
return QString::fromStdString(Monero::Wallet::displayAmount(amount));
}
@@ -408,13 +404,27 @@ QVariantMap WalletManager::parse_uri_to_object(const QString &uri) const
if (this->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error)) {
result.insert("address", address);
result.insert("payment_id", payment_id);
result.insert("amount", amount > 0 ? this->displayAmount(amount) : "");
result.insert("amount", amount > 0 ? displayAmount(amount) : "");
result.insert("tx_description", tx_description);
result.insert("recipient_name", recipient_name);
QVariantMap extra_parameters;
if (unknown_parameters.size() > 0)
{
for (const QString &item : unknown_parameters)
{
const auto parsed_item = item.splitRef("=");
if (parsed_item.size() == 2)
{
extra_parameters.insert(parsed_item[0].toString(), parsed_item[1].toString());
}
}
}
result.insert("extra_parameters", extra_parameters);
} else {
result.insert("error", error);
result.insert("error", !error.isEmpty() ? error : tr("Unknown error"));
}
return result;
}
@@ -461,15 +471,43 @@ bool WalletManager::saveQrCode(const QString &code, const QString &path) const
return QRCodeImageProvider::genQrImage(code, &size).scaled(size.expandedTo(QSize(240, 240)), Qt::KeepAspectRatio).save(path, "PNG", 100);
}
void WalletManager::checkUpdatesAsync(const QString &software, const QString &subdir)
void WalletManager::checkUpdatesAsync(
const QString &software,
const QString &subdir,
const QString &buildTag,
const QString &version)
{
m_scheduler.run([this, software, subdir] {
emit checkUpdatesComplete(checkUpdates(software, subdir));
m_scheduler.run([this, software, subdir, buildTag, version] {
const auto updateInfo = Monero::WalletManager::checkUpdates(
software.toStdString(),
subdir.toStdString(),
buildTag.toStdString().c_str(),
version.toStdString().c_str());
if (!std::get<0>(updateInfo))
{
return;
}
const QString version = QString::fromStdString(std::get<1>(updateInfo));
const QByteArray hashFromDns = QByteArray::fromHex(QString::fromStdString(std::get<2>(updateInfo)).toUtf8());
const QString downloadUrl = QString::fromStdString(std::get<4>(updateInfo));
try
{
const QString binaryFilename = QUrl(downloadUrl).fileName();
QPair<QString, QString> signers;
const QString signedHash = Updater().fetchSignedHash(binaryFilename, hashFromDns, signers).toHex();
qInfo() << "Update found" << version << downloadUrl << "hash" << signedHash << "signed by" << signers;
emit checkUpdatesComplete(version, downloadUrl, signedHash, signers.first, signers.second);
}
catch (const std::exception &e)
{
qCritical() << "Failed to fetch and verify signed hash:" << e.what();
}
});
}
QString WalletManager::checkUpdates(const QString &software, const QString &subdir) const
{
qDebug() << "Checking for updates";
@@ -499,6 +537,7 @@ bool WalletManager::clearWalletCache(const QString &wallet_path) const
WalletManager::WalletManager(QObject *parent)
: QObject(parent)
, m_passphraseReceiver(nullptr)
, m_scheduler(this)
{
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
@@ -509,22 +548,16 @@ WalletManager::~WalletManager()
m_scheduler.shutdownWaitForFinished();
}
void WalletManager::onWalletPassphraseNeeded(Monero::Wallet *)
void WalletManager::onWalletPassphraseNeeded(bool on_device)
{
m_mutex_pass.lock();
m_passphrase_abort = false;
emit this->walletPassphraseNeeded();
m_cond_pass.wait(&m_mutex_pass);
m_mutex_pass.unlock();
emit this->walletPassphraseNeeded(on_device);
}
void WalletManager::onPassphraseEntered(const QString &passphrase, bool entry_abort)
void WalletManager::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
{
m_mutex_pass.lock();
m_passphrase = passphrase;
m_passphrase_abort = entry_abort;
m_cond_pass.wakeAll();
m_mutex_pass.unlock();
QMutexLocker locker(&m_mutex_passphraseReceiver);
if (m_passphraseReceiver != nullptr)
{
m_passphraseReceiver->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
}
}

View File

@@ -36,21 +36,24 @@
#include <QMutex>
#include <QPointer>
#include <QWaitCondition>
#include <QMutex>
#include "qt/FutureScheduler.h"
#include "NetworkType.h"
#include "PassphraseHelper.h"
class Wallet;
namespace Monero {
struct WalletManager;
}
class WalletManager : public QObject
class WalletManager : public QObject, public PassprasePrompter
{
Q_OBJECT
Q_PROPERTY(bool connected READ connected)
public:
explicit WalletManager(QObject *parent = 0);
~WalletManager();
enum LogLevel {
LogLevel_Silent = Monero::WalletManagerFactory::LogLevel_Silent,
LogLevel_0 = Monero::WalletManagerFactory::LogLevel_0,
@@ -62,7 +65,6 @@ public:
LogLevel_Max = Monero::WalletManagerFactory::LogLevel_Max,
};
static WalletManager * instance();
// wizard: createWallet path;
Q_INVOKABLE Wallet * createWallet(const QString &path, const QString &password,
const QString &language, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
@@ -83,7 +85,7 @@ public:
Q_INVOKABLE void openWalletAsync(const QString &path, const QString &password, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
// wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = ""
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &memo,
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset,
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
@@ -129,7 +131,7 @@ public:
Q_INVOKABLE QString errorString() const;
//! since we can't call static method from QML, move it to this class
Q_INVOKABLE QString displayAmount(quint64 amount) const;
Q_INVOKABLE static QString displayAmount(quint64 amount);
Q_INVOKABLE quint64 amountFromString(const QString &amount) const;
Q_INVOKABLE quint64 amountFromDouble(double amount) const;
Q_INVOKABLE quint64 maximumAllowedAmount() const;
@@ -176,44 +178,46 @@ public:
Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error) const;
Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const;
Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const;
Q_INVOKABLE void checkUpdatesAsync(const QString &software, const QString &subdir);
Q_INVOKABLE void checkUpdatesAsync(
const QString &software,
const QString &subdir,
const QString &buildTag,
const QString &version);
Q_INVOKABLE QString checkUpdates(const QString &software, const QString &subdir) const;
// clear/rename wallet cache
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
Q_INVOKABLE void onWalletPassphraseNeeded(Monero::Wallet * wallet);
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool entry_abort=false);
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
virtual void onWalletPassphraseNeeded(bool on_device) override;
signals:
void walletOpened(Wallet * wallet);
void walletCreated(Wallet * wallet);
void walletPassphraseNeeded();
void walletPassphraseNeeded(bool onDevice);
void deviceButtonRequest(quint64 buttonCode);
void deviceButtonPressed();
void checkUpdatesComplete(const QString &result) const;
void checkUpdatesComplete(
const QString &version,
const QString &downloadUrl,
const QString &hash,
const QString &firstSigner,
const QString &secondSigner) const;
void miningStatus(bool isMining) const;
public slots:
private:
friend class WalletPassphraseListenerImpl;
explicit WalletManager(QObject *parent = 0);
~WalletManager();
bool isMining() const;
static WalletManager * m_instance;
Monero::WalletManager * m_pimpl;
mutable QMutex m_mutex;
QPointer<Wallet> m_currentWallet;
QWaitCondition m_cond_pass;
QMutex m_mutex_pass;
QString m_passphrase;
bool m_passphrase_abort;
PassphraseReceiver * m_passphraseReceiver;
QMutex m_mutex_passphraseReceiver;
FutureScheduler m_scheduler;
};

View File

@@ -30,7 +30,6 @@
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QStandardPaths>
#include <QNetworkAccessManager>
#include <QIcon>
#include <QDebug>
#include <QDesktopServices>
@@ -39,6 +38,9 @@
#include <QScreen>
#include <QRegExp>
#include <QThread>
#include <version.h>
#include "clipboardAdapter.h"
#include "filter.h"
#include "oscursor.h"
@@ -62,18 +64,25 @@
#include "wallet/api/wallet2_api.h"
#include "Logger.h"
#include "MainApp.h"
#include "qt/downloader.h"
#include "qt/ipc.h"
#include "qt/network.h"
#include "qt/updater.h"
#include "qt/utils.h"
#include "qt/TailsOS.h"
#include "qt/KeysFiles.h"
#include "qt/MoneroSettings.h"
#include "qt/prices.h"
#include "qt/NetworkAccessBlockingFactory.h"
// IOS exclusions
#ifndef Q_OS_IOS
#include "daemon/DaemonManager.h"
#endif
#if defined(Q_OS_WIN)
#include <QOpenGLContext>
#endif
#ifdef WITH_SCANNER
#include "QR-Code-scanner/QrCodeScanner.h"
#endif
@@ -87,13 +96,12 @@
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin);
#elif defined(Q_OS_LINUX)
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
Q_IMPORT_PLUGIN(QXcbGlxIntegrationPlugin);
#endif
Q_IMPORT_PLUGIN(QSvgIconPlugin)
Q_IMPORT_PLUGIN(QGifPlugin)
Q_IMPORT_PLUGIN(QICNSPlugin)
Q_IMPORT_PLUGIN(QICOPlugin)
Q_IMPORT_PLUGIN(QJpegPlugin)
Q_IMPORT_PLUGIN(QMngPlugin)
Q_IMPORT_PLUGIN(QSvgPlugin)
Q_IMPORT_PLUGIN(QTgaPlugin)
Q_IMPORT_PLUGIN(QTiffPlugin)
@@ -125,7 +133,9 @@ Q_IMPORT_PLUGIN(QtQuick2PrivateWidgetsPlugin)
Q_IMPORT_PLUGIN(QtQuickControls2Plugin)
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin)
Q_IMPORT_PLUGIN(QmlXmlListModelPlugin)
#ifdef WITH_SCANNER
Q_IMPORT_PLUGIN(QMultimediaDeclarativeModule)
#endif
#endif
@@ -140,6 +150,8 @@ bool isOpenGL = true;
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(translations);
// platform dependant settings
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS)
bool isDesktop = true;
@@ -178,6 +190,17 @@ int main(int argc, char *argv[])
MainApp app(argc, argv);
#if defined(Q_OS_WIN)
if (isOpenGL)
{
QOpenGLContext ctx;
isOpenGL = ctx.create() && ctx.format().version() >= qMakePair(2, 1);
if (!isOpenGL) {
qputenv("QMLSCENE_DEVICE", "softwarecontext");
}
}
#endif
app.setApplicationName("monero-core");
app.setOrganizationDomain("getmonero.org");
app.setOrganizationName("monero-project");
@@ -209,6 +232,7 @@ int main(int argc, char *argv[])
qCritical() << "Error: accounts root directory could not be set";
return 1;
}
moneroAccountsDir = QDir::toNativeSeparators(moneroAccountsDir);
#if defined(Q_OS_LINUX)
if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico"));
@@ -222,6 +246,16 @@ int main(int argc, char *argv[])
QCoreApplication::translate("main", "Log to specified file"),
QCoreApplication::translate("main", "file"));
QCommandLineOption verifyUpdateOption("verify-update", "\
Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by two maintainers.\n\
* Requires 'hashes.txt' - signed 'shasum' output \
(i.e. 'gpg -o hashes.txt --clear-sign <shasum_output>') generated by a maintainer.\n\
* Requires 'hashes.txt.sig' - detached signature of 'hashes.txt' \
(i.e. 'gpg -b hashes.txt') generated by another maintainer.", "update-binary");
parser.addOption(verifyUpdateOption);
QCommandLineOption disableCheckUpdatesOption("disable-check-updates", "Disable automatic check for updates.");
parser.addOption(disableCheckUpdatesOption);
QCommandLineOption testQmlOption("test-qml");
testQmlOption.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(logPathOption);
@@ -232,7 +266,7 @@ int main(int argc, char *argv[])
Monero::Utils::onStartup();
// Log settings
const QString logPath = getLogPath(parser.value(logPathOption));
const QString logPath = QDir::toNativeSeparators(getLogPath(parser.value(logPathOption)));
Monero::Wallet::init(argv[0], "monero-wallet-gui", logPath.toStdString().c_str(), true);
qInstallMessageHandler(messageHandler);
@@ -245,6 +279,32 @@ int main(int argc, char *argv[])
}
qWarning().noquote() << "app startd" << "(log: " + logPath + ")";
if (parser.isSet(verifyUpdateOption))
{
const QString updateBinaryFullPath = parser.value(verifyUpdateOption);
const QFileInfo updateBinaryInfo(updateBinaryFullPath);
const QString updateBinaryDir = QDir::toNativeSeparators(updateBinaryInfo.absolutePath()) + QDir::separator();
const QString hashesTxt = updateBinaryDir + "hashes.txt";
const QString hashesTxtSig = hashesTxt + ".sig";
try
{
const QByteArray updateBinaryContents = fileGetContents(updateBinaryFullPath);
const QPair<QString, QString> signers = Updater().verifySignaturesAndHashSum(
fileGetContents(hashesTxt),
fileGetContents(hashesTxtSig),
updateBinaryInfo.fileName(),
updateBinaryContents.data(),
updateBinaryContents.size());
qCritical() << "successfully verified, signed by" << signers.first << "and" << signers.second;
return 0;
}
catch (const std::exception &e)
{
qCritical() << e.what();
}
return 1;
}
// Desktop entry
#ifdef Q_OS_LINUX
registerXdgMime(app);
@@ -296,6 +356,9 @@ int main(int argc, char *argv[])
// registering types for QML
qmlRegisterType<clipboardAdapter>("moneroComponents.Clipboard", 1, 0, "Clipboard");
qmlRegisterType<Downloader>("moneroComponents.Downloader", 1, 0, "Downloader");
qmlRegisterType<WalletKeysFilesModel>("moneroComponents.WalletKeysFilesModel", 1, 0, "WalletKeysFilesModel");
qmlRegisterType<WalletManager>("moneroComponents.WalletManager", 1, 0, "WalletManager");
// Temporary Qt.labs.settings replacement
qmlRegisterType<MoneroSettings>("moneroComponents.Settings", 1, 0, "MoneroSettings");
@@ -309,15 +372,9 @@ int main(int argc, char *argv[])
qmlRegisterUncreatableType<UnsignedTransaction>("moneroComponents.UnsignedTransaction", 1, 0, "UnsignedTransaction",
"UnsignedTransaction can't be instantiated directly");
qmlRegisterUncreatableType<WalletManager>("moneroComponents.WalletManager", 1, 0, "WalletManager",
"WalletManager can't be instantiated directly");
qmlRegisterUncreatableType<TranslationManager>("moneroComponents.TranslationManager", 1, 0, "TranslationManager",
"TranslationManager can't be instantiated directly");
qmlRegisterUncreatableType<WalletKeysFilesModel>("moneroComponents.walletKeysFilesModel", 1, 0, "WalletKeysFilesModel",
"walletKeysFilesModel can't be instantiated directly");
qmlRegisterUncreatableType<TransactionHistoryModel>("moneroComponents.TransactionHistoryModel", 1, 0, "TransactionHistoryModel",
"TransactionHistoryModel can't be instantiated directly");
@@ -364,6 +421,9 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine;
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
engine.setNetworkAccessManagerFactory(new NetworkAccessBlockingFactory);
#endif
OSCursor cursor;
engine.rootContext()->setContextProperty("globalCursor", &cursor);
OSHelper osHelper;
@@ -373,10 +433,6 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("moneroAccountsDir", moneroAccountsDir);
WalletManager *walletManager = WalletManager::instance();
engine.rootContext()->setContextProperty("walletManager", walletManager);
engine.rootContext()->setContextProperty("translationManager", TranslationManager::instance());
engine.addImageProvider(QLatin1String("qrcode"), new QRCodeImageProvider());
@@ -414,11 +470,6 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("desktopFolder", desktopFolder);
#endif
// Wallet .keys files model (wizard -> open wallet)
WalletKeysFilesModel walletKeysFilesModel(walletManager);
engine.rootContext()->setContextProperty("walletKeysFilesModel", &walletKeysFilesModel);
engine.rootContext()->setContextProperty("walletKeysFilesModelProxy", &walletKeysFilesModel.proxyModel());
// Get default account name
QString accountName = qgetenv("USER"); // mac/linux
if (accountName.isEmpty())
@@ -430,6 +481,7 @@ int main(int argc, char *argv[])
engine.rootContext()->setContextProperty("homePath", QDir::homePath());
engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount());
engine.rootContext()->setContextProperty("disableCheckUpdatesFlag", parser.isSet(disableCheckUpdatesOption));
bool builtWithScanner = false;
#ifdef WITH_SCANNER
@@ -437,9 +489,10 @@ int main(int argc, char *argv[])
#endif
engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner);
QNetworkAccessManager *manager = new QNetworkAccessManager();
Prices prices(manager);
engine.rootContext()->setContextProperty("Prices", &prices);
engine.rootContext()->setContextProperty("moneroVersion", MONERO_VERSION_FULL);
Network network;
engine.rootContext()->setContextProperty("Network", &network);
// Load main window (context properties needs to be defined obove this line)
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

View File

@@ -27,6 +27,9 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "oshelper.h"
#include <QCoreApplication>
#include <QFileDialog>
#include <QStandardPaths>
#include <QTemporaryFile>
#include <QDir>
#include <QDebug>
@@ -82,6 +85,11 @@ OSHelper::OSHelper(QObject *parent) : QObject(parent)
}
QString OSHelper::downloadLocation() const
{
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
}
bool OSHelper::openContainingFolder(const QString &filePath) const
{
#if defined(Q_OS_WIN)
@@ -105,6 +113,12 @@ bool OSHelper::openContainingFolder(const QString &filePath) const
return QDesktopServices::openUrl(url);
}
QString OSHelper::openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const
{
const QString hint = (folder.isEmpty() ? "" : folder + QDir::separator()) + filename;
return QFileDialog::getSaveFileName(nullptr, title, hint);
}
QString OSHelper::temporaryFilename() const
{
QString tempFileName;
@@ -151,3 +165,47 @@ QString OSHelper::temporaryPath() const
{
return QDir::tempPath();
}
bool OSHelper::installed() const
{
#ifdef Q_OS_WIN
static constexpr const wchar_t installKey[] =
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Monero GUI Wallet_is1";
static constexpr const wchar_t installValue[] = L"InstallLocation";
DWORD size;
LSTATUS status =
::RegGetValueW(HKEY_LOCAL_MACHINE, installKey, installValue, RRF_RT_REG_SZ, nullptr, nullptr, &size);
if (status == ERROR_FILE_NOT_FOUND)
{
return false;
}
if (status != ERROR_SUCCESS)
{
qCritical() << "RegGetValueW failed (get size)" << status;
return false;
}
std::wstring installLocation;
installLocation.resize(size / sizeof(std::wstring::value_type));
size = installLocation.size() * sizeof(std::wstring::value_type);
status = ::RegGetValueW(
HKEY_LOCAL_MACHINE,
installKey,
installValue,
RRF_RT_REG_SZ,
nullptr,
&installLocation[0],
&size);
if (status != ERROR_SUCCESS)
{
qCritical() << "RegGetValueW Failed (read)" << status;
return false;
}
const QDir installDir(QString(reinterpret_cast<const QChar *>(&installLocation[0])));
return installDir == QDir(QCoreApplication::applicationDirPath());
#else
return false;
#endif
}

View File

@@ -36,15 +36,22 @@
class OSHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(bool installed READ installed CONSTANT);
public:
explicit OSHelper(QObject *parent = 0);
Q_INVOKABLE QString downloadLocation() const;
Q_INVOKABLE bool openContainingFolder(const QString &filePath) const;
Q_INVOKABLE QString openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const;
Q_INVOKABLE QString temporaryFilename() const;
Q_INVOKABLE QString temporaryPath() const;
Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const;
Q_INVOKABLE bool isCapsLock() const;
private:
bool installed() const;
signals:
public slots:

View File

@@ -30,6 +30,7 @@
#include "TransactionHistoryModel.h"
#include <QDebug>
#include <QtGlobal>
namespace {
/**
@@ -204,9 +205,14 @@ bool TransactionHistorySortFilterModel::filterAcceptsRow(int source_row, const Q
break;
case TransactionHistoryModel::TransactionTimeStampRole:
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
QDateTime from = dateFromFilter().startOfDay();
QDateTime to = dateToFilter().endOfDay();
#else
QDateTime from = QDateTime(dateFromFilter());
QDateTime to = QDateTime(dateToFilter());
to = to.addDays(1); // including upperbound
#endif
QDateTime timestamp = data.toDateTime();
bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from;
bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to;

View File

@@ -0,0 +1,18 @@
file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE HEADERS *.h)
find_library(GCRYPT_LIBRARY gcrypt)
find_library(GPG_ERROR_LIBRARY gpg-error)
add_library(openpgp
${SOURCES}
${HEADERS})
target_include_directories(openpgp
PUBLIC
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include)
target_link_libraries(openpgp
PUBLIC
${GCRYPT_LIBRARY}
${GPG_ERROR_LIBRARY})

107
src/openpgp/hash.h Normal file
View File

@@ -0,0 +1,107 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <vector>
#include <gcrypt.h>
#include <span.h>
namespace openpgp
{
class hash
{
public:
enum algorithm : uint8_t
{
sha256 = 8,
};
hash(const hash &) = delete;
hash &operator=(const hash &) = delete;
hash(uint8_t algorithm)
: algo(algorithm)
, consumed(0)
{
if (gcry_md_open(&md, algo, 0) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to create message digest object");
}
}
~hash()
{
gcry_md_close(md);
}
hash &operator<<(uint8_t byte)
{
gcry_md_putc(md, byte);
++consumed;
return *this;
}
hash &operator<<(const epee::span<const uint8_t> &bytes)
{
gcry_md_write(md, &bytes[0], bytes.size());
consumed += bytes.size();
return *this;
}
hash &operator<<(const std::vector<uint8_t> &bytes)
{
return *this << epee::to_span(bytes);
}
std::vector<uint8_t> finish() const
{
std::vector<uint8_t> result(gcry_md_get_algo_dlen(algo));
const void *digest = gcry_md_read(md, algo);
if (digest == nullptr)
{
throw std::runtime_error("failed to read the digest");
}
memcpy(&result[0], digest, result.size());
return result;
}
size_t consumed_bytes() const
{
return consumed;
}
private:
const uint8_t algo;
gcry_md_hd_t md;
size_t consumed;
};
}

78
src/openpgp/mpi.h Normal file
View File

@@ -0,0 +1,78 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <gcrypt.h>
namespace openpgp
{
class mpi
{
public:
mpi(const mpi &) = delete;
mpi &operator=(const mpi &) = delete;
mpi(mpi &&other)
: data(other.data)
{
other.data = nullptr;
}
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
mpi(const byte_container &buffer, gcry_mpi_format format = GCRYMPI_FMT_USG)
: mpi(&buffer[0], buffer.size(), format)
{
}
mpi(const void *buffer, size_t size, gcry_mpi_format format = GCRYMPI_FMT_USG)
{
if (gcry_mpi_scan(&data, format, buffer, size, nullptr) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to read mpi from buffer");
}
}
~mpi()
{
gcry_mpi_release(data);
}
const gcry_mpi_t &get() const
{
return data;
}
private:
gcry_mpi_t data;
};
} // namespace openpgp

382
src/openpgp/openpgp.cpp Normal file
View File

@@ -0,0 +1,382 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "openpgp.h"
#include <algorithm>
#include <locale>
#include <vector>
#include <string_coding.h>
#include "hash.h"
#include "mpi.h"
#include "packet_stream.h"
#include "s_expression.h"
#include "serialization.h"
namespace openpgp
{
namespace
{
std::string::const_iterator find_next_line(std::string::const_iterator begin, const std::string::const_iterator &end)
{
begin = std::find(begin, end, '\n');
return begin != end ? ++begin : end;
}
std::string::const_iterator find_line_starting_with(
std::string::const_iterator it,
const std::string::const_iterator &end,
const std::string &starts_with)
{
for (std::string::const_iterator next_line; it != end; it = next_line)
{
next_line = find_next_line(it, end);
const size_t line_length = static_cast<size_t>(std::distance(it, next_line));
if (line_length >= starts_with.size() && std::equal(starts_with.begin(), starts_with.end(), it))
{
return it;
}
}
return end;
}
std::string::const_iterator find_empty_line(std::string::const_iterator it, const std::string::const_iterator &end)
{
for (; it != end && *it != '\r' && *it != '\n'; it = find_next_line(it, end))
{
}
return it;
}
std::string get_armored_block_contents(const std::string &text, const std::string &block_name)
{
static constexpr const char dashes[] = "-----";
const std::string armor_header = dashes + block_name + dashes;
auto block_start = find_line_starting_with(text.begin(), text.end(), armor_header);
auto block_headers = find_next_line(block_start, text.end());
auto block_end = find_line_starting_with(block_headers, text.end(), dashes);
auto contents_begin = find_next_line(find_empty_line(block_headers, block_end), block_end);
if (contents_begin == block_end)
{
throw std::runtime_error("armored block not found");
}
return std::string(contents_begin, block_end);
}
} // namespace
public_key_rsa::public_key_rsa(s_expression expression, size_t bits)
: m_expression(std::move(expression))
, m_bits(bits)
{
}
const gcry_sexp_t &public_key_rsa::get() const
{
return m_expression.get();
}
size_t public_key_rsa::bits() const
{
return m_bits;
}
public_key_block::public_key_block(const std::string &armored)
: public_key_block(epee::to_byte_span(epee::to_span(epee::string_encoding::base64_decode(
strip_line_breaks(get_armored_block_contents(armored, "BEGIN PGP PUBLIC KEY BLOCK"))))))
{
}
// TODO: Public-Key expiration, User ID and Public-Key certification, Subkey binding checks
public_key_block::public_key_block(const epee::span<const uint8_t> buffer)
{
packet_stream packets(buffer);
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::user_id);
if (data == nullptr)
{
throw std::runtime_error("user id is missing");
}
m_user_id.assign(data->begin(), data->end());
const auto append_public_key = [this](const std::vector<uint8_t> &data) {
deserializer<std::vector<uint8_t>> serialized(data);
const auto version = serialized.read_big_endian<uint8_t>();
if (version != 4)
{
throw std::runtime_error("unsupported public key version");
}
/* const auto timestamp = */ serialized.read_big_endian<uint32_t>();
const auto algorithm = serialized.read_big_endian<uint8_t>();
if (algorithm != openpgp::algorithm::rsa)
{
throw std::runtime_error("unsupported public key algorithm");
}
{
const mpi public_key_n = serialized.read_mpi();
const mpi public_key_e = serialized.read_mpi();
emplace_back(
s_expression("(public-key (rsa (n %m) (e %m)))", public_key_n.get(), public_key_e.get()),
gcry_mpi_get_nbits(public_key_n.get()));
}
};
data = packets.find_first(packet_tag::type::public_key);
if (data == nullptr)
{
throw std::runtime_error("public key is missing");
}
append_public_key(*data);
packets.for_each(packet_tag::type::public_subkey, append_public_key);
}
std::string public_key_block::user_id() const
{
return m_user_id;
}
// TODO: Signature expiration check
signature_rsa::signature_rsa(
uint8_t algorithm,
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
type type,
s_expression signature,
uint8_t version)
: m_hash_algorithm(hash_algorithm)
, m_hash_leftmost_bytes(hash_leftmost_bytes)
, m_hashed_appendix(format_hashed_appendix(algorithm, hash_algorithm, hashed_data, type, version))
, m_signature(std::move(signature))
, m_type(type)
{
}
signature_rsa signature_rsa::from_armored(const std::string &armored_signed_message)
{
return from_base64(get_armored_block_contents(armored_signed_message, "BEGIN PGP SIGNATURE"));
}
signature_rsa signature_rsa::from_base64(const std::string &base64)
{
std::string decoded = epee::string_encoding::base64_decode(strip_line_breaks(base64));
epee::span<const uint8_t> buffer(reinterpret_cast<const uint8_t *>(&decoded[0]), decoded.size());
return from_buffer(buffer);
}
signature_rsa signature_rsa::from_buffer(const epee::span<const uint8_t> input)
{
packet_stream packets(input);
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::signature);
if (data == nullptr)
{
throw std::runtime_error("signature is missing");
}
deserializer<std::vector<uint8_t>> buffer(*data);
const auto version = buffer.read_big_endian<uint8_t>();
if (version != 4)
{
throw std::runtime_error("unsupported signature version");
}
const auto signature_type = static_cast<type>(buffer.read_big_endian<uint8_t>());
const auto algorithm = buffer.read_big_endian<uint8_t>();
if (algorithm != openpgp::algorithm::rsa)
{
throw std::runtime_error("unsupported signature algorithm");
}
const auto hash_algorithm = buffer.read_big_endian<uint8_t>();
const auto hashed_data_length = buffer.read_big_endian<uint16_t>();
std::vector<uint8_t> hashed_data = buffer.read(hashed_data_length);
const auto unhashed_data_length = buffer.read_big_endian<uint16_t>();
buffer.read_span(unhashed_data_length);
std::pair<uint8_t, uint8_t> hash_leftmost_bytes{buffer.read_big_endian<uint8_t>(), buffer.read_big_endian<uint8_t>()};
const mpi signature = buffer.read_mpi();
return signature_rsa(
algorithm,
std::move(hash_leftmost_bytes),
hash_algorithm,
hashed_data,
signature_type,
s_expression("(sig-val (rsa (s %m)))", signature.get()),
version);
}
bool signature_rsa::verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const
{
const s_expression signed_data = hash_message(message, public_key.bits());
return gcry_pk_verify(m_signature.get(), signed_data.get(), public_key.get()) == 0;
}
s_expression signature_rsa::hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const
{
switch (m_type)
{
case type::binary_document:
return hash_bytes(message, public_key_bits);
case type::canonical_text_document:
{
std::vector<uint8_t> crlf_formatted;
crlf_formatted.reserve(message.size());
const size_t message_size = message.size();
for (size_t offset = 0; offset < message_size; ++offset)
{
const auto &character = message[offset];
if (character == '\r')
{
continue;
}
if (character == '\n')
{
const bool skip_last_crlf = offset + 1 == message_size;
if (skip_last_crlf)
{
break;
}
crlf_formatted.push_back('\r');
}
crlf_formatted.push_back(character);
}
return hash_bytes(epee::to_span(crlf_formatted), public_key_bits);
}
default:
throw std::runtime_error("unsupported signature type");
}
}
std::vector<uint8_t> signature_rsa::hash_asn_object_id() const
{
size_t size;
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, nullptr, &size) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID) size");
}
std::vector<uint8_t> asn_object_id(size);
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, &asn_object_id[0], &size) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID)");
}
return asn_object_id;
}
s_expression signature_rsa::hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const
{
const std::vector<uint8_t> plain_hash = (hash(m_hash_algorithm) << message << m_hashed_appendix).finish();
if (plain_hash.size() < 2)
{
throw std::runtime_error("insufficient message hash size");
}
if (plain_hash[0] != m_hash_leftmost_bytes.first || plain_hash[1] != m_hash_leftmost_bytes.second)
{
throw std::runtime_error("signature checksum doesn't match the expected value");
}
std::vector<uint8_t> asn_object_id = hash_asn_object_id();
const size_t public_key_bytes = bits_to_bytes(public_key_bits);
if (public_key_bytes < plain_hash.size() + asn_object_id.size() + 11)
{
throw std::runtime_error("insufficient public key bit length");
}
std::vector<uint8_t> emsa_pkcs1_v1_5_encoded;
emsa_pkcs1_v1_5_encoded.reserve(public_key_bytes);
emsa_pkcs1_v1_5_encoded.push_back(0);
emsa_pkcs1_v1_5_encoded.push_back(1);
const size_t ps_size = public_key_bytes - plain_hash.size() - asn_object_id.size() - 3;
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), ps_size, 0xff);
emsa_pkcs1_v1_5_encoded.push_back(0);
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), asn_object_id.begin(), asn_object_id.end());
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), plain_hash.begin(), plain_hash.end());
mpi value(emsa_pkcs1_v1_5_encoded);
return s_expression("(data (flags raw) (value %m))", value.get());
}
std::vector<uint8_t> signature_rsa::format_hashed_appendix(
uint8_t algorithm,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
uint8_t type,
uint8_t version)
{
const uint16_t hashed_data_size = static_cast<uint16_t>(hashed_data.size());
const uint32_t hashed_pefix_size = sizeof(version) + sizeof(type) + sizeof(algorithm) + sizeof(hash_algorithm) +
sizeof(hashed_data_size) + hashed_data.size();
std::vector<uint8_t> appendix;
appendix.reserve(hashed_pefix_size + sizeof(version) + sizeof(uint8_t) + sizeof(hashed_pefix_size));
appendix.push_back(version);
appendix.push_back(type);
appendix.push_back(algorithm);
appendix.push_back(hash_algorithm);
appendix.push_back(static_cast<uint8_t>(hashed_data_size >> 8));
appendix.push_back(static_cast<uint8_t>(hashed_data_size));
appendix.insert(appendix.end(), hashed_data.begin(), hashed_data.end());
appendix.push_back(version);
appendix.push_back(0xff);
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 24));
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 16));
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 8));
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size));
return appendix;
}
message_armored::message_armored(const std::string &message_armored)
: m_message(get_armored_block_contents(message_armored, "BEGIN PGP SIGNED MESSAGE"))
{
}
message_armored::operator epee::span<const uint8_t>() const
{
return epee::to_byte_span(epee::to_span(m_message));
}
} // namespace openpgp

127
src/openpgp/openpgp.h Normal file
View File

@@ -0,0 +1,127 @@
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <vector>
#include <gcrypt.h>
#include <span.h>
#include "s_expression.h"
namespace openpgp
{
enum algorithm : uint8_t
{
rsa = 1,
};
class public_key_rsa
{
public:
public_key_rsa(s_expression expression, size_t bits);
size_t bits() const;
const gcry_sexp_t &get() const;
private:
s_expression m_expression;
size_t m_bits;
};
class public_key_block : public std::vector<public_key_rsa>
{
public:
public_key_block(const std::string &armored);
public_key_block(const epee::span<const uint8_t> buffer);
std::string user_id() const;
private:
std::string m_user_id;
};
class signature_rsa
{
public:
enum type : uint8_t
{
binary_document = 0,
canonical_text_document = 1,
};
signature_rsa(
uint8_t algorithm,
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
type type,
s_expression signature,
uint8_t version);
static signature_rsa from_armored(const std::string &armored_signed_message);
static signature_rsa from_base64(const std::string &base64);
static signature_rsa from_buffer(const epee::span<const uint8_t> input);
bool verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const;
private:
s_expression hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const;
std::vector<uint8_t> hash_asn_object_id() const;
s_expression hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const;
static std::vector<uint8_t> format_hashed_appendix(
uint8_t algorithm,
uint8_t hash_algorithm,
const std::vector<uint8_t> &hashed_data,
uint8_t type,
uint8_t version);
private:
uint8_t m_hash_algorithm;
std::pair<uint8_t, uint8_t> m_hash_leftmost_bytes;
std::vector<uint8_t> m_hashed_appendix;
s_expression m_signature;
type m_type;
};
class message_armored
{
public:
message_armored(const std::string &message_armored);
operator epee::span<const uint8_t>() const;
private:
std::string m_message;
};
} // namespace openpgp

View File

@@ -1,21 +1,21 @@
// Copyright (c) 2017-2018, The Monero Project
//
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -26,60 +26,63 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.9
import QtQuick.Controls 1.4
import moneroComponents.Wallet 1.0
import "." as MoneroComponents
#pragma once
Item {
id: item
property string message: ""
property bool active: false
height: 180
width: 320
property int margin: 15
x: parent.width - width - margin
y: parent.height - height * scale.yScale - margin * scale.yScale
#include <vector>
Rectangle {
color: "#FF6C3C"
border.color: "black"
anchors.fill: parent
#include <span.h>
TextArea {
id:versionText
readOnly: true
backgroundVisible: false
textFormat: TextEdit.AutoText
anchors.fill: parent
font.family: MoneroComponents.Style.fontRegular.name
font.pixelSize: 12
textMargin: 20
textColor: "white"
text: item.message
wrapMode: Text.WrapAnywhere
}
#include "serialization.h"
namespace openpgp
{
class packet_stream
{
public:
packet_stream(const epee::span<const uint8_t> buffer)
: packet_stream(deserializer<epee::span<const uint8_t>>(buffer))
{
}
template <
typename byte_container,
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
packet_stream(deserializer<byte_container> buffer)
{
while (!buffer.empty())
{
packet_tag tag = buffer.read_packet_tag();
packets.push_back({std::move(tag), buffer.read(tag.length)});
}
}
transform: Scale {
id: scale
yScale: item.active ? 1 : 0
Behavior on yScale {
NumberAnimation { duration: 500; easing.type: Easing.InOutCubic }
}
const std::vector<uint8_t> *find_first(packet_tag::type type) const
{
for (const auto &packet : packets)
{
if (packet.first.packet_type == type)
{
return &packet.second;
}
}
return nullptr;
}
Timer {
id: hider
interval: 30000; running: false; repeat: false
onTriggered: { item.active = false }
template <typename Callback>
void for_each(packet_tag::type type, Callback &callback) const
{
for (const auto &packet : packets)
{
if (packet.first.packet_type == type)
{
callback(packet.second);
}
}
}
function show(message) {
item.visible = true
item.message = message
item.active = true
hider.running = true
}
}
private:
std::vector<std::pair<packet_tag, std::vector<uint8_t>>> packets;
};
} // namespace openpgp

View File

@@ -1,21 +1,21 @@
// Copyright (c) 2014-2019, The Monero Project
//
// Copyright (c) 2020, The Monero Project
//
// All rights reserved.
//
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
@@ -26,26 +26,53 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import QtQuick 2.9
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.0
#pragma once
import "../components" as MoneroComponents
#include <algorithm>
#include <stdexcept>
Rectangle {
property bool active: false
Layout.preferredWidth: 30
Layout.fillHeight: true
property string activeColor: MoneroComponents.Style.defaultFontColor
property string inactiveColor: MoneroComponents.Style.progressBarBackgroundColor
color: "transparent"
#include <gcrypt.h>
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
width: 10
height: 10
radius: 10
color: parent.active ? parent.activeColor : parent.inactiveColor
namespace openpgp
{
class s_expression
{
public:
s_expression(const s_expression &) = delete;
s_expression &operator=(const s_expression &) = delete;
template <typename... Args>
s_expression(Args... args)
{
if (gcry_sexp_build(&data, nullptr, args...) != GPG_ERR_NO_ERROR)
{
throw std::runtime_error("failed to build S-expression");
}
}
}
s_expression(s_expression &&other)
{
std::swap(data, other.data);
}
s_expression(gcry_sexp_t data)
: data(data)
{
}
~s_expression()
{
gcry_sexp_release(data);
}
const gcry_sexp_t &get() const
{
return data;
}
private:
gcry_sexp_t data = nullptr;
};
} // namespace openpgp

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