Browse Source

Merge pull request #521 from laanwj/qt

Qt GUI
miguelfreitas
Gavin Andresen 13 years ago
parent
commit
f7f2a36925
  1. 13
      .gitignore
  2. 1
      README.md
  3. 185
      bitcoin-qt.pro
  4. 43
      contrib/miniupnpc/Portfile
  5. 73
      doc/assets-attribution.txt
  6. 162
      doc/readme-qt.rst
  7. 63
      scripts/qt/extract_strings_qt.py
  8. BIN
      scripts/qt/img/reload.xcf
  9. 43
      scripts/qt/make_spinner.py
  10. 9
      scripts/qt/make_windows_icon.py
  11. 4
      src/bitcoinrpc.cpp
  12. 0
      src/bitcoinrpc.h
  13. 4
      src/headers.h
  14. 18
      src/init.cpp
  15. 11
      src/main.cpp
  16. 1
      src/main.h
  17. 4
      src/makefile.linux-mingw
  18. 4
      src/makefile.mingw
  19. 4
      src/makefile.osx
  20. 4
      src/makefile.unix
  21. 2
      src/makefile.vc
  22. 4
      src/noui.h
  23. 26
      src/qt/aboutdialog.cpp
  24. 27
      src/qt/aboutdialog.h
  25. 215
      src/qt/addressbookpage.cpp
  26. 59
      src/qt/addressbookpage.h
  27. 347
      src/qt/addresstablemodel.cpp
  28. 83
      src/qt/addresstablemodel.h
  29. 186
      src/qt/askpassphrasedialog.cpp
  30. 40
      src/qt/askpassphrasedialog.h
  31. 170
      src/qt/bitcoin.cpp
  32. 48
      src/qt/bitcoin.qrc
  33. 67
      src/qt/bitcoinaddressvalidator.cpp
  34. 24
      src/qt/bitcoinaddressvalidator.h
  35. 178
      src/qt/bitcoinamountfield.cpp
  36. 59
      src/qt/bitcoinamountfield.h
  37. 613
      src/qt/bitcoingui.cpp
  38. 120
      src/qt/bitcoingui.h
  39. 217
      src/qt/bitcoinstrings.cpp
  40. 181
      src/qt/bitcoinunits.cpp
  41. 57
      src/qt/bitcoinunits.h
  42. 83
      src/qt/clientmodel.cpp
  43. 60
      src/qt/clientmodel.h
  44. 83
      src/qt/csvmodelwriter.cpp
  45. 43
      src/qt/csvmodelwriter.h
  46. 115
      src/qt/editaddressdialog.cpp
  47. 47
      src/qt/editaddressdialog.h
  48. 162
      src/qt/forms/aboutdialog.ui
  49. 130
      src/qt/forms/addressbookpage.ui
  50. 148
      src/qt/forms/askpassphrasedialog.ui
  51. 105
      src/qt/forms/editaddressdialog.ui
  52. 161
      src/qt/forms/overviewpage.ui
  53. 122
      src/qt/forms/sendcoinsdialog.ui
  54. 178
      src/qt/forms/sendcoinsentry.ui
  55. 71
      src/qt/forms/transactiondescdialog.ui
  56. 23
      src/qt/guiconstants.h
  57. 74
      src/qt/guiutil.cpp
  58. 34
      src/qt/guiutil.h
  59. 1155
      src/qt/locale/bitcoin_de.ts
  60. 1441
      src/qt/locale/bitcoin_nl.ts
  61. 2173
      src/qt/locale/bitcoin_ru.ts
  62. 36
      src/qt/monitoreddatamapper.cpp
  63. 32
      src/qt/monitoreddatamapper.h
  64. 224
      src/qt/notificator.cpp
  65. 63
      src/qt/notificator.h
  66. 277
      src/qt/optionsdialog.cpp
  67. 50
      src/qt/optionsdialog.h
  68. 162
      src/qt/optionsmodel.cpp
  69. 56
      src/qt/optionsmodel.h
  70. 173
      src/qt/overviewpage.cpp
  71. 45
      src/qt/overviewpage.h
  72. 45
      src/qt/qvalidatedlineedit.cpp
  73. 28
      src/qt/qvalidatedlineedit.h
  74. 27
      src/qt/qvaluecombobox.cpp
  75. 33
      src/qt/qvaluecombobox.h
  76. 1
      src/qt/res/bitcoin-qt.rc
  77. BIN
      src/qt/res/icons/add.png
  78. BIN
      src/qt/res/icons/address-book.png
  79. BIN
      src/qt/res/icons/bitcoin.icns
  80. BIN
      src/qt/res/icons/bitcoin.ico
  81. BIN
      src/qt/res/icons/bitcoin.png
  82. BIN
      src/qt/res/icons/bitcoin_testnet.png
  83. BIN
      src/qt/res/icons/clock1.png
  84. BIN
      src/qt/res/icons/clock2.png
  85. BIN
      src/qt/res/icons/clock3.png
  86. BIN
      src/qt/res/icons/clock4.png
  87. BIN
      src/qt/res/icons/clock5.png
  88. BIN
      src/qt/res/icons/configure.png
  89. BIN
      src/qt/res/icons/connect0_16.png
  90. BIN
      src/qt/res/icons/connect1_16.png
  91. BIN
      src/qt/res/icons/connect2_16.png
  92. BIN
      src/qt/res/icons/connect3_16.png
  93. BIN
      src/qt/res/icons/connect4_16.png
  94. BIN
      src/qt/res/icons/edit.png
  95. BIN
      src/qt/res/icons/editcopy.png
  96. BIN
      src/qt/res/icons/editpaste.png
  97. BIN
      src/qt/res/icons/export.png
  98. BIN
      src/qt/res/icons/history.png
  99. BIN
      src/qt/res/icons/key.png
  100. BIN
      src/qt/res/icons/lock_closed.png
  101. Some files were not shown because too many files have changed in this diff Show More

13
.gitignore vendored

@ -9,3 +9,16 @@ src/bitcoind
*.o *.o
*.patch *.patch
.bitcoin .bitcoin
#compilation and Qt preprocessor part
*.o
ui_*.h
*.qm
moc_*
Makefile
bitcoin-qt
#resources cpp
qrc_*.cpp
#qt creator
*.pro.user
#mac specific
.DS_Store

1
README.md

@ -27,4 +27,3 @@ help test the Bitcoin core, please contact QA@BitcoinTesting.org.
Feature branches are created when there are major new features being Feature branches are created when there are major new features being
worked on by several people. worked on by several people.

185
bitcoin-qt.pro

@ -0,0 +1,185 @@
TEMPLATE = app
TARGET =
INCLUDEPATH += src src/json src/cryptopp src/qt
DEFINES += QT_GUI
# DEFINES += SSL
CONFIG += no_include_pwd
# for boost 1.37, add -mt to the boost libraries
LIBS += -lssl -lcrypto -ldb_cxx
unix:!macx:LIBS += -lboost_system -lboost_filesystem -lboost_program_options -lboost_thread
macx:LIBS += -lboost_system-mt -lboost_filesystem-mt -lboost_program_options-mt -lboost_thread-mt
macx:DEFINES += __WXMAC_OSX__ MSG_NOSIGNAL=0 BOOST_FILESYSTEM_VERSION=3
windows:LIBS += -lboost_system-mgw44-mt-1_43 -lboost_filesystem-mgw44-mt-1_43 -lboost_program_options-mgw44-mt-1_43 -lboost_thread-mgw44-mt-1_43 -lws2_32 -lgdi32
windows:DEFINES += __WXMSW__
windows:RC_FILE = src/qt/res/bitcoin-qt.rc
# use: qmake "USE_UPNP=1"
# miniupnpc (http://miniupnp.free.fr/files/) must be installed
count(USE_UPNP, 1) {
message(Building with UPNP support)
DEFINES += USE_UPNP=$$USE_UPNP
LIBS += -lminiupnpc
}
count(USE_DBUS, 1) {
message(Building with DBUS (Freedesktop notifications) support)
DEFINES += QT_DBUS
QT += dbus
}
# for extra security against potential buffer overflows
QMAKE_CXXFLAGS += -fstack-protector
QMAKE_LFLAGS += -fstack-protector
# disable quite some warnings because bitcoin core "sins" a lot
QMAKE_CXXFLAGS_WARN_ON = -fdiagnostics-show-option -Wall -Wno-invalid-offsetof -Wno-unused-variable -Wno-unused-parameter -Wno-sign-compare -Wno-char-subscripts -Wno-unused-value -Wno-sequence-point -Wno-parentheses -Wno-unknown-pragmas -Wno-switch
# Input
DEPENDPATH += src/qt src src/cryptopp src json/include
HEADERS += src/qt/bitcoingui.h \
src/qt/transactiontablemodel.h \
src/qt/addresstablemodel.h \
src/qt/optionsdialog.h \
src/qt/sendcoinsdialog.h \
src/qt/addressbookpage.h \
src/qt/aboutdialog.h \
src/qt/editaddressdialog.h \
src/qt/bitcoinaddressvalidator.h \
src/base58.h \
src/bignum.h \
src/util.h \
src/uint256.h \
src/serialize.h \
src/cryptopp/stdcpp.h \
src/cryptopp/smartptr.h \
src/cryptopp/simple.h \
src/cryptopp/sha.h \
src/cryptopp/secblock.h \
src/cryptopp/pch.h \
src/cryptopp/misc.h \
src/cryptopp/iterhash.h \
src/cryptopp/cryptlib.h \
src/cryptopp/cpu.h \
src/cryptopp/config.h \
src/strlcpy.h \
src/main.h \
src/net.h \
src/key.h \
src/db.h \
src/script.h \
src/noui.h \
src/init.h \
src/headers.h \
src/irc.h \
src/json/json_spirit_writer_template.h \
src/json/json_spirit_writer.h \
src/json/json_spirit_value.h \
src/json/json_spirit_utils.h \
src/json/json_spirit_stream_reader.h \
src/json/json_spirit_reader_template.h \
src/json/json_spirit_reader.h \
src/json/json_spirit_error_position.h \
src/json/json_spirit.h \
src/qt/clientmodel.h \
src/qt/guiutil.h \
src/qt/transactionrecord.h \
src/qt/guiconstants.h \
src/qt/optionsmodel.h \
src/qt/monitoreddatamapper.h \
src/qtui.h \
src/qt/transactiondesc.h \
src/qt/transactiondescdialog.h \
src/qt/bitcoinamountfield.h \
src/wallet.h \
src/keystore.h \
src/qt/transactionfilterproxy.h \
src/qt/transactionview.h \
src/qt/walletmodel.h \
src/bitcoinrpc.h \
src/qt/overviewpage.h \
src/qt/csvmodelwriter.h \
src/crypter.h \
src/qt/sendcoinsentry.h \
src/qt/qvalidatedlineedit.h \
src/qt/bitcoinunits.h \
src/qt/qvaluecombobox.h \
src/qt/askpassphrasedialog.h \
src/protocol.h \
src/qt/notificator.h
SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
src/qt/transactiontablemodel.cpp \
src/qt/addresstablemodel.cpp \
src/qt/optionsdialog.cpp \
src/qt/sendcoinsdialog.cpp \
src/qt/addressbookpage.cpp \
src/qt/aboutdialog.cpp \
src/qt/editaddressdialog.cpp \
src/qt/bitcoinaddressvalidator.cpp \
src/cryptopp/sha.cpp \
src/cryptopp/cpu.cpp \
src/util.cpp \
src/script.cpp \
src/main.cpp \
src/init.cpp \
src/net.cpp \
src/irc.cpp \
src/db.cpp \
src/json/json_spirit_writer.cpp \
src/json/json_spirit_value.cpp \
src/json/json_spirit_reader.cpp \
src/qt/clientmodel.cpp \
src/qt/guiutil.cpp \
src/qt/transactionrecord.cpp \
src/qt/optionsmodel.cpp \
src/qt/monitoreddatamapper.cpp \
src/qt/transactiondesc.cpp \
src/qt/transactiondescdialog.cpp \
src/qt/bitcoinstrings.cpp \
src/qt/bitcoinamountfield.cpp \
src/wallet.cpp \
src/keystore.cpp \
src/qt/transactionfilterproxy.cpp \
src/qt/transactionview.cpp \
src/qt/walletmodel.cpp \
src/bitcoinrpc.cpp \
src/qt/overviewpage.cpp \
src/qt/csvmodelwriter.cpp \
src/crypter.cpp \
src/qt/sendcoinsentry.cpp \
src/qt/qvalidatedlineedit.cpp \
src/qt/bitcoinunits.cpp \
src/qt/qvaluecombobox.cpp \
src/qt/askpassphrasedialog.cpp \
src/protocol.cpp \
src/qt/notificator.cpp
RESOURCES += \
src/qt/bitcoin.qrc
FORMS += \
src/qt/forms/sendcoinsdialog.ui \
src/qt/forms/addressbookpage.ui \
src/qt/forms/aboutdialog.ui \
src/qt/forms/editaddressdialog.ui \
src/qt/forms/transactiondescdialog.ui \
src/qt/forms/overviewpage.ui \
src/qt/forms/sendcoinsentry.ui \
src/qt/forms/askpassphrasedialog.ui
CODECFORTR = UTF-8
# for lrelease/lupdate
TRANSLATIONS = src/qt/locale/bitcoin_nl.ts src/qt/locale/bitcoin_de.ts \
src/qt/locale/bitcoin_ru.ts
OTHER_FILES += \
README.rst
# For use with MacPorts
macx:INCLUDEPATH += /opt/local/include /opt/local/include/db48
macx:LIBS += -L/opt/local/lib -L/opt/local/lib/db48
# Additional Mac options
macx:ICON = src/qt/res/icons/bitcoin.icns
macx:TARGET = "Bitcoin Qt"

43
contrib/miniupnpc/Portfile

@ -0,0 +1,43 @@
# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=tcl:et:sw=4:ts=4:sts=4
# $Id$
PortSystem 1.0
name miniupnpc
epoch 2
version 1.6
revision 2
categories net
platforms darwin
license BSD
maintainers singingwolfboy openmaintainer
description Lightweight client for UPnP protocol
long_description \
${description}
homepage http://miniupnp.free.fr/
master_sites http://miniupnp.free.fr/files/download.php?file=${distname}${extract.suffix}&dummy=
checksums md5 88055f2d4a061cfd4cfe25a9eae22f67 \
sha1 ef8f2edb17f2e7c5b8dc67ee80a65c199d823e0a \
rmd160 d86b75b331a3fb5525c71708548f311977c0598f
use_configure no
variant universal {}
if {[variant_isset universal]} {
set archflags ${configure.universal_cflags}
} else {
set archflags ${configure.cc_archflags}
}
build.args-append CC="${configure.cc} ${archflags}"
post-patch {
reinplace "s|-Wl,-install_name,|-Wl,-install_name,${prefix}/lib/|" ${worksrcpath}/Makefile
}
destroot.destdir PREFIX=${prefix} INSTALLPREFIX=${destroot}${prefix}
livecheck.type regex
livecheck.url http://miniupnp.free.fr/files/
livecheck.regex ${name}-(\\d+(\\.\\d{1,4})+)${extract.suffix}

73
doc/assets-attribution.txt

@ -0,0 +1,73 @@
Icon: src/qt/res/icons/clock*.png, src/qt/res/icons/tx*.png,
src/qt/res/src/*.svg
Designer: Wladimir van der Laan
License: Creative Commons Attribution
Icon: src/qt/res/icons/send.png
Icon Pack: Vista Style Arrow
Designer: Icons Land
License: Freeware Non-commercial
Site: http://findicons.com/icon/231371/right3green
Icon: src/qt/res/icons/address-book.png
Icon Pack: Farm-Fresh Web
Designer: FatCow Web Hosting
License: Creative Commons Attribution (by)
Site: http://findicons.com/icon/163938/book_open
Icon: src/qt/res/icons/connect*.png, src/qt/res/icons/synced.png, src/qt/res/icons/lock_*.png
Icon Pack: Human-O2
Designer: schollidesign
License: GNU/GPL
Site: http://findicons.com/icon/93743/blocks_gnome_netstatus_0
Icon: src/qt/res/icons/transaction*.png
Designer: md2k7
Site: https://forum.bitcoin.org/index.php?topic=15276.0
License: You are free to do with these icons as you wish, including selling,
copying, modifying etc.
Icon: src/qt/res/icons/configure.png, src/qt/res/icons/quit.png,
src/qt/res/icons/editcopy.png, src/qt/res/icons/editpaste.png,
src/qt/res/icons/add.png, src/qt/res/icons/edit.png,
src/qt/res/icons/remove.png (edited)
Designer: http://www.everaldo.com
Icon Pack: Crystal SVG
License: LGPL
Icon: src/qt/res/icons/receive.png, src/qt/res/icons/history.png,
src/qt/res/icons/export.png
Designer: Oxygen team
Icon Pack: Oxygen
License: Creative Common Attribution-ShareAlike 3.0 License or LGPL
Site: http://www.oxygen-icons.org/
Icon: src/qt/res/icons/bitcoin.png, src/qt/res/icons/toolbar.png
Designer: Bitboy (optimized for 16x16 by Wladimir van der Laan)
License: Public Domain
Site: http://forum.bitcoin.org/?topic=1756.0
Icon: src/qt/res/icons/overview.png
Icon Pack: Primo
Designer: Jack Cai
License: Creative Commons Attribution No Derivatives (by-nd)
Site: http://findicons.com/icon/175944/home?id=176221#
Icon: scripts/img/reload.xcf (modified),src/qt/res/movies/update_spinner.mng
Icon Pack: Kids
Designer: Everaldo (Everaldo Coelho)
License: GNU/GPL
Site: http://findicons.com/icon/17102/reload?id=17102
Image: src/qt/res/images/splash2.jpg (Wallet image)
Designer: Crobbo (forum)
Site: https://bitcointalk.org/index.php?topic=32273.0
License: Public domain
Icon: src/qt/res/icons/key.png
Designer: VisualPharm (Ivan Boyko)
Icon Pack: Must Have
Site: http://findicons.com/icon/51009/key?id=51009
License: Creative Commons Attribution (by)

162
doc/readme-qt.rst

@ -0,0 +1,162 @@
Bitcoin-qt: Qt4 based GUI replacement for Bitcoin
=================================================
Features
========
- All functionality of the Wx GUI, including wallet encryption
- Compatibility with Linux (both GNOME and KDE), MacOSX and Windows
- Notification on incoming / outgoing transactions (compatible with FreeDesktop and other desktop notification schemes)
- General interface improvements: Splash screen, tabbed interface
- Overview page with current balance, unconfirmed balance, and such
- Better transaction list with status icons, real-time filtering and a context menu
- Asks for confirmation before sending coins, for your own safety
- CSV export of transactions and address book (for Excel bookkeeping)
- Shows alternative icon when connected to testnet, so you never accidentally send real coins during testing
- Shows a progress bar on initial block download, so that you don't have to wonder how many blocks it needs to download to be up to date
- Sendmany support, send to multiple recipients at the same time
- Multiple unit support, can show subdivided bitcoins (uBTC, mBTC) for users that like large numbers
- Support for English, German, Russian and Dutch languages
- Address books and transaction table can be sorted by any column
- Accepts "bitcoin:" URLs from browsers and other sources through drag and drop
Build instructions
===================
Debian
-------
First, make sure that the required packages for Qt4 development of your
distribution are installed, for Debian and Ubuntu these are:
::
apt-get install qt4-qmake libqt4-dev build-essential libboost-dev libboost-system-dev \
libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev \
libssl-dev libdb4.8++-dev
then execute the following:
::
qmake
make
Alternatively, install Qt Creator and open the `bitcoin-qt.pro` file.
An executable named `bitcoin-qt` will be built.
Windows
--------
Windows build instructions:
- Download the `QT Windows SDK`_ and install it. You don't need the Symbian stuff, just the desktop Qt.
- Download and extract the `dependencies archive`_ [#]_, or compile openssl, boost and dbcxx yourself.
- Copy the contents of the folder "deps" to "X:\\QtSDK\\mingw", replace X:\\ with the location where you installed the Qt SDK. Make sure that the contents of "deps\\include" end up in the current "include" directory.
- Open the .pro file in QT creator and build as normal (ctrl-B)
.. _`QT Windows SDK`: http://qt.nokia.com/downloads/sdk-windows-cpp
.. _`dependencies archive`: https://download.visucore.com/bitcoin/qtgui_deps_1.zip
.. [#] PGP signature: https://download.visucore.com/bitcoin/qtgui_deps_1.zip.sig (signed with RSA key ID `610945D0`_)
.. _`610945D0`: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x610945D0
Mac OS X
--------
- Download and install the `Qt Mac OS X SDK`_. It is recommended to also install Apple's Xcode with UNIX tools.
- Download and install `MacPorts`_.
- Execute the following commands in a terminal to get the dependencies:
::
sudo port selfupdate
sudo port install boost db48
- Open the .pro file in Qt Creator and build as normal (cmd-B)
.. _`Qt Mac OS X SDK`: http://qt.nokia.com/downloads/sdk-mac-os-cpp
.. _`MacPorts`: http://www.macports.org/install.php
Build configuration options
============================
UPNnP port forwarding
---------------------
To use UPnP for port forwarding behind a NAT router (recommended, as more connections overall allow for a faster and more stable bitcoin experience), pass the following argument to qmake:
::
qmake "USE_UPNP=1"
(in **Qt Creator**, you can find the setting for additional qmake arguments under "Projects" -> "Build Settings" -> "Build Steps", then click "Details" next to **qmake**)
This requires miniupnpc for UPnP port mapping. It can be downloaded from
http://miniupnp.tuxfamily.org/files/. UPnP support is not compiled in by default.
Set USE_UPNP to a different value to control this:
+------------+--------------------------------------------------------------+
| USE_UPNP= | (the default) no UPnP support, miniupnpc not required; |
+------------+--------------------------------------------------------------+
| USE_UPNP=0 | UPnP support turned off by default at runtime; |
+------------+--------------------------------------------------------------+
| USE_UPNP=1 | UPnP support turned on by default at runtime. |
+------------+--------------------------------------------------------------+
Mac OS X users: miniupnpc is currently outdated on MacPorts. An updated Portfile is provided in contrib/miniupnpc within this project.
You can execute the following commands in a terminal to install it:
::
cd <location of bitcoin-qt>/contrib/miniupnpc
sudo port install
Notification support for recent (k)ubuntu versions
---------------------------------------------------
To see desktop notifications on (k)ubuntu versions starting from 10.04, enable usage of the
FreeDesktop notification interface through DBUS using the following qmake option:
::
qmake "USE_DBUS=1"
Berkely DB version warning
==========================
A warning for people using the *static binary* version of Bitcoin on a Linux/UNIX-ish system (tl;dr: **Berkely DB databases are not forward compatible**).
The static binary version of Bitcoin is linked against libdb4.7 or libdb4.8 (see also `this Debian issue`_).
Now the nasty thing is that databases from 5.X are not compatible with 4.X.
If the globally installed development package of Berkely DB installed on your system is 5.X, any source you
build yourself will be linked against that. The first time you run with a 5.X version the database will be upgraded,
and 4.X cannot open the new format. This means that you cannot go back to the old statically linked version without
significant hassle!
.. _`this Debian issue`: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=621425

63
scripts/qt/extract_strings_qt.py

@ -0,0 +1,63 @@
#!/usr/bin/python
'''
Extract _("...") strings for translation and convert to Qt4 stringdefs so that
they can be picked up by Qt linguist.
'''
from subprocess import Popen, PIPE
OUT_CPP="src/qt/bitcoinstrings.cpp"
EMPTY=['""']
def parse_po(text):
"""
Parse 'po' format produced by xgettext.
Return a list of (msgid,msgstr) tuples.
"""
messages = []
msgid = []
msgstr = []
in_msgid = False
in_msgstr = False
for line in text.split('\n'):
line = line.rstrip('\r')
if line.startswith('msgid '):
if in_msgstr:
messages.append((msgid, msgstr))
in_msgstr = False
# message start
in_msgid = True
msgid = [line[6:]]
elif line.startswith('msgstr '):
in_msgid = False
in_msgstr = True
msgstr = [line[7:]]
elif line.startswith('"'):
if in_msgid:
msgid.append(line)
if in_msgstr:
msgstr.append(line)
if in_msgstr:
messages.append((msgid, msgstr))
return messages
files = ['src/base58.h', 'src/bignum.h', 'src/db.cpp', 'src/db.h', 'src/headers.h', 'src/init.cpp', 'src/init.h', 'src/irc.cpp', 'src/irc.h', 'src/key.h', 'src/main.cpp', 'src/main.h', 'src/net.cpp', 'src/net.h', 'src/noui.h', 'src/script.cpp', 'src/script.h', 'src/serialize.h', 'src/strlcpy.h', 'src/uint256.h', 'src/util.cpp', 'src/util.h']
# xgettext -n --keyword=_ $FILES
child = Popen(['xgettext','--output=-','-n','--keyword=_'] + files, stdout=PIPE)
(out, err) = child.communicate()
messages = parse_po(out)
f = open(OUT_CPP, 'w')
f.write('#include <QtGlobal>\n')
f.write('// Automatically generated by extract_strings.py\n')
f.write('static const char *bitcoin_strings[] = {')
for (msgid, msgstr) in messages:
if msgid != EMPTY:
f.write('QT_TRANSLATE_NOOP("bitcoin-core", %s),\n' % ('\n'.join(msgid)))
f.write('};')
f.close()

BIN
scripts/qt/img/reload.xcf

Binary file not shown.

43
scripts/qt/make_spinner.py

@ -0,0 +1,43 @@
#!/usr/bin/env python
# W.J. van der Laan, 2011
# Make spinning .mng animation from a .png
# Requires imagemagick 6.7+
from __future__ import division
from os import path
from PIL import Image
from subprocess import Popen
SRC='img/reload_scaled.png'
DST='../../src/qt/res/movies/update_spinner.mng'
TMPDIR='/tmp'
TMPNAME='tmp-%03i.png'
NUMFRAMES=35
FRAMERATE=10.0
CONVERT='convert'
CLOCKWISE=True
DSIZE=(16,16)
im_src = Image.open(SRC)
if CLOCKWISE:
im_src = im_src.transpose(Image.FLIP_LEFT_RIGHT)
def frame_to_filename(frame):
return path.join(TMPDIR, TMPNAME % frame)
frame_files = []
for frame in xrange(NUMFRAMES):
rotation = (frame + 0.5) / NUMFRAMES * 360.0
if CLOCKWISE:
rotation = -rotation
im_new = im_src.rotate(rotation, Image.BICUBIC)
im_new.thumbnail(DSIZE, Image.ANTIALIAS)
outfile = frame_to_filename(frame)
im_new.save(outfile, 'png')
frame_files.append(outfile)
p = Popen([CONVERT, "-delay", str(FRAMERATE), "-dispose", "2"] + frame_files + [DST])
p.communicate()

9
scripts/qt/make_windows_icon.py

@ -0,0 +1,9 @@
#!/bin/bash
# create multiresolution windows icon
ICON_SRC=../../src/qt/res/icons/bitcoin.png
ICON_DST=../../src/qt/res/icons/bitcoin.ico
convert ${ICON_SRC} -resize 16x16 bitcoin-16.png
convert ${ICON_SRC} -resize 32x32 bitcoin-32.png
convert ${ICON_SRC} -resize 48x48 bitcoin-48.png
convert bitcoin-16.png bitcoin-32.png bitcoin-48.png ${ICON_DST}

4
src/rpc.cpp → src/bitcoinrpc.cpp

@ -50,13 +50,13 @@ Object JSONRPCError(int code, const string& message)
} }
void PrintConsole(const char* format, ...) void PrintConsole(const std::string &format, ...)
{ {
char buffer[50000]; char buffer[50000];
int limit = sizeof(buffer); int limit = sizeof(buffer);
va_list arg_ptr; va_list arg_ptr;
va_start(arg_ptr, format); va_start(arg_ptr, format);
int ret = _vsnprintf(buffer, limit, format, arg_ptr); int ret = _vsnprintf(buffer, limit, format.c_str(), arg_ptr);
va_end(arg_ptr); va_end(arg_ptr);
if (ret < 0 || ret >= limit) if (ret < 0 || ret >= limit)
{ {

0
src/rpc.h → src/bitcoinrpc.h

4
src/headers.h

@ -99,8 +99,12 @@
#include "uibase.h" #include "uibase.h"
#include "ui.h" #include "ui.h"
#else #else
#ifdef QT_GUI
#include "qtui.h"
#else
#include "noui.h" #include "noui.h"
#endif #endif
#endif
#ifdef GUI #ifdef GUI
#include "xpm/addressbook16.xpm" #include "xpm/addressbook16.xpm"

18
src/init.cpp

@ -4,7 +4,7 @@
// file license.txt or http://www.opensource.org/licenses/mit-license.php. // file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "headers.h" #include "headers.h"
#include "db.h" #include "db.h"
#include "rpc.h" #include "bitcoinrpc.h"
#include "net.h" #include "net.h"
#include "init.h" #include "init.h"
#include "strlcpy.h" #include "strlcpy.h"
@ -80,7 +80,7 @@ void HandleSIGTERM(int)
// //
// Start // Start
// //
#ifndef GUI #if !defined(QT_GUI) && !defined(GUI)
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
bool fRet = false; bool fRet = false;
@ -240,10 +240,9 @@ bool AppInit2(int argc, char* argv[])
fServer = GetBoolArg("-server"); fServer = GetBoolArg("-server");
/* force fServer when running without GUI */ /* force fServer when running without GUI */
#ifndef GUI #if !defined(QT_GUI) && !defined(GUI)
fServer = true; fServer = true;
#endif #endif
fPrintToConsole = GetBoolArg("-printtoconsole"); fPrintToConsole = GetBoolArg("-printtoconsole");
fPrintToDebugger = GetBoolArg("-printtodebugger"); fPrintToDebugger = GetBoolArg("-printtodebugger");
@ -252,6 +251,7 @@ bool AppInit2(int argc, char* argv[])
fNoListen = GetBoolArg("-nolisten") || fTOR; fNoListen = GetBoolArg("-nolisten") || fTOR;
fLogTimestamps = GetBoolArg("-logtimestamps"); fLogTimestamps = GetBoolArg("-logtimestamps");
#ifndef QT_GUI
for (int i = 1; i < argc; i++) for (int i = 1; i < argc; i++)
if (!IsSwitchChar(argv[i][0])) if (!IsSwitchChar(argv[i][0]))
fCommandLine = true; fCommandLine = true;
@ -261,6 +261,7 @@ bool AppInit2(int argc, char* argv[])
int ret = CommandLineRPC(argc, argv); int ret = CommandLineRPC(argc, argv);
exit(ret); exit(ret);
} }
#endif
#ifndef __WXMSW__ #ifndef __WXMSW__
if (fDaemon) if (fDaemon)
@ -373,18 +374,21 @@ bool AppInit2(int argc, char* argv[])
strErrors = ""; strErrors = "";
int64 nStart; int64 nStart;
InitMessage(_("Loading addresses..."));
printf("Loading addresses...\n"); printf("Loading addresses...\n");
nStart = GetTimeMillis(); nStart = GetTimeMillis();
if (!LoadAddresses()) if (!LoadAddresses())
strErrors += _("Error loading addr.dat \n"); strErrors += _("Error loading addr.dat \n");
printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart); printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart);
InitMessage(_("Loading block index..."));
printf("Loading block index...\n"); printf("Loading block index...\n");
nStart = GetTimeMillis(); nStart = GetTimeMillis();
if (!LoadBlockIndex()) if (!LoadBlockIndex())
strErrors += _("Error loading blkindex.dat \n"); strErrors += _("Error loading blkindex.dat \n");
printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart); printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
InitMessage(_("Loading wallet..."));
printf("Loading wallet...\n"); printf("Loading wallet...\n");
nStart = GetTimeMillis(); nStart = GetTimeMillis();
bool fFirstRun; bool fFirstRun;
@ -415,12 +419,14 @@ bool AppInit2(int argc, char* argv[])
} }
if (pindexBest != pindexRescan) if (pindexBest != pindexRescan)
{ {
InitMessage(_("Rescanning..."));
printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight); printf("Rescanning last %i blocks (from block %i)...\n", pindexBest->nHeight - pindexRescan->nHeight, pindexRescan->nHeight);
nStart = GetTimeMillis(); nStart = GetTimeMillis();
pwalletMain->ScanForWalletTransactions(pindexRescan, true); pwalletMain->ScanForWalletTransactions(pindexRescan, true);
printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart); printf(" rescan %15"PRI64d"ms\n", GetTimeMillis() - nStart);
} }
InitMessage(_("Done loading"));
printf("Done loading\n"); printf("Done loading\n");
//// debug print //// debug print
@ -543,7 +549,7 @@ bool AppInit2(int argc, char* argv[])
RandAddSeedPerfmon(); RandAddSeedPerfmon();
if (!CreateThread(StartNode, NULL)) if (!CreateThread(StartNode, NULL))
wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin"); wxMessageBox(_("Error: CreateThread(StartNode) failed"), "Bitcoin");
if (fServer) if (fServer)
CreateThread(ThreadRPCServer, NULL); CreateThread(ThreadRPCServer, NULL);
@ -553,7 +559,7 @@ bool AppInit2(int argc, char* argv[])
SetStartOnSystemStartup(true); SetStartOnSystemStartup(true);
#endif #endif
#ifndef GUI #if !defined(QT_GUI) && !defined(GUI)
while (1) while (1)
Sleep(5000); Sleep(5000);
#endif #endif

11
src/main.cpp

@ -32,6 +32,7 @@ uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain
const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
int nMaxBlocksOfPeers = 0; // Amount of blocks that other nodes claim to have
CBlockIndex* pindexGenesisBlock = NULL; CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1; int nBestHeight = -1;
CBigNum bnBestChainWork = 0; CBigNum bnBestChainWork = 0;
@ -726,6 +727,12 @@ int GetTotalBlocksEstimate()
} }
} }
// Return maximum amount of blocks that other nodes claim to have
int GetNumBlocksOfPeers()
{
return std::max(nMaxBlocksOfPeers, GetTotalBlocksEstimate());
}
bool IsInitialBlockDownload() bool IsInitialBlockDownload()
{ {
if (pindexBest == NULL || nBestHeight < (GetTotalBlocksEstimate()-nInitialBlockThreshold)) if (pindexBest == NULL || nBestHeight < (GetTotalBlocksEstimate()-nInitialBlockThreshold))
@ -1840,6 +1847,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->fSuccessfullyConnected = true; pfrom->fSuccessfullyConnected = true;
printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
if(pfrom->nStartingHeight > nMaxBlocksOfPeers)
{
nMaxBlocksOfPeers = pfrom->nStartingHeight;
}
} }

1
src/main.h

@ -99,6 +99,7 @@ void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey); bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
bool CheckProofOfWork(uint256 hash, unsigned int nBits); bool CheckProofOfWork(uint256 hash, unsigned int nBits);
int GetTotalBlocksEstimate(); int GetTotalBlocksEstimate();
int GetNumBlocksOfPeers();
bool IsInitialBlockDownload(); bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor); std::string GetWarnings(std::string strFor);

4
src/makefile.linux-mingw

@ -49,7 +49,7 @@ HEADERS = \
net.h \ net.h \
noui.h \ noui.h \
protocol.h \ protocol.h \
rpc.h \ bitcoinrpc.h \
script.h \ script.h \
serialize.h \ serialize.h \
strlcpy.h \ strlcpy.h \
@ -76,7 +76,7 @@ OBJS= \
obj/main.o \ obj/main.o \
obj/net.o \ obj/net.o \
obj/protocol.o \ obj/protocol.o \
obj/rpc.o \ obj/bitcoinrpc.o \
obj/script.o \ obj/script.o \
obj/util.o \ obj/util.o \
obj/wallet.o \ obj/wallet.o \

4
src/makefile.mingw

@ -46,7 +46,7 @@ HEADERS = \
net.h \ net.h \
noui.h \ noui.h \
protocol.h \ protocol.h \
rpc.h \ bitcoinrpc.h \
script.h \ script.h \
serialize.h \ serialize.h \
strlcpy.h \ strlcpy.h \
@ -74,7 +74,7 @@ OBJS= \
obj/main.o \ obj/main.o \
obj/net.o \ obj/net.o \
obj/protocol.o \ obj/protocol.o \
obj/rpc.o \ obj/bitcoinrpc.o \
obj/script.o \ obj/script.o \
obj/util.o \ obj/util.o \
obj/wallet.o \ obj/wallet.o \

4
src/makefile.osx vendored

@ -46,7 +46,7 @@ HEADERS = \
net.h \ net.h \
noui.h \ noui.h \
protocol.h \ protocol.h \
rpc.h \ bitcoinrpc.h \
script.h \ script.h \
serialize.h \ serialize.h \
strlcpy.h \ strlcpy.h \
@ -65,7 +65,7 @@ OBJS= \
obj/main.o \ obj/main.o \
obj/net.o \ obj/net.o \
obj/protocol.o \ obj/protocol.o \
obj/rpc.o \ obj/bitcoinrpc.o \
obj/script.o \ obj/script.o \
obj/util.o \ obj/util.o \
obj/wallet.o \ obj/wallet.o \

4
src/makefile.unix

@ -52,7 +52,7 @@ HEADERS = \
net.h \ net.h \
noui.h \ noui.h \
protocol.h \ protocol.h \
rpc.h \ bitcoinrpc.h \
script.h \ script.h \
serialize.h \ serialize.h \
strlcpy.h \ strlcpy.h \
@ -71,7 +71,7 @@ OBJS= \
obj/main.o \ obj/main.o \
obj/net.o \ obj/net.o \
obj/protocol.o \ obj/protocol.o \
obj/rpc.o \ obj/bitcoinrpc.o \
obj/script.o \ obj/script.o \
obj/util.o \ obj/util.o \
obj/wallet.o \ obj/wallet.o \

2
src/makefile.vc

@ -59,7 +59,7 @@ HEADERS = \
net.h \ net.h \
noui.h \ noui.h \
protocol.h \ protocol.h \
rpc.h \ bitcoinrpc.h \
script.h \ script.h \
serialize.h \ serialize.h \
strlcpy.h \ strlcpy.h \

4
src/noui.h

@ -67,4 +67,8 @@ inline void MainFrameRepaint()
{ {
} }
inline void InitMessage(const std::string &message)
{
}
#endif #endif

26
src/qt/aboutdialog.cpp

@ -0,0 +1,26 @@
#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include "clientmodel.h"
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
}
void AboutDialog::setModel(ClientModel *model)
{
ui->versionLabel->setText(model->formatFullVersion());
}
AboutDialog::~AboutDialog()
{
delete ui;
}
void AboutDialog::on_buttonBox_accepted()
{
close();
}

27
src/qt/aboutdialog.h

@ -0,0 +1,27 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class ClientModel;
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent = 0);
~AboutDialog();
void setModel(ClientModel *model);
private:
Ui::AboutDialog *ui;
private slots:
void on_buttonBox_accepted();
};
#endif // ABOUTDIALOG_H

215
src/qt/addressbookpage.cpp

@ -0,0 +1,215 @@
#include "addressbookpage.h"
#include "ui_addressbookpage.h"
#include "addresstablemodel.h"
#include "editaddressdialog.h"
#include "csvmodelwriter.h"
#include <QSortFilterProxyModel>
#include <QClipboard>
#include <QFileDialog>
#include <QMessageBox>
AddressBookPage::AddressBookPage(Mode mode, Tabs tab, QWidget *parent) :
QDialog(parent),
ui(new Ui::AddressBookPage),
model(0),
mode(mode),
tab(tab)
{
ui->setupUi(this);
switch(mode)
{
case ForSending:
connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(accept()));
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableView->setFocus();
break;
case ForEditing:
ui->buttonBox->hide();
break;
}
switch(tab)
{
case SendingTab:
ui->labelExplanation->hide();
break;
case ReceivingTab:
break;
}
ui->tableView->setTabKeyNavigation(false);
connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
}
AddressBookPage::~AddressBookPage()
{
delete ui;
}
void AddressBookPage::setModel(AddressTableModel *model)
{
this->model = model;
// Refresh list from core
model->updateList();
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(model);
proxyModel->setDynamicSortFilter(true);
switch(tab)
{
case ReceivingTab:
// Receive filter
proxyModel->setFilterRole(AddressTableModel::TypeRole);
proxyModel->setFilterFixedString(AddressTableModel::Receive);
break;
case SendingTab:
// Send filter
proxyModel->setFilterRole(AddressTableModel::TypeRole);
proxyModel->setFilterFixedString(AddressTableModel::Send);
break;
}
ui->tableView->setModel(proxyModel);
ui->tableView->sortByColumn(0, Qt::AscendingOrder);
// Set column widths
ui->tableView->horizontalHeader()->resizeSection(
AddressTableModel::Address, 320);
ui->tableView->horizontalHeader()->setResizeMode(
AddressTableModel::Label, QHeaderView::Stretch);
connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(selectionChanged()));
if(mode == ForSending)
{
// Auto-select first row when in sending mode
ui->tableView->selectRow(0);
}
selectionChanged();
}
QTableView *AddressBookPage::getCurrentTable()
{
return ui->tableView;
}
void AddressBookPage::on_copyToClipboard_clicked()
{
// Copy currently selected address to clipboard
// (or nothing, if nothing selected)
QTableView *table = getCurrentTable();
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
foreach (QModelIndex index, indexes)
{
QVariant address = index.data();
QApplication::clipboard()->setText(address.toString());
}
}
void AddressBookPage::on_newAddressButton_clicked()
{
EditAddressDialog dlg(
tab == SendingTab ?
EditAddressDialog::NewSendingAddress :
EditAddressDialog::NewReceivingAddress);
dlg.setModel(model);
if(dlg.exec())
{
// Select row for newly created address
QString address = dlg.getAddress();
QModelIndexList lst = proxyModel->match(proxyModel->index(0,
AddressTableModel::Address, QModelIndex()),
Qt::EditRole, address, 1, Qt::MatchExactly);
if(!lst.isEmpty())
{
ui->tableView->setFocus();
ui->tableView->selectRow(lst.at(0).row());
}
}
}
void AddressBookPage::on_deleteButton_clicked()
{
QTableView *table = getCurrentTable();
QModelIndexList indexes = table->selectionModel()->selectedRows();
if(!indexes.isEmpty())
{
table->model()->removeRow(indexes.at(0).row());
}
}
void AddressBookPage::selectionChanged()
{
// Set button states based on selected tab and selection
QTableView *table = getCurrentTable();
if(table->selectionModel()->hasSelection())
{
switch(tab)
{
case SendingTab:
ui->deleteButton->setEnabled(true);
break;
case ReceivingTab:
ui->deleteButton->setEnabled(false);
break;
}
ui->copyToClipboard->setEnabled(true);
}
else
{
ui->deleteButton->setEnabled(false);
ui->copyToClipboard->setEnabled(false);
}
}
void AddressBookPage::done(int retval)
{
// When this is a tab/widget and not a model dialog, ignore "done"
if(mode == ForEditing)
return;
// Figure out which address was selected, and return it
QTableView *table = getCurrentTable();
QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address);
foreach (QModelIndex index, indexes)
{
QVariant address = table->model()->data(index);
returnValue = address.toString();
}
if(returnValue.isEmpty())
{
retval = Rejected;
}
QDialog::done(retval);
}
void AddressBookPage::exportClicked()
{
// CSV is currently the only supported format
QString filename = QFileDialog::getSaveFileName(
this,
tr("Export Address Book Data"),
QDir::currentPath(),
tr("Comma separated file (*.csv)"));
if (filename.isNull()) return;
CSVModelWriter writer(filename);
// name, column, role
writer.setModel(proxyModel);
writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);
if(!writer.write())
{
QMessageBox::critical(this, tr("Error exporting"), tr("Could not write to file %1.").arg(filename),
QMessageBox::Abort, QMessageBox::Abort);
}
}

59
src/qt/addressbookpage.h

@ -0,0 +1,59 @@
#ifndef ADDRESSBOOKPAGE_H
#define ADDRESSBOOKPAGE_H
#include <QDialog>
namespace Ui {
class AddressBookPage;
}
class AddressTableModel;
QT_BEGIN_NAMESPACE
class QTableView;
class QItemSelection;
class QSortFilterProxyModel;
QT_END_NAMESPACE
class AddressBookPage : public QDialog
{
Q_OBJECT
public:
enum Tabs {
SendingTab = 0,
ReceivingTab = 1
};
enum Mode {
ForSending, // Pick address for sending
ForEditing // Open address book for editing
};
explicit AddressBookPage(Mode mode, Tabs tab, QWidget *parent = 0);
~AddressBookPage();
void setModel(AddressTableModel *model);
const QString &getReturnValue() const { return returnValue; }
public slots:
void done(int retval);
void exportClicked();
private:
Ui::AddressBookPage *ui;
AddressTableModel *model;
Mode mode;
Tabs tab;
QString returnValue;
QSortFilterProxyModel *proxyModel;
QTableView *getCurrentTable();
private slots:
void on_deleteButton_clicked();
void on_newAddressButton_clicked();
void on_copyToClipboard_clicked();
void selectionChanged();
};
#endif // ADDRESSBOOKDIALOG_H

347
src/qt/addresstablemodel.cpp

@ -0,0 +1,347 @@
#include "addresstablemodel.h"
#include "guiutil.h"
#include "walletmodel.h"
#include "headers.h"
#include <QFont>
#include <QColor>
const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";
struct AddressTableEntry
{
enum Type {
Sending,
Receiving
};
Type type;
QString label;
QString address;
AddressTableEntry() {}
AddressTableEntry(Type type, const QString &label, const QString &address):
type(type), label(label), address(address) {}
};
// Private implementation
struct AddressTablePriv
{
CWallet *wallet;
QList<AddressTableEntry> cachedAddressTable;
AddressTablePriv(CWallet *wallet):
wallet(wallet) {}
void refreshAddressTable()
{
cachedAddressTable.clear();
CRITICAL_BLOCK(wallet->cs_wallet)
{
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, std::string)& item, wallet->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
const std::string& strName = item.second;
bool fMine = wallet->HaveKey(address);
cachedAddressTable.append(AddressTableEntry(fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
QString::fromStdString(strName),
QString::fromStdString(address.ToString())));
}
}
}
int size()
{
return cachedAddressTable.size();
}
AddressTableEntry *index(int idx)
{
if(idx >= 0 && idx < cachedAddressTable.size())
{
return &cachedAddressTable[idx];
}
else
{
return 0;
}
}
};
AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
{
columns << tr("Label") << tr("Address");
priv = new AddressTablePriv(wallet);
priv->refreshAddressTable();
}
AddressTableModel::~AddressTableModel()
{
delete priv;
}
int AddressTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return priv->size();
}
int AddressTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return columns.length();
}
QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
if(role == Qt::DisplayRole || role == Qt::EditRole)
{
switch(index.column())
{
case Label:
if(rec->label.isEmpty() && role == Qt::DisplayRole)
{
return tr("(no label)");
}
else
{
return rec->label;
}
case Address:
return rec->address;
}
}
else if (role == Qt::FontRole)
{
QFont font;
if(index.column() == Address)
{
font = GUIUtil::bitcoinAddressFont();
}
return font;
}
else if (role == TypeRole)
{
switch(rec->type)
{
case AddressTableEntry::Sending:
return Send;
case AddressTableEntry::Receiving:
return Receive;
default: break;
}
}
return QVariant();
}
bool AddressTableModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
if(!index.isValid())
return false;
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
editStatus = OK;
if(role == Qt::EditRole)
{
switch(index.column())
{
case Label:
wallet->SetAddressBookName(rec->address.toStdString(), value.toString().toStdString());
rec->label = value.toString();
break;
case Address:
// Refuse to set invalid address, set error status and return false
if(!walletModel->validateAddress(value.toString()))
{
editStatus = INVALID_ADDRESS;
return false;
}
// Double-check that we're not overwriting a receiving address
if(rec->type == AddressTableEntry::Sending)
{
CRITICAL_BLOCK(wallet->cs_wallet)
{
// Remove old entry
wallet->DelAddressBookName(rec->address.toStdString());
// Add new entry with new address
wallet->SetAddressBookName(value.toString().toStdString(), rec->label.toStdString());
}
rec->address = value.toString();
}
break;
}
emit dataChanged(index, index);
return true;
}
return false;
}
QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal)
{
if(role == Qt::DisplayRole)
{
return columns[section];
}
}
return QVariant();
}
Qt::ItemFlags AddressTableModel::flags(const QModelIndex & index) const
{
if(!index.isValid())
return 0;
AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
// Can edit address and label for sending addresses,
// and only label for receiving addresses.
if(rec->type == AddressTableEntry::Sending ||
(rec->type == AddressTableEntry::Receiving && index.column()==Label))
{
retval |= Qt::ItemIsEditable;
}
return retval;
}
QModelIndex AddressTableModel::index(int row, int column, const QModelIndex & parent) const
{
Q_UNUSED(parent);
AddressTableEntry *data = priv->index(row);
if(data)
{
return createIndex(row, column, priv->index(row));
}
else
{
return QModelIndex();
}
}
void AddressTableModel::updateList()
{
// Update address book model from Bitcoin core
beginResetModel();
priv->refreshAddressTable();
endResetModel();
}
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
{
std::string strLabel = label.toStdString();
std::string strAddress = address.toStdString();
editStatus = OK;
if(type == Send)
{
if(!walletModel->validateAddress(address))
{
editStatus = INVALID_ADDRESS;
return QString();
}
// Check for duplicate addresses
CRITICAL_BLOCK(wallet->cs_wallet)
{
if(wallet->mapAddressBook.count(strAddress))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
}
}
}
else if(type == Receive)
{
// Generate a new address to associate with given label
WalletModel::UnlockContext ctx(walletModel->requestUnlock());
if(!ctx.isValid())
{
// Unlock wallet failed or was cancelled
editStatus = WALLET_UNLOCK_FAILURE;
return QString();
}
std::vector<unsigned char> newKey;
if(!wallet->GetKeyFromPool(newKey, true))
{
editStatus = KEY_GENERATION_FAILURE;
return QString();
}
strAddress = CBitcoinAddress(newKey).ToString();
}
else
{
return QString();
}
// Add entry and update list
CRITICAL_BLOCK(wallet->cs_wallet)
wallet->SetAddressBookName(strAddress, strLabel);
updateList();
return QString::fromStdString(strAddress);
}
bool AddressTableModel::removeRows(int row, int count, const QModelIndex & parent)
{
Q_UNUSED(parent);
AddressTableEntry *rec = priv->index(row);
if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
{
// Can only remove one row at a time, and cannot remove rows not in model.
// Also refuse to remove receiving addresses.
return false;
}
CRITICAL_BLOCK(wallet->cs_wallet)
{
wallet->DelAddressBookName(rec->address.toStdString());
}
updateList();
return true;
}
void AddressTableModel::update()
{
}
/* Look up label for address in address book, if not found return empty string.
*/
QString AddressTableModel::labelForAddress(const QString &address) const
{
CRITICAL_BLOCK(wallet->cs_wallet)
{
CBitcoinAddress address_parsed(address.toStdString());
std::map<CBitcoinAddress, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed);
if (mi != wallet->mapAddressBook.end())
{
return QString::fromStdString(mi->second);
}
}
return QString();
}
int AddressTableModel::lookupAddress(const QString &address) const
{
QModelIndexList lst = match(index(0, Address, QModelIndex()),
Qt::EditRole, address, 1, Qt::MatchExactly);
if(lst.isEmpty())
{
return -1;
}
else
{
return lst.at(0).row();
}
}

83
src/qt/addresstablemodel.h

@ -0,0 +1,83 @@
#ifndef ADDRESSTABLEMODEL_H
#define ADDRESSTABLEMODEL_H
#include <QAbstractTableModel>
#include <QStringList>
class AddressTablePriv;
class CWallet;
class WalletModel;
class AddressTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0);
~AddressTableModel();
enum ColumnIndex {
Label = 0, /* User specified label */
Address = 1 /* Bitcoin address */
};
enum RoleIndex {
TypeRole = Qt::UserRole
};
// Return status of last edit/insert operation
enum EditStatus {
OK,
INVALID_ADDRESS,
DUPLICATE_ADDRESS,
WALLET_UNLOCK_FAILURE,
KEY_GENERATION_FAILURE
};
static const QString Send; /* Send addres */
static const QString Receive; /* Receive address */
/* Overridden methods from QAbstractTableModel */
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex & index, const QVariant & value, int role);
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QModelIndex index(int row, int column, const QModelIndex & parent) const;
bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
Qt::ItemFlags flags(const QModelIndex & index) const;
/* Add an address to the model.
Returns the added address on success, and an empty string otherwise.
*/
QString addRow(const QString &type, const QString &label, const QString &address);
/* Update address list from core. Invalidates any indices.
*/
void updateList();
/* Look up label for address in address book, if not found return empty string.
*/
QString labelForAddress(const QString &address) const;
/* Look up row index of an address in the model.
Return -1 if not found.
*/
int lookupAddress(const QString &address) const;
EditStatus getEditStatus() const { return editStatus; }
private:
WalletModel *walletModel;
CWallet *wallet;
AddressTablePriv *priv;
QStringList columns;
EditStatus editStatus;
signals:
void defaultAddressChanged(const QString &address);
public slots:
void update();
};
#endif // ADDRESSTABLEMODEL_H

186
src/qt/askpassphrasedialog.cpp

@ -0,0 +1,186 @@
#include "askpassphrasedialog.h"
#include "ui_askpassphrasedialog.h"
#include "guiconstants.h"
#include "walletmodel.h"
#include <QMessageBox>
#include <QPushButton>
AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) :
QDialog(parent),
ui(new Ui::AskPassphraseDialog),
mode(mode),
model(0)
{
ui->setupUi(this);
ui->passEdit1->setMaxLength(MAX_PASSPHRASE_SIZE);
ui->passEdit2->setMaxLength(MAX_PASSPHRASE_SIZE);
ui->passEdit3->setMaxLength(MAX_PASSPHRASE_SIZE);
switch(mode)
{
case Encrypt: // Ask passphrase x2
ui->passLabel1->hide();
ui->passEdit1->hide();
ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>10 or more random characters</b>, or <b>eight or more words</b>."));
setWindowTitle(tr("Encrypt wallet"));
break;
case Unlock: // Ask passphrase
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to unlock the wallet."));
ui->passLabel2->hide();
ui->passEdit2->hide();
ui->passLabel3->hide();
ui->passEdit3->hide();
setWindowTitle(tr("Unlock wallet"));
break;
case Decrypt: // Ask passphrase
ui->warningLabel->setText(tr("This operation needs your wallet passphrase to decrypt the wallet."));
ui->passLabel2->hide();
ui->passEdit2->hide();
ui->passLabel3->hide();
ui->passEdit3->hide();
setWindowTitle(tr("Decrypt wallet"));
break;
case ChangePass: // Ask old passphrase + new passphrase x2
setWindowTitle(tr("Change passphrase"));
ui->warningLabel->setText(tr("Enter the old and new passphrase to the wallet."));
break;
}
resize(minimumSize()); // Get rid of extra space in dialog
textChanged();
connect(ui->passEdit1, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit2, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
connect(ui->passEdit3, SIGNAL(textChanged(QString)), this, SLOT(textChanged()));
}
AskPassphraseDialog::~AskPassphraseDialog()
{
// Attempt to overwrite text so that they do not linger around in memory
ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size()));
ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size()));
ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size()));
delete ui;
}
void AskPassphraseDialog::setModel(WalletModel *model)
{
this->model = model;
}
void AskPassphraseDialog::accept()
{
std::string oldpass, newpass1, newpass2;
// TODO: mlock memory / munlock on return so they will not be swapped out, really need "mlockedstring" wrapper class to do this safely
oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE);
newpass2.reserve(MAX_PASSPHRASE_SIZE);
oldpass.assign(ui->passEdit1->text().toStdString());
newpass1.assign(ui->passEdit2->text().toStdString());
newpass2.assign(ui->passEdit3->text().toStdString());
switch(mode)
{
case Encrypt: {
if(newpass1.empty() || newpass2.empty())
{
// Cannot encrypt with empty passphrase
break;
}
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm wallet encryption"),
tr("WARNING: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR BITCOINS</b>!\nAre you sure you wish to encrypt your wallet?"),
QMessageBox::Yes|QMessageBox::Cancel,
QMessageBox::Cancel);
if(retval == QMessageBox::Yes)
{
if(newpass1 == newpass2)
{
if(model->setWalletEncrypted(true, newpass1))
{
QMessageBox::warning(this, tr("Wallet encrypted"),
tr("Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer."));
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("Wallet encryption failed due to an internal error. Your wallet was not encrypted."));
}
QDialog::accept(); // Success
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The supplied passphrases do not match."));
}
}
else
{
QDialog::reject(); // Cancelled
}
} break;
case Unlock:
if(!model->setWalletLocked(false, oldpass))
{
QMessageBox::critical(this, tr("Wallet unlock failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
else
{
QDialog::accept(); // Success
}
break;
case Decrypt:
if(!model->setWalletEncrypted(false, oldpass))
{
QMessageBox::critical(this, tr("Wallet decryption failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
else
{
QDialog::accept(); // Success
}
break;
case ChangePass:
if(newpass1 == newpass2)
{
if(model->changePassphrase(oldpass, newpass1))
{
QMessageBox::information(this, tr("Wallet encrypted"),
tr("Wallet passphrase was succesfully changed."));
QDialog::accept(); // Success
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The passphrase entered for the wallet decryption was incorrect."));
}
}
else
{
QMessageBox::critical(this, tr("Wallet encryption failed"),
tr("The supplied passphrases do not match."));
}
break;
}
}
void AskPassphraseDialog::textChanged()
{
// Validate input, set Ok button to enabled when accepable
bool acceptable = false;
switch(mode)
{
case Encrypt: // New passphrase x2
acceptable = !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
case Unlock: // Old passphrase x1
case Decrypt:
acceptable = !ui->passEdit1->text().isEmpty();
break;
case ChangePass: // Old passphrase x1, new passphrase x2
acceptable = !ui->passEdit1->text().isEmpty() && !ui->passEdit2->text().isEmpty() && !ui->passEdit3->text().isEmpty();
break;
}
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
}

40
src/qt/askpassphrasedialog.h

@ -0,0 +1,40 @@
#ifndef ASKPASSPHRASEDIALOG_H
#define ASKPASSPHRASEDIALOG_H
#include <QDialog>
namespace Ui {
class AskPassphraseDialog;
}
class WalletModel;
class AskPassphraseDialog : public QDialog
{
Q_OBJECT
public:
enum Mode {
Encrypt, // Ask passphrase x2
Unlock, // Ask passphrase
ChangePass, // Ask old passphrase + new passphrase x2
Decrypt // Ask passphrase
};
explicit AskPassphraseDialog(Mode mode, QWidget *parent = 0);
~AskPassphraseDialog();
void accept();
void setModel(WalletModel *model);
private:
Ui::AskPassphraseDialog *ui;
Mode mode;
WalletModel *model;
private slots:
void textChanged();
};
#endif // ASKPASSPHRASEDIALOG_H

170
src/qt/bitcoin.cpp

@ -0,0 +1,170 @@
/*
* W.J. van der Laan 2011
*/
#include "bitcoingui.h"
#include "clientmodel.h"
#include "walletmodel.h"
#include "optionsmodel.h"
#include "headers.h"
#include "init.h"
#include <QApplication>
#include <QMessageBox>
#include <QThread>
#include <QTextCodec>
#include <QLocale>
#include <QTranslator>
#include <QSplashScreen>
// Need a global reference for the notifications to find the GUI
BitcoinGUI *guiref;
QSplashScreen *splashref;
int MyMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y)
{
// Message from main thread
if(guiref)
{
guiref->error(QString::fromStdString(caption),
QString::fromStdString(message));
}
else
{
QMessageBox::critical(0, QString::fromStdString(caption),
QString::fromStdString(message),
QMessageBox::Ok, QMessageBox::Ok);
}
return 4;
}
int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style, wxWindow* parent, int x, int y)
{
// Message from network thread
if(guiref)
{
QMetaObject::invokeMethod(guiref, "error", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message)));
}
else
{
printf("%s: %s\n", caption.c_str(), message.c_str());
fprintf(stderr, "%s: %s\n", caption.c_str(), message.c_str());
}
return 4;
}
bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent)
{
if(!guiref)
return false;
if(nFeeRequired < MIN_TX_FEE || nFeeRequired <= nTransactionFee || fDaemon)
return true;
bool payFee = false;
// Call slot on GUI thread.
// If called from another thread, use a blocking QueuedConnection.
Qt::ConnectionType connectionType = Qt::DirectConnection;
if(QThread::currentThread() != QCoreApplication::instance()->thread())
{
connectionType = Qt::BlockingQueuedConnection;
}
QMetaObject::invokeMethod(guiref, "askFee", connectionType,
Q_ARG(qint64, nFeeRequired),
Q_ARG(bool*, &payFee));
return payFee;
}
void CalledSetStatusBar(const std::string& strText, int nField)
{
// Only used for built-in mining, which is disabled, simple ignore
}
void UIThreadCall(boost::function0<void> fn)
{
// Only used for built-in mining, which is disabled, simple ignore
}
void MainFrameRepaint()
{
}
void InitMessage(const std::string &message)
{
if(splashref)
{
splashref->showMessage(QString::fromStdString(message), Qt::AlignBottom|Qt::AlignHCenter, QColor(255,255,200));
QApplication::instance()->processEvents();
}
}
/*
Translate string to current locale using Qt.
*/
std::string _(const char* psz)
{
return QCoreApplication::translate("bitcoin-core", psz).toStdString();
}
int main(int argc, char *argv[])
{
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForTr());
Q_INIT_RESOURCE(bitcoin);
QApplication app(argc, argv);
// Load language file for system locale
QString locale = QLocale::system().name();
QTranslator translator;
translator.load("bitcoin_"+locale);
app.installTranslator(&translator);
QSplashScreen splash(QPixmap(":/images/splash"), 0);
splash.show();
splash.setAutoFillBackground(true);
splashref = &splash;
app.processEvents();
app.setQuitOnLastWindowClosed(false);
try
{
if(AppInit2(argc, argv))
{
{
// Put this in a block, so that BitcoinGUI is cleaned up properly before
// calling Shutdown().
BitcoinGUI window;
splash.finish(&window);
OptionsModel optionsModel(pwalletMain);
ClientModel clientModel(&optionsModel);
WalletModel walletModel(pwalletMain, &optionsModel);
guiref = &window;
window.setClientModel(&clientModel);
window.setWalletModel(&walletModel);
window.show();
app.exec();
guiref = 0;
}
Shutdown(NULL);
}
else
{
return 1;
}
} catch (std::exception& e) {
PrintException(&e, "Runaway exception");
} catch (...) {
PrintException(NULL, "Runaway exception");
}
return 0;
}

48
src/qt/bitcoin.qrc

@ -0,0 +1,48 @@
<RCC>
<qresource prefix="/icons" lang="edit">
<file alias="bitcoin">res/icons/bitcoin.png</file>
<file alias="address-book">res/icons/address-book.png</file>
<file alias="quit">res/icons/quit.png</file>
<file alias="send">res/icons/send.png</file>
<file alias="toolbar">res/icons/toolbar.png</file>
<file alias="connect_0">res/icons/connect0_16.png</file>
<file alias="connect_1">res/icons/connect1_16.png</file>
<file alias="connect_2">res/icons/connect2_16.png</file>
<file alias="connect_3">res/icons/connect3_16.png</file>
<file alias="connect_4">res/icons/connect4_16.png</file>
<file alias="transaction_0">res/icons/transaction0.png</file>
<file alias="transaction_confirmed">res/icons/transaction2.png</file>
<file alias="transaction_1">res/icons/clock1.png</file>
<file alias="transaction_2">res/icons/clock2.png</file>
<file alias="transaction_3">res/icons/clock3.png</file>
<file alias="transaction_4">res/icons/clock4.png</file>
<file alias="transaction_5">res/icons/clock5.png</file>
<file alias="options">res/icons/configure.png</file>
<file alias="receiving_addresses">res/icons/receive.png</file>
<file alias="editpaste">res/icons/editpaste.png</file>
<file alias="editcopy">res/icons/editcopy.png</file>
<file alias="add">res/icons/add.png</file>
<file alias="bitcoin_testnet">res/icons/bitcoin_testnet.png</file>
<file alias="toolbar_testnet">res/icons/toolbar_testnet.png</file>
<file alias="edit">res/icons/edit.png</file>
<file alias="history">res/icons/history.png</file>
<file alias="overview">res/icons/overview.png</file>
<file alias="export">res/icons/export.png</file>
<file alias="synced">res/icons/synced.png</file>
<file alias="remove">res/icons/remove.png</file>
<file alias="tx_mined">res/icons/tx_mined.png</file>
<file alias="tx_input">res/icons/tx_input.png</file>
<file alias="tx_output">res/icons/tx_output.png</file>
<file alias="tx_inout">res/icons/tx_inout.png</file>
<file alias="lock_closed">res/icons/lock_closed.png</file>
<file alias="lock_open">res/icons/lock_open.png</file>
<file alias="key">res/icons/key.png</file>
</qresource>
<qresource prefix="/images">
<file alias="about">res/images/about.png</file>
<file alias="splash">res/images/splash2.jpg</file>
</qresource>
<qresource prefix="/movies">
<file alias="update_spinner">res/movies/update_spinner.mng</file>
</qresource>
</RCC>

67
src/qt/bitcoinaddressvalidator.cpp

@ -0,0 +1,67 @@
#include "bitcoinaddressvalidator.h"
/* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
This is:
- All numbers except for '0'
- All uppercase letters except for 'I' and 'O'
- All lowercase letters except for 'l'
User friendly Base58 input can map
- 'l' and 'I' to '1'
- '0' and 'O' to 'o'
*/
BitcoinAddressValidator::BitcoinAddressValidator(QObject *parent) :
QValidator(parent)
{
}
QValidator::State BitcoinAddressValidator::validate(QString &input, int &pos) const
{
// Correction
for(int idx=0; idx<input.size(); ++idx)
{
switch(input.at(idx).unicode())
{
case 'l':
case 'I':
input[idx] = QChar('1');
break;
case '0':
case 'O':
input[idx] = QChar('o');
break;
default:
break;
}
}
// Validation
QValidator::State state = QValidator::Acceptable;
for(int idx=0; idx<input.size(); ++idx)
{
int ch = input.at(idx).unicode();
if(((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') ||
(ch >= 'A' && ch<='Z')) &&
ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
{
// Alphanumeric and not a 'forbidden' character
}
else
{
state = QValidator::Invalid;
}
}
// Empty address is "intermediate" input
if(input.isEmpty())
{
state = QValidator::Intermediate;
}
return state;
}

24
src/qt/bitcoinaddressvalidator.h

@ -0,0 +1,24 @@
#ifndef BITCOINADDRESSVALIDATOR_H
#define BITCOINADDRESSVALIDATOR_H
#include <QRegExpValidator>
/* Base48 entry widget validator.
Corrects near-miss characters and refuses characters that are no part of base48.
*/
class BitcoinAddressValidator : public QValidator
{
Q_OBJECT
public:
explicit BitcoinAddressValidator(QObject *parent = 0);
State validate(QString &input, int &pos) const;
static const int MaxAddressLength = 34;
signals:
public slots:
};
#endif // BITCOINADDRESSVALIDATOR_H

178
src/qt/bitcoinamountfield.cpp

@ -0,0 +1,178 @@
#include "bitcoinamountfield.h"
#include "qvalidatedlineedit.h"
#include "qvaluecombobox.h"
#include "bitcoinunits.h"
#include <QLabel>
#include <QLineEdit>
#include <QRegExpValidator>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QComboBox>
BitcoinAmountField::BitcoinAmountField(QWidget *parent):
QWidget(parent), amount(0), decimals(0), currentUnit(-1)
{
amount = new QValidatedLineEdit(this);
amount->setValidator(new QRegExpValidator(QRegExp("[0-9]*"), this));
amount->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
amount->installEventFilter(this);
amount->setMaximumWidth(75);
decimals = new QValidatedLineEdit(this);
decimals->setValidator(new QRegExpValidator(QRegExp("[0-9]+"), this));
decimals->setAlignment(Qt::AlignLeft|Qt::AlignVCenter);
decimals->setMaximumWidth(75);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setSpacing(0);
layout->addWidget(amount);
layout->addWidget(new QLabel(QString("<b>.</b>")));
layout->addWidget(decimals);
unit = new QValueComboBox(this);
unit->setModel(new BitcoinUnits(this));
layout->addWidget(unit);
layout->addStretch(1);
layout->setContentsMargins(0,0,0,0);
setLayout(layout);
setFocusPolicy(Qt::TabFocus);
setFocusProxy(amount);
// If one if the widgets changes, the combined content changes as well
connect(amount, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged()));
connect(decimals, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged()));
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
// Set default based on configuration
unitChanged(unit->currentIndex());
}
void BitcoinAmountField::setText(const QString &text)
{
const QStringList parts = text.split(QString("."));
if(parts.size() == 2)
{
amount->setText(parts[0]);
decimals->setText(parts[1]);
}
else
{
amount->setText(QString());
decimals->setText(QString());
}
}
void BitcoinAmountField::clear()
{
amount->clear();
decimals->clear();
unit->setCurrentIndex(0);
}
bool BitcoinAmountField::validate()
{
bool valid = true;
if(decimals->text().isEmpty())
{
decimals->setValid(false);
valid = false;
}
if(!BitcoinUnits::parse(currentUnit, text(), 0))
{
setValid(false);
valid = false;
}
return valid;
}
void BitcoinAmountField::setValid(bool valid)
{
amount->setValid(valid);
decimals->setValid(valid);
}
QString BitcoinAmountField::text() const
{
if(decimals->text().isEmpty() && amount->text().isEmpty())
{
return QString();
}
return amount->text() + QString(".") + decimals->text();
}
// Intercept '.' and ',' keys, if pressed focus a specified widget
bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
{
Q_UNUSED(object);
if(event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Period || keyEvent->key() == Qt::Key_Comma)
{
decimals->setFocus();
decimals->selectAll();
}
}
return false;
}
QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
{
QWidget::setTabOrder(prev, amount);
QWidget::setTabOrder(amount, decimals);
return decimals;
}
qint64 BitcoinAmountField::value(bool *valid_out) const
{
qint64 val_out = 0;
bool valid = BitcoinUnits::parse(currentUnit, text(), &val_out);
if(valid_out)
{
*valid_out = valid;
}
return val_out;
}
void BitcoinAmountField::setValue(qint64 value)
{
setText(BitcoinUnits::format(currentUnit, value));
}
void BitcoinAmountField::unitChanged(int idx)
{
// Use description tooltip for current unit for the combobox
unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
// Determine new unit ID
int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
// Parse current value and convert to new unit
bool valid = false;
qint64 currentValue = value(&valid);
currentUnit = newUnit;
// Set max length after retrieving the value, to prevent truncation
amount->setMaxLength(BitcoinUnits::amountDigits(currentUnit));
decimals->setMaxLength(BitcoinUnits::decimals(currentUnit));
if(valid)
{
// If value was valid, re-place it in the widget with the new unit
setValue(currentValue);
}
else
{
// If current value is invalid, just clear field
setText("");
}
setValid(true);
}
void BitcoinAmountField::setDisplayUnit(int newUnit)
{
unit->setValue(newUnit);
}

59
src/qt/bitcoinamountfield.h

@ -0,0 +1,59 @@
#ifndef BITCOINFIELD_H
#define BITCOINFIELD_H
#include <QWidget>
QT_BEGIN_NAMESPACE
class QValidatedLineEdit;
class QValueComboBox;
QT_END_NAMESPACE
// Coin amount entry widget with separate parts for whole
// coins and decimals.
class BitcoinAmountField: public QWidget
{
Q_OBJECT
Q_PROPERTY(qint64 value READ value WRITE setValue NOTIFY textChanged USER true);
public:
explicit BitcoinAmountField(QWidget *parent = 0);
qint64 value(bool *valid=0) const;
void setValue(qint64 value);
// Mark current valid as invalid in UI
void setValid(bool valid);
bool validate();
// Change current unit
void setDisplayUnit(int unit);
// Make field empty and ready for new input
void clear();
// Qt messes up the tab chain by default in some cases (issue http://bugreports.qt.nokia.com/browse/QTBUG-10907)
// Hence we have to set it up manually
QWidget *setupTabChain(QWidget *prev);
signals:
void textChanged();
protected:
// Intercept '.' and ',' keys, if pressed focus a specified widget
bool eventFilter(QObject *object, QEvent *event);
private:
QValidatedLineEdit *amount;
QValidatedLineEdit *decimals;
QValueComboBox *unit;
int currentUnit;
void setText(const QString &text);
QString text() const;
private slots:
void unitChanged(int idx);
};
#endif // BITCOINFIELD_H

613
src/qt/bitcoingui.cpp

@ -0,0 +1,613 @@
/*
* Qt4 bitcoin GUI.
*
* W.J. van der Laan 2011
*/
#include "bitcoingui.h"
#include "transactiontablemodel.h"
#include "addressbookpage.h"
#include "sendcoinsdialog.h"
#include "optionsdialog.h"
#include "aboutdialog.h"
#include "clientmodel.h"
#include "walletmodel.h"
#include "editaddressdialog.h"
#include "optionsmodel.h"
#include "transactiondescdialog.h"
#include "addresstablemodel.h"
#include "transactionview.h"
#include "overviewpage.h"
#include "bitcoinunits.h"
#include "guiconstants.h"
#include "askpassphrasedialog.h"
#include "notificator.h"
#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QMenu>
#include <QIcon>
#include <QTabWidget>
#include <QVBoxLayout>
#include <QToolBar>
#include <QStatusBar>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QLocale>
#include <QMessageBox>
#include <QProgressBar>
#include <QStackedWidget>
#include <QDateTime>
#include <QMovie>
#include <QDragEnterEvent>
#include <QUrl>
#include <iostream>
BitcoinGUI::BitcoinGUI(QWidget *parent):
QMainWindow(parent),
clientModel(0),
walletModel(0),
encryptWalletAction(0),
changePassphraseAction(0),
trayIcon(0),
notificator(0)
{
resize(850, 550);
setWindowTitle(tr("Bitcoin Wallet"));
setWindowIcon(QIcon(":icons/bitcoin"));
// Accept D&D of URIs
setAcceptDrops(true);
createActions();
// Menus
QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(sendCoinsAction);
file->addAction(receiveCoinsAction);
file->addSeparator();
file->addAction(quitAction);
QMenu *settings = menuBar()->addMenu(tr("&Settings"));
settings->addAction(encryptWalletAction);
settings->addAction(changePassphraseAction);
settings->addSeparator();
settings->addAction(optionsAction);
QMenu *help = menuBar()->addMenu(tr("&Help"));
help->addAction(aboutAction);
// Toolbars
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar->addAction(overviewAction);
toolbar->addAction(sendCoinsAction);
toolbar->addAction(receiveCoinsAction);
toolbar->addAction(historyAction);
toolbar->addAction(addressBookAction);
QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toolbar2->addAction(exportAction);
// Create tabs
overviewPage = new OverviewPage();
transactionsPage = new QWidget(this);
QVBoxLayout *vbox = new QVBoxLayout();
transactionView = new TransactionView(this);
vbox->addWidget(transactionView);
transactionsPage->setLayout(vbox);
addressBookPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab);
receiveCoinsPage = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::ReceivingTab);
sendCoinsPage = new SendCoinsDialog(this);
centralWidget = new QStackedWidget(this);
centralWidget->addWidget(overviewPage);
centralWidget->addWidget(transactionsPage);
centralWidget->addWidget(addressBookPage);
centralWidget->addWidget(receiveCoinsPage);
centralWidget->addWidget(sendCoinsPage);
setCentralWidget(centralWidget);
// Create status bar
statusBar();
// Status bar notification icons
QFrame *frameBlocks = new QFrame();
//frameBlocks->setFrameStyle(QFrame::Panel | QFrame::Sunken);
frameBlocks->setContentsMargins(0,0,0,0);
frameBlocks->setMinimumWidth(56);
frameBlocks->setMaximumWidth(56);
QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks);
frameBlocksLayout->setContentsMargins(3,0,3,0);
frameBlocksLayout->setSpacing(3);
labelEncryptionIcon = new QLabel();
labelConnectionsIcon = new QLabel();
labelBlocksIcon = new QLabel();
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelEncryptionIcon);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelConnectionsIcon);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelBlocksIcon);
frameBlocksLayout->addStretch();
// Progress bar for blocks download
progressBarLabel = new QLabel(tr("Synchronizing with network..."));
progressBarLabel->setVisible(false);
progressBar = new QProgressBar();
progressBar->setToolTip(tr("Block chain synchronization in progress"));
progressBar->setVisible(false);
statusBar()->addWidget(progressBarLabel);
statusBar()->addWidget(progressBar);
statusBar()->addPermanentWidget(frameBlocks);
createTrayIcon();
syncIconMovie = new QMovie(":/movies/update_spinner", "mng", this);
// Clicking on a transaction on the overview page simply sends you to transaction history page
connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage()));
// Doubleclicking on a transaction on the transaction history page shows details
connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
gotoOverviewPage();
}
void BitcoinGUI::createActions()
{
QActionGroup *tabGroup = new QActionGroup(this);
overviewAction = new QAction(QIcon(":/icons/overview"), tr("&Overview"), this);
overviewAction->setToolTip(tr("Show general overview of wallet"));
overviewAction->setCheckable(true);
tabGroup->addAction(overviewAction);
historyAction = new QAction(QIcon(":/icons/history"), tr("&Transactions"), this);
historyAction->setToolTip(tr("Browse transaction history"));
historyAction->setCheckable(true);
tabGroup->addAction(historyAction);
addressBookAction = new QAction(QIcon(":/icons/address-book"), tr("&Address Book"), this);
addressBookAction->setToolTip(tr("Edit the list of stored addresses and labels"));
addressBookAction->setCheckable(true);
tabGroup->addAction(addressBookAction);
receiveCoinsAction = new QAction(QIcon(":/icons/receiving_addresses"), tr("&Receive coins"), this);
receiveCoinsAction->setToolTip(tr("Show the list of addresses for receiving payments"));
receiveCoinsAction->setCheckable(true);
tabGroup->addAction(receiveCoinsAction);
sendCoinsAction = new QAction(QIcon(":/icons/send"), tr("&Send coins"), this);
sendCoinsAction->setToolTip(tr("Send coins to a bitcoin address"));
sendCoinsAction->setCheckable(true);
tabGroup->addAction(sendCoinsAction);
connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage()));
connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage()));
connect(addressBookAction, SIGNAL(triggered()), this, SLOT(gotoAddressBookPage()));
connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage()));
connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage()));
quitAction = new QAction(QIcon(":/icons/quit"), tr("&Exit"), this);
quitAction->setToolTip(tr("Quit application"));
aboutAction = new QAction(QIcon(":/icons/bitcoin"), tr("&About"), this);
aboutAction->setToolTip(tr("Show information about Bitcoin"));
optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this);
optionsAction->setToolTip(tr("Modify configuration options for bitcoin"));
openBitcoinAction = new QAction(QIcon(":/icons/bitcoin"), tr("Open &Bitcoin"), this);
openBitcoinAction->setToolTip(tr("Show the Bitcoin window"));
exportAction = new QAction(QIcon(":/icons/export"), tr("&Export..."), this);
exportAction->setToolTip(tr("Export the current view to a file"));
encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet"), this);
encryptWalletAction->setToolTip(tr("Encrypt or decrypt wallet"));
encryptWalletAction->setCheckable(true);
changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase"), this);
changePassphraseAction->setToolTip(tr("Change the passphrase used for wallet encryption"));
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
connect(optionsAction, SIGNAL(triggered()), this, SLOT(optionsClicked()));
connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutClicked()));
connect(openBitcoinAction, SIGNAL(triggered()), this, SLOT(showNormal()));
connect(encryptWalletAction, SIGNAL(triggered(bool)), this, SLOT(encryptWallet(bool)));
connect(changePassphraseAction, SIGNAL(triggered()), this, SLOT(changePassphrase()));
}
void BitcoinGUI::setClientModel(ClientModel *clientModel)
{
this->clientModel = clientModel;
if(clientModel->isTestNet())
{
QString title_testnet = windowTitle() + QString(" ") + tr("[testnet]");
setWindowTitle(title_testnet);
setWindowIcon(QIcon(":icons/bitcoin_testnet"));
if(trayIcon)
{
trayIcon->setToolTip(title_testnet);
trayIcon->setIcon(QIcon(":/icons/toolbar_testnet"));
}
}
// Keep up to date with client
setNumConnections(clientModel->getNumConnections());
connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
setNumBlocks(clientModel->getNumBlocks());
connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
// Report errors from network/worker thread
connect(clientModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString)));
}
void BitcoinGUI::setWalletModel(WalletModel *walletModel)
{
this->walletModel = walletModel;
// Report errors from wallet thread
connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString)));
// Put transaction list in tabs
transactionView->setModel(walletModel);
overviewPage->setModel(walletModel);
addressBookPage->setModel(walletModel->getAddressTableModel());
receiveCoinsPage->setModel(walletModel->getAddressTableModel());
sendCoinsPage->setModel(walletModel);
setEncryptionStatus(walletModel->getEncryptionStatus());
connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int)));
// Balloon popup for new transaction
connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(incomingTransaction(QModelIndex,int,int)));
// Ask for passphrase if needed
connect(walletModel, SIGNAL(requireUnlock()), this, SLOT(unlockWallet()));
}
void BitcoinGUI::createTrayIcon()
{
QMenu *trayIconMenu = new QMenu(this);
trayIconMenu->addAction(openBitcoinAction);
trayIconMenu->addAction(optionsAction);
trayIconMenu->addSeparator();
trayIconMenu->addAction(quitAction);
trayIcon = new QSystemTrayIcon(this);
trayIcon->setContextMenu(trayIconMenu);
trayIcon->setToolTip("Bitcoin client");
trayIcon->setIcon(QIcon(":/icons/toolbar"));
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, SLOT(trayIconActivated(QSystemTrayIcon::ActivationReason)));
trayIcon->show();
notificator = new Notificator(tr("bitcoin-qt"), trayIcon);
}
void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
if(reason == QSystemTrayIcon::Trigger)
{
// Click on system tray icon triggers "open bitcoin"
openBitcoinAction->trigger();
}
}
void BitcoinGUI::optionsClicked()
{
OptionsDialog dlg;
dlg.setModel(clientModel->getOptionsModel());
dlg.exec();
}
void BitcoinGUI::aboutClicked()
{
AboutDialog dlg;
dlg.setModel(clientModel);
dlg.exec();
}
void BitcoinGUI::setNumConnections(int count)
{
QString icon;
switch(count)
{
case 0: icon = ":/icons/connect_0"; break;
case 1: case 2: case 3: icon = ":/icons/connect_1"; break;
case 4: case 5: case 6: icon = ":/icons/connect_2"; break;
case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
default: icon = ":/icons/connect_4"; break;
}
labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
}
void BitcoinGUI::setNumBlocks(int count)
{
int initTotal = clientModel->getNumBlocksAtStartup();
int total = clientModel->getNumBlocksOfPeers();
QString tooltip;
if(count < total)
{
progressBarLabel->setVisible(true);
progressBar->setVisible(true);
progressBar->setMaximum(total - initTotal);
progressBar->setValue(count - initTotal);
tooltip = tr("Downloaded %1 of %2 blocks of transaction history.").arg(count).arg(total);
}
else
{
progressBarLabel->setVisible(false);
progressBar->setVisible(false);
tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count);
}
QDateTime now = QDateTime::currentDateTime();
QDateTime lastBlockDate = clientModel->getLastBlockDate();
int secs = lastBlockDate.secsTo(now);
QString text;
// Represent time from last generated block in human readable text
if(secs < 60)
{
text = tr("%n second(s) ago","",secs);
}
else if(secs < 60*60)
{
text = tr("%n minute(s) ago","",secs/60);
}
else if(secs < 24*60*60)
{
text = tr("%n hour(s) ago","",secs/(60*60));
}
else
{
text = tr("%n day(s) ago","",secs/(60*60*24));
}
// Set icon state: spinning if catching up, tick otherwise
if(secs < 30*60)
{
tooltip = tr("Up to date") + QString("\n") + tooltip;
labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
}
else
{
tooltip = tr("Catching up...") + QString("\n") + tooltip;
labelBlocksIcon->setMovie(syncIconMovie);
syncIconMovie->start();
}
tooltip += QString("\n");
tooltip += tr("Last received block was generated %1.").arg(text);
labelBlocksIcon->setToolTip(tooltip);
progressBarLabel->setToolTip(tooltip);
progressBar->setToolTip(tooltip);
}
void BitcoinGUI::error(const QString &title, const QString &message)
{
// Report errors from network/worker thread
notificator->notify(Notificator::Critical, title, message);
}
void BitcoinGUI::changeEvent(QEvent *e)
{
if (e->type() == QEvent::WindowStateChange)
{
if(clientModel->getOptionsModel()->getMinimizeToTray())
{
if (isMinimized())
{
hide();
e->ignore();
}
else
{
show();
e->accept();
}
}
}
QMainWindow::changeEvent(e);
}
void BitcoinGUI::closeEvent(QCloseEvent *event)
{
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
!clientModel->getOptionsModel()->getMinimizeOnClose())
{
qApp->quit();
}
QMainWindow::closeEvent(event);
}
void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
{
QString strMessage =
tr("This transaction is over the size limit. You can still send it for a fee of %1, "
"which goes to the nodes that process your transaction and helps to support the network. "
"Do you want to pay the fee?").arg(
BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired));
QMessageBox::StandardButton retval = QMessageBox::question(
this, tr("Sending..."), strMessage,
QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes);
*payFee = (retval == QMessageBox::Yes);
}
void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end)
{
TransactionTableModel *ttm = walletModel->getTransactionTableModel();
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent)
.data(Qt::EditRole).toULongLong();
if(!clientModel->inInitialBlockDownload())
{
// On new transaction, make an info balloon
// Unless the initial block download is in progress, to prevent balloon-spam
QString date = ttm->index(start, TransactionTableModel::Date, parent)
.data().toString();
QString type = ttm->index(start, TransactionTableModel::Type, parent)
.data().toString();
QString address = ttm->index(start, TransactionTableModel::ToAddress, parent)
.data().toString();
QIcon icon = qvariant_cast<QIcon>(ttm->index(start,
TransactionTableModel::ToAddress, parent)
.data(Qt::DecorationRole));
notificator->notify(Notificator::Information,
(amount)<0 ? tr("Sent transaction") :
tr("Incoming transaction"),
tr("Date: %1\n"
"Amount: %2\n"
"Type: %3\n"
"Address: %4\n")
.arg(date)
.arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), amount, true))
.arg(type)
.arg(address), icon);
}
}
void BitcoinGUI::gotoOverviewPage()
{
overviewAction->setChecked(true);
centralWidget->setCurrentWidget(overviewPage);
exportAction->setEnabled(false);
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
}
void BitcoinGUI::gotoHistoryPage()
{
historyAction->setChecked(true);
centralWidget->setCurrentWidget(transactionsPage);
exportAction->setEnabled(true);
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
connect(exportAction, SIGNAL(triggered()), transactionView, SLOT(exportClicked()));
}
void BitcoinGUI::gotoAddressBookPage()
{
addressBookAction->setChecked(true);
centralWidget->setCurrentWidget(addressBookPage);
exportAction->setEnabled(true);
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
connect(exportAction, SIGNAL(triggered()), addressBookPage, SLOT(exportClicked()));
}
void BitcoinGUI::gotoReceiveCoinsPage()
{
receiveCoinsAction->setChecked(true);
centralWidget->setCurrentWidget(receiveCoinsPage);
exportAction->setEnabled(true);
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
connect(exportAction, SIGNAL(triggered()), receiveCoinsPage, SLOT(exportClicked()));
}
void BitcoinGUI::gotoSendCoinsPage()
{
sendCoinsAction->setChecked(true);
if(centralWidget->currentWidget() != sendCoinsPage)
{
// Clear the current contents if we arrived from another tab
sendCoinsPage->clear();
}
centralWidget->setCurrentWidget(sendCoinsPage);
exportAction->setEnabled(false);
disconnect(exportAction, SIGNAL(triggered()), 0, 0);
}
void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event)
{
// Accept only URLs
if(event->mimeData()->hasUrls())
event->acceptProposedAction();
}
void BitcoinGUI::dropEvent(QDropEvent *event)
{
if(event->mimeData()->hasUrls())
{
gotoSendCoinsPage();
QList<QUrl> urls = event->mimeData()->urls();
foreach(const QUrl &url, urls)
{
sendCoinsPage->handleURL(&url);
}
}
event->acceptProposedAction();
}
void BitcoinGUI::setEncryptionStatus(int status)
{
switch(status)
{
case WalletModel::Unencrypted:
labelEncryptionIcon->hide();
encryptWalletAction->setChecked(false);
changePassphraseAction->setEnabled(false);
encryptWalletAction->setEnabled(true);
break;
case WalletModel::Unlocked:
labelEncryptionIcon->show();
labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
break;
case WalletModel::Locked:
labelEncryptionIcon->show();
labelEncryptionIcon->setPixmap(QIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
labelEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
encryptWalletAction->setChecked(true);
changePassphraseAction->setEnabled(true);
encryptWalletAction->setEnabled(false); // TODO: decrypt currently not supported
break;
}
}
void BitcoinGUI::encryptWallet(bool status)
{
AskPassphraseDialog dlg(status ? AskPassphraseDialog::Encrypt:
AskPassphraseDialog::Decrypt, this);
dlg.setModel(walletModel);
dlg.exec();
setEncryptionStatus(walletModel->getEncryptionStatus());
}
void BitcoinGUI::changePassphrase()
{
AskPassphraseDialog dlg(AskPassphraseDialog::ChangePass, this);
dlg.setModel(walletModel);
dlg.exec();
}
void BitcoinGUI::unlockWallet()
{
// Unlock wallet when requested by wallet model
if(walletModel->getEncryptionStatus() == WalletModel::Locked)
{
AskPassphraseDialog dlg(AskPassphraseDialog::Unlock, this);
dlg.setModel(walletModel);
dlg.exec();
}
}

120
src/qt/bitcoingui.h

@ -0,0 +1,120 @@
#ifndef BITCOINGUI_H
#define BITCOINGUI_H
#include <QMainWindow>
#include <QSystemTrayIcon>
class TransactionTableModel;
class ClientModel;
class WalletModel;
class TransactionView;
class OverviewPage;
class AddressBookPage;
class SendCoinsDialog;
class Notificator;
QT_BEGIN_NAMESPACE
class QLabel;
class QLineEdit;
class QTableView;
class QAbstractItemModel;
class QModelIndex;
class QProgressBar;
class QStackedWidget;
class QUrl;
QT_END_NAMESPACE
class BitcoinGUI : public QMainWindow
{
Q_OBJECT
public:
explicit BitcoinGUI(QWidget *parent = 0);
void setClientModel(ClientModel *clientModel);
void setWalletModel(WalletModel *walletModel);
/* Transaction table tab indices */
enum {
AllTransactions = 0,
SentReceived = 1,
Sent = 2,
Received = 3
} TabIndex;
protected:
void changeEvent(QEvent *e);
void closeEvent(QCloseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
private:
ClientModel *clientModel;
WalletModel *walletModel;
QStackedWidget *centralWidget;
OverviewPage *overviewPage;
QWidget *transactionsPage;
AddressBookPage *addressBookPage;
AddressBookPage *receiveCoinsPage;
SendCoinsDialog *sendCoinsPage;
QLabel *labelEncryptionIcon;
QLabel *labelConnectionsIcon;
QLabel *labelBlocksIcon;
QLabel *progressBarLabel;
QProgressBar *progressBar;
QAction *overviewAction;
QAction *historyAction;
QAction *quitAction;
QAction *sendCoinsAction;
QAction *addressBookAction;
QAction *aboutAction;
QAction *receiveCoinsAction;
QAction *optionsAction;
QAction *openBitcoinAction;
QAction *exportAction;
QAction *encryptWalletAction;
QAction *changePassphraseAction;
QSystemTrayIcon *trayIcon;
Notificator *notificator;
TransactionView *transactionView;
QMovie *syncIconMovie;
void createActions();
QWidget *createTabs();
void createTrayIcon();
public slots:
void setNumConnections(int count);
void setNumBlocks(int count);
void setEncryptionStatus(int status);
void error(const QString &title, const QString &message);
/* It is currently not possible to pass a return value to another thread through
BlockingQueuedConnection, so use an indirected pointer.
http://bugreports.qt.nokia.com/browse/QTBUG-10440
*/
void askFee(qint64 nFeeRequired, bool *payFee);
private slots:
// UI pages
void gotoOverviewPage();
void gotoHistoryPage();
void gotoAddressBookPage();
void gotoReceiveCoinsPage();
void gotoSendCoinsPage();
// Misc actions
void optionsClicked();
void aboutClicked();
void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
void incomingTransaction(const QModelIndex & parent, int start, int end);
void encryptWallet(bool status);
void changePassphrase();
void unlockWallet();
};
#endif

217
src/qt/bitcoinstrings.cpp

@ -0,0 +1,217 @@
#include <QtGlobal>
// Automatically generated by extract_strings.py
static const char *bitcoin_strings[] = {QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin version"),
QT_TRANSLATE_NOOP("bitcoin-core", "Usage:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Send command to -server or bitcoind\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "List commands\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Options:\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify configuration file (default: bitcoin.conf)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify pid file (default: bitcoind.pid)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Don't generate coins\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Start minimized\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify data directory\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Specify connection timeout (in milliseconds)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connect through socks4 proxy\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for addnode and connect\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Don't accept connections from outside\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Don't attempt to use UPnP to map the listening port\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to use UPnP to map the listening port\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Fee per KB to add to transactions you send\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Run in the background as a daemon and accept commands\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on <port> (default: 8332)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Allow JSON-RPC connections from specified IP address\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Send commands to node running on <ip> (default: 127.0.0.1)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Set key pool size to <n> (default: 100)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescan the block chain for missing wallet transactions\n"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"\n"
"SSL options: (see the Bitcoin Wiki for SSL setup instructions)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use OpenSSL (https) for JSON-RPC connections\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Server certificate file (default: server.cert)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Server private key (default: server.pem)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:"
"@STRENGTH)\n"),
QT_TRANSLATE_NOOP("bitcoin-core", "This help message\n"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. Bitcoin is probably already "
"running."),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading addr.dat \n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading blkindex.dat \n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted \n"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error loading wallet.dat: Wallet requires newer version of Bitcoin \n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat \n"),
QT_TRANSLATE_NOOP("bitcoin-core", "Rescanning..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: -paytxfee is set very high. This is the transaction fee you will "
"pay if you send a transaction."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: CreateThread(StartNode) failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning: Disk space is low "),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Unable to bind to port %d on this computer. Bitcoin is probably already "
"running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"This transaction is over the size limit. You can still send it for a fee of "
"%s, which goes to the nodes that process your transaction and helps to "
"support the network. Do you want to pay the fee?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Enter the current passphrase to the wallet."),
QT_TRANSLATE_NOOP("bitcoin-core", "Passphrase"),
QT_TRANSLATE_NOOP("bitcoin-core", "Please supply the current wallet decryption passphrase."),
QT_TRANSLATE_NOOP("bitcoin-core", "The passphrase entered for the wallet decryption was incorrect."),
QT_TRANSLATE_NOOP("bitcoin-core", "Status"),
QT_TRANSLATE_NOOP("bitcoin-core", "Date"),
QT_TRANSLATE_NOOP("bitcoin-core", "Description"),
QT_TRANSLATE_NOOP("bitcoin-core", "Debit"),
QT_TRANSLATE_NOOP("bitcoin-core", "Credit"),
QT_TRANSLATE_NOOP("bitcoin-core", "Open for %d blocks"),
QT_TRANSLATE_NOOP("bitcoin-core", "Open until %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "%d/offline?"),
QT_TRANSLATE_NOOP("bitcoin-core", "%d/unconfirmed"),
QT_TRANSLATE_NOOP("bitcoin-core", "%d confirmations"),
QT_TRANSLATE_NOOP("bitcoin-core", "Generated"),
QT_TRANSLATE_NOOP("bitcoin-core", "Generated (%s matures in %d more blocks)"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Generated - Warning: This block was not received by any other nodes and will "
"probably not be accepted!"),
QT_TRANSLATE_NOOP("bitcoin-core", "Generated (not accepted)"),
QT_TRANSLATE_NOOP("bitcoin-core", "From: "),
QT_TRANSLATE_NOOP("bitcoin-core", "Received with: "),
QT_TRANSLATE_NOOP("bitcoin-core", "Payment to yourself"),
QT_TRANSLATE_NOOP("bitcoin-core", "To: "),
QT_TRANSLATE_NOOP("bitcoin-core", " Generating"),
QT_TRANSLATE_NOOP("bitcoin-core", "(not connected)"),
QT_TRANSLATE_NOOP("bitcoin-core", " %d connections %d blocks %d transactions"),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet already encrypted."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Enter the new passphrase to the wallet.\n"
"Please use a passphrase of 10 or more random characters, or eight or more "
"words."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: The supplied passphrase was too short."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"WARNING: If you encrypt your wallet and lose your passphrase, you will LOSE "
"ALL OF YOUR BITCOINS!\n"
"Are you sure you wish to encrypt your wallet?"),
QT_TRANSLATE_NOOP("bitcoin-core", "Please re-enter your new wallet passphrase."),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: the supplied passphrases didn't match."),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet encryption failed."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Wallet Encrypted.\n"
"Remember that encrypting your wallet cannot fully protect your bitcoins from "
"being stolen by malware infecting your computer."),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet is unencrypted, please encrypt it first."),
QT_TRANSLATE_NOOP("bitcoin-core", "Enter the new passphrase for the wallet."),
QT_TRANSLATE_NOOP("bitcoin-core", "Re-enter the new passphrase for the wallet."),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet Passphrase Changed."),
QT_TRANSLATE_NOOP("bitcoin-core", "New Receiving Address"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"You should use a new address for each payment you receive.\n"
"\n"
"Label"),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Status:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", ", has not been successfully broadcast yet"),
QT_TRANSLATE_NOOP("bitcoin-core", ", broadcast through %d node"),
QT_TRANSLATE_NOOP("bitcoin-core", ", broadcast through %d nodes"),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Date:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Source:</b> Generated<br>"),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>From:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", "unknown"),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>To:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", " (yours, label: "),
QT_TRANSLATE_NOOP("bitcoin-core", " (yours)"),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Credit:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", "(%s matures in %d more blocks)"),
QT_TRANSLATE_NOOP("bitcoin-core", "(not accepted)"),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Debit:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Transaction fee:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", "<b>Net amount:</b> "),
QT_TRANSLATE_NOOP("bitcoin-core", "Message:"),
QT_TRANSLATE_NOOP("bitcoin-core", "Comment:"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Generated coins must wait 120 blocks before they can be spent. When you "
"generated this block, it was broadcast to the network to be added to the "
"block chain. If it fails to get into the chain, it will change to \"not "
"accepted\" and not be spendable. This may occasionally happen if another "
"node generates a block within a few seconds of yours."),
QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write autostart/bitcoin.desktop file"),
QT_TRANSLATE_NOOP("bitcoin-core", "Main"),
QT_TRANSLATE_NOOP("bitcoin-core", "&Start Bitcoin on window system startup"),
QT_TRANSLATE_NOOP("bitcoin-core", "&Minimize on close"),
QT_TRANSLATE_NOOP("bitcoin-core", "version %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error in amount "),
QT_TRANSLATE_NOOP("bitcoin-core", "Send Coins"),
QT_TRANSLATE_NOOP("bitcoin-core", "Amount exceeds your balance "),
QT_TRANSLATE_NOOP("bitcoin-core", "Total exceeds your balance when the "),
QT_TRANSLATE_NOOP("bitcoin-core", " transaction fee is included "),
QT_TRANSLATE_NOOP("bitcoin-core", "Payment sent "),
QT_TRANSLATE_NOOP("bitcoin-core", "Sending..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid address "),
QT_TRANSLATE_NOOP("bitcoin-core", "Sending %s to %s"),
QT_TRANSLATE_NOOP("bitcoin-core", "CANCELLED"),
QT_TRANSLATE_NOOP("bitcoin-core", "Cancelled"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transfer cancelled "),
QT_TRANSLATE_NOOP("bitcoin-core", "Error: "),
QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Connecting..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Unable to connect"),
QT_TRANSLATE_NOOP("bitcoin-core", "Requesting public key..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Received public key..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Recipient is not accepting transactions sent by IP address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transfer was not accepted"),
QT_TRANSLATE_NOOP("bitcoin-core", "Invalid response received"),
QT_TRANSLATE_NOOP("bitcoin-core", "Creating transaction..."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"This transaction requires a transaction fee of at least %s because of its "
"amount, complexity, or use of recently received funds"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction creation failed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Transaction aborted"),
QT_TRANSLATE_NOOP("bitcoin-core", "Lost connection, transaction cancelled"),
QT_TRANSLATE_NOOP("bitcoin-core", "Sending payment..."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"The transaction was rejected. This might happen if some of the coins in "
"your wallet were already spent, such as if you used a copy of wallet.dat and "
"coins were spent in the copy but not marked as spent here."),
QT_TRANSLATE_NOOP("bitcoin-core", "Waiting for confirmation..."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"The payment was sent, but the recipient was unable to verify it.\n"
"The transaction is recorded and will credit to the recipient,\n"
"but the comment information will be blank."),
QT_TRANSLATE_NOOP("bitcoin-core", "Payment was sent, but an invalid response was received"),
QT_TRANSLATE_NOOP("bitcoin-core", "Payment completed"),
QT_TRANSLATE_NOOP("bitcoin-core", "Name"),
QT_TRANSLATE_NOOP("bitcoin-core", "Address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Label"),
QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Address"),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"This is one of your own addresses for receiving payments and cannot be "
"entered in the address book. "),
QT_TRANSLATE_NOOP("bitcoin-core", "Edit Address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Edit Address Label"),
QT_TRANSLATE_NOOP("bitcoin-core", "Add Address"),
QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin"),
QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin - Generating"),
QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin - (not connected)"),
QT_TRANSLATE_NOOP("bitcoin-core", "&Open Bitcoin"),
QT_TRANSLATE_NOOP("bitcoin-core", "&Send Bitcoins"),
QT_TRANSLATE_NOOP("bitcoin-core", "O&ptions..."),
QT_TRANSLATE_NOOP("bitcoin-core", "E&xit"),
QT_TRANSLATE_NOOP("bitcoin-core", "Program has crashed and will terminate. "),
QT_TRANSLATE_NOOP("bitcoin-core", ""
"Warning: Please check that your computer's date and time are correct. If "
"your clock is wrong Bitcoin will not work properly."),
QT_TRANSLATE_NOOP("bitcoin-core", "beta"),
};

181
src/qt/bitcoinunits.cpp

@ -0,0 +1,181 @@
#include "bitcoinunits.h"
#include <QStringList>
BitcoinUnits::BitcoinUnits(QObject *parent):
QAbstractListModel(parent),
unitlist(availableUnits())
{
}
QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits()
{
QList<BitcoinUnits::Unit> unitlist;
unitlist.append(BTC);
unitlist.append(mBTC);
unitlist.append(uBTC);
return unitlist;
}
bool BitcoinUnits::valid(int unit)
{
switch(unit)
{
case BTC:
case mBTC:
case uBTC:
return true;
default:
return false;
}
}
QString BitcoinUnits::name(int unit)
{
switch(unit)
{
case BTC: return QString("BTC");
case mBTC: return QString("mBTC");
case uBTC: return QString::fromUtf8("μBTC");
default: return QString("???");
}
}
QString BitcoinUnits::description(int unit)
{
switch(unit)
{
case BTC: return QString("Bitcoins");
case mBTC: return QString("Milli-Bitcoins (1 / 1,000)");
case uBTC: return QString("Micro-Bitcoins (1 / 1,000,000)");
default: return QString("???");
}
}
qint64 BitcoinUnits::factor(int unit)
{
switch(unit)
{
case BTC: return 100000000;
case mBTC: return 100000;
case uBTC: return 100;
default: return 100000000;
}
}
int BitcoinUnits::amountDigits(int unit)
{
switch(unit)
{
case BTC: return 8; // 21,000,000 (# digits, without commas)
case mBTC: return 11; // 21,000,000,000
case uBTC: return 14; // 21,000,000,000,000
default: return 0;
}
}
int BitcoinUnits::decimals(int unit)
{
switch(unit)
{
case BTC: return 8;
case mBTC: return 5;
case uBTC: return 2;
default: return 0;
}
}
QString BitcoinUnits::format(int unit, qint64 n, bool fPlus)
{
// Note: not using straight sprintf here because we do NOT want
// localized number formatting.
if(!valid(unit))
return QString(); // Refuse to format invalid unit
qint64 coin = factor(unit);
int num_decimals = decimals(unit);
qint64 n_abs = (n > 0 ? n : -n);
qint64 quotient = n_abs / coin;
qint64 remainder = n_abs % coin;
QString quotient_str = QString::number(quotient);
QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0');
// Right-trim excess 0's after the decimal point
int nTrim = 0;
for (int i = remainder_str.size()-1; i>=2 && (remainder_str.at(i) == '0'); --i)
++nTrim;
remainder_str.chop(nTrim);
if (n < 0)
quotient_str.insert(0, '-');
else if (fPlus && n > 0)
quotient_str.insert(0, '+');
return quotient_str + QString(".") + remainder_str;
}
QString BitcoinUnits::formatWithUnit(int unit, qint64 amount, bool plussign)
{
return format(unit, amount, plussign) + QString(" ") + name(unit);
}
bool BitcoinUnits::parse(int unit, const QString &value, qint64 *val_out)
{
if(!valid(unit) || value.isEmpty())
return false; // Refuse to parse invalid unit or empty string
int num_decimals = decimals(unit);
QStringList parts = value.split(".");
if(parts.size() > 2)
{
return false; // More than one dot
}
QString whole = parts[0];
QString decimals;
if(parts.size() > 1)
{
decimals = parts[1];
}
if(decimals.size() > num_decimals)
{
return false; // Exceeds max precision
}
bool ok = false;
QString str = whole + decimals.leftJustified(num_decimals, '0');
if(str.size() > 18)
{
return false; // Longer numbers will exceed 63 bits
}
qint64 retvalue = str.toLongLong(&ok);
if(val_out)
{
*val_out = retvalue;
}
return ok;
}
int BitcoinUnits::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return unitlist.size();
}
QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
{
int row = index.row();
if(row >= 0 && row < unitlist.size())
{
Unit unit = unitlist.at(row);
switch(role)
{
case Qt::EditRole:
case Qt::DisplayRole:
return QVariant(name(unit));
case Qt::ToolTipRole:
return QVariant(description(unit));
case UnitRole:
return QVariant(static_cast<int>(unit));
}
}
return QVariant();
}

57
src/qt/bitcoinunits.h

@ -0,0 +1,57 @@
#ifndef BITCOINUNITS_H
#define BITCOINUNITS_H
#include <QString>
#include <QAbstractListModel>
// Bitcoin unit definitions, encapsulates parsing and formatting
// and serves as list model for dropdown selection boxes.
class BitcoinUnits: public QAbstractListModel
{
public:
explicit BitcoinUnits(QObject *parent);
enum Unit
{
// Source: https://en.bitcoin.it/wiki/Units
// Please add only sensible ones
BTC,
mBTC,
uBTC
};
/// Static API
// Get list of units, for dropdown box
static QList<Unit> availableUnits();
// Is unit ID valid?
static bool valid(int unit);
// Short name
static QString name(int unit);
// Longer description
static QString description(int unit);
// Number of satoshis / unit
static qint64 factor(int unit);
// Number of amount digits (to represent max number of coins)
static int amountDigits(int unit);
// Number of decimals left
static int decimals(int unit);
// Format as string
static QString format(int unit, qint64 amount, bool plussign=false);
// Format as string (with unit)
static QString formatWithUnit(int unit, qint64 amount, bool plussign=false);
// Parse string to coin amount
static bool parse(int unit, const QString &value, qint64 *val_out);
/// AbstractListModel implementation
enum {
// Unit identifier
UnitRole = Qt::UserRole
} RoleIndex;
int rowCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
private:
QList<BitcoinUnits::Unit> unitlist;
};
typedef BitcoinUnits::Unit BitcoinUnit;
#endif // BITCOINUNITS_H

83
src/qt/clientmodel.cpp

@ -0,0 +1,83 @@
#include "clientmodel.h"
#include "guiconstants.h"
#include "optionsmodel.h"
#include "addresstablemodel.h"
#include "transactiontablemodel.h"
#include "headers.h"
#include <QTimer>
#include <QDateTime>
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
QObject(parent), optionsModel(optionsModel),
cachedNumConnections(0), cachedNumBlocks(0)
{
// Until signal notifications is built into the bitcoin core,
// simply update everything after polling using a timer.
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start(MODEL_UPDATE_DELAY);
numBlocksAtStartup = -1;
}
int ClientModel::getNumConnections() const
{
return vNodes.size();
}
int ClientModel::getNumBlocks() const
{
return nBestHeight;
}
int ClientModel::getNumBlocksAtStartup()
{
if (numBlocksAtStartup == -1) numBlocksAtStartup = getNumBlocks();
return numBlocksAtStartup;
}
QDateTime ClientModel::getLastBlockDate() const
{
return QDateTime::fromTime_t(pindexBest->GetBlockTime());
}
void ClientModel::update()
{
int newNumConnections = getNumConnections();
int newNumBlocks = getNumBlocks();
if(cachedNumConnections != newNumConnections)
emit numConnectionsChanged(newNumConnections);
if(cachedNumBlocks != newNumBlocks)
emit numBlocksChanged(newNumBlocks);
cachedNumConnections = newNumConnections;
cachedNumBlocks = newNumBlocks;
}
bool ClientModel::isTestNet() const
{
return fTestNet;
}
bool ClientModel::inInitialBlockDownload() const
{
return IsInitialBlockDownload();
}
int ClientModel::getNumBlocksOfPeers() const
{
return GetNumBlocksOfPeers();
}
OptionsModel *ClientModel::getOptionsModel()
{
return optionsModel;
}
QString ClientModel::formatFullVersion() const
{
return QString::fromStdString(FormatFullVersion());
}

60
src/qt/clientmodel.h

@ -0,0 +1,60 @@
#ifndef CLIENTMODEL_H
#define CLIENTMODEL_H
#include <QObject>
class OptionsModel;
class AddressTableModel;
class TransactionTableModel;
class CWallet;
QT_BEGIN_NAMESPACE
class QDateTime;
QT_END_NAMESPACE
// Model for Bitcoin network client
class ClientModel : public QObject
{
Q_OBJECT
public:
explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0);
OptionsModel *getOptionsModel();
int getNumConnections() const;
int getNumBlocks() const;
int getNumBlocksAtStartup();
QDateTime getLastBlockDate() const;
// Return true if client connected to testnet
bool isTestNet() const;
// Return true if core is doing initial block download
bool inInitialBlockDownload() const;
// Return conservative estimate of total number of blocks, or 0 if unknown
int getNumBlocksOfPeers() const;
QString formatFullVersion() const;
private:
OptionsModel *optionsModel;
int cachedNumConnections;
int cachedNumBlocks;
int numBlocksAtStartup;
signals:
void numConnectionsChanged(int count);
void numBlocksChanged(int count);
// Asynchronous error notification
void error(const QString &title, const QString &message);
public slots:
private slots:
void update();
};
#endif // CLIENTMODEL_H

83
src/qt/csvmodelwriter.cpp

@ -0,0 +1,83 @@
#include "csvmodelwriter.h"
#include <QAbstractItemModel>
#include <QFile>
#include <QTextStream>
CSVModelWriter::CSVModelWriter(const QString &filename, QObject *parent) :
QObject(parent),
filename(filename)
{
}
void CSVModelWriter::setModel(const QAbstractItemModel *model)
{
this->model = model;
}
void CSVModelWriter::addColumn(const QString &title, int column, int role)
{
Column col;
col.title = title;
col.column = column;
col.role = role;
columns.append(col);
}
static void writeValue(QTextStream &f, const QString &value)
{
// TODO: quoting if " or \n in string
f << "\"" << value << "\"";
}
static void writeSep(QTextStream &f)
{
f << ",";
}
static void writeNewline(QTextStream &f)
{
f << "\n";
}
bool CSVModelWriter::write()
{
QFile file(filename);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QTextStream out(&file);
int numRows = model->rowCount();
// Header row
for(int i=0; i<columns.size(); ++i)
{
if(i!=0)
{
writeSep(out);
}
writeValue(out, columns[i].title);
}
writeNewline(out);
// Data rows
for(int j=0; j<numRows; ++j)
{
for(int i=0; i<columns.size(); ++i)
{
if(i!=0)
{
writeSep(out);
}
QVariant data = model->index(j, columns[i].column).data(columns[i].role);
writeValue(out, data.toString());
}
writeNewline(out);
}
file.close();
return file.error() == QFile::NoError;
}

43
src/qt/csvmodelwriter.h

@ -0,0 +1,43 @@
#ifndef CSVMODELWRITER_H
#define CSVMODELWRITER_H
#include <QObject>
#include <QList>
QT_BEGIN_NAMESPACE
class QAbstractItemModel;
QT_END_NAMESPACE
// Export TableModel to CSV file
class CSVModelWriter : public QObject
{
Q_OBJECT
public:
explicit CSVModelWriter(const QString &filename, QObject *parent = 0);
void setModel(const QAbstractItemModel *model);
void addColumn(const QString &title, int column, int role=Qt::EditRole);
// Perform write operation
// Returns true on success, false otherwise
bool write();
private:
QString filename;
const QAbstractItemModel *model;
struct Column
{
QString title;
int column;
int role;
};
QList<Column> columns;
signals:
public slots:
};
#endif // CSVMODELWRITER_H

115
src/qt/editaddressdialog.cpp

@ -0,0 +1,115 @@
#include "editaddressdialog.h"
#include "ui_editaddressdialog.h"
#include "addresstablemodel.h"
#include "guiutil.h"
#include <QDataWidgetMapper>
#include <QMessageBox>
EditAddressDialog::EditAddressDialog(Mode mode, QWidget *parent) :
QDialog(parent),
ui(new Ui::EditAddressDialog), mapper(0), mode(mode), model(0)
{
ui->setupUi(this);
GUIUtil::setupAddressWidget(ui->addressEdit, this);
switch(mode)
{
case NewReceivingAddress:
setWindowTitle(tr("New receiving address"));
ui->addressEdit->setEnabled(false);
break;
case NewSendingAddress:
setWindowTitle(tr("New sending address"));
break;
case EditReceivingAddress:
setWindowTitle(tr("Edit receiving address"));
ui->addressEdit->setDisabled(true);
break;
case EditSendingAddress:
setWindowTitle(tr("Edit sending address"));
break;
}
mapper = new QDataWidgetMapper(this);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
}
EditAddressDialog::~EditAddressDialog()
{
delete ui;
}
void EditAddressDialog::setModel(AddressTableModel *model)
{
this->model = model;
mapper->setModel(model);
mapper->addMapping(ui->labelEdit, AddressTableModel::Label);
mapper->addMapping(ui->addressEdit, AddressTableModel::Address);
}
void EditAddressDialog::loadRow(int row)
{
mapper->setCurrentIndex(row);
}
bool EditAddressDialog::saveCurrentRow()
{
switch(mode)
{
case NewReceivingAddress:
case NewSendingAddress:
address = model->addRow(
mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive,
ui->labelEdit->text(),
ui->addressEdit->text());
break;
case EditReceivingAddress:
case EditSendingAddress:
if(mapper->submit())
{
address = ui->addressEdit->text();
}
break;
}
return !address.isEmpty();
}
void EditAddressDialog::accept()
{
if(!saveCurrentRow())
{
switch(model->getEditStatus())
{
case AddressTableModel::DUPLICATE_ADDRESS:
QMessageBox::warning(this, windowTitle(),
tr("The entered address \"%1\" is already in the address book.").arg(ui->addressEdit->text()),
QMessageBox::Ok, QMessageBox::Ok);
break;
case AddressTableModel::INVALID_ADDRESS:
QMessageBox::warning(this, windowTitle(),
tr("The entered address \"%1\" is not a valid bitcoin address.").arg(ui->addressEdit->text()),
QMessageBox::Ok, QMessageBox::Ok);
return;
case AddressTableModel::WALLET_UNLOCK_FAILURE:
QMessageBox::critical(this, windowTitle(),
tr("Could not unlock wallet."),
QMessageBox::Ok, QMessageBox::Ok);
return;
case AddressTableModel::KEY_GENERATION_FAILURE:
QMessageBox::critical(this, windowTitle(),
tr("New key generation failed."),
QMessageBox::Ok, QMessageBox::Ok);
return;
}
return;
}
QDialog::accept();
}
QString EditAddressDialog::getAddress() const
{
return address;
}

47
src/qt/editaddressdialog.h

@ -0,0 +1,47 @@
#ifndef EDITADDRESSDIALOG_H
#define EDITADDRESSDIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QDataWidgetMapper;
QT_END_NAMESPACE
namespace Ui {
class EditAddressDialog;
}
class AddressTableModel;
class EditAddressDialog : public QDialog
{
Q_OBJECT
public:
enum Mode {
NewReceivingAddress,
NewSendingAddress,
EditReceivingAddress,
EditSendingAddress
};
explicit EditAddressDialog(Mode mode, QWidget *parent = 0);
~EditAddressDialog();
void setModel(AddressTableModel *model);
void loadRow(int row);
void accept();
QString getAddress() const;
private:
bool saveCurrentRow();
Ui::EditAddressDialog *ui;
QDataWidgetMapper *mapper;
Mode mode;
AddressTableModel *model;
QString address;
};
#endif // EDITADDRESSDIALOG_H

162
src/qt/forms/aboutdialog.ui

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>593</width>
<height>319</height>
</rect>
</property>
<property name="windowTitle">
<string>About Bitcoin</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../bitcoin.qrc">:/images/about</pixmap>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;b&gt;Bitcoin&lt;/b&gt; version</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="versionLabel">
<property name="text">
<string notr="true">0.3.666-beta</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Copyright © 2009-2011 Bitcoin Developers
This is experimental software.
Distributed under the MIT/X11 software license, see the accompanying file license.txt or http://www.opensource.org/licenses/mit-license.php.
This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/) and cryptographic software written by Eric Young (eay@cryptsoft.com) and UPnP software written by Thomas Bernard.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../bitcoin.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>360</x>
<y>308</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>428</x>
<y>308</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

130
src/qt/forms/addressbookpage.ui

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddressBookPage</class>
<widget class="QWidget" name="AddressBookPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>627</width>
<height>347</height>
</rect>
</property>
<property name="windowTitle">
<string>Address Book</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelExplanation">
<property name="text">
<string>These are your Bitcoin addresses for receiving payments. You may want to give a different one to each sender so you can keep track of who is paying you.</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableView">
<property name="toolTip">
<string>Double-click to edit address or label</string>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="newAddressButton">
<property name="toolTip">
<string>Create a new address</string>
</property>
<property name="text">
<string>&amp;New Address...</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/add</normaloff>:/icons/add</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyToClipboard">
<property name="toolTip">
<string>Copy the currently selected address to the system clipboard</string>
</property>
<property name="text">
<string>&amp;Copy to Clipboard</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/editcopy</normaloff>:/icons/editcopy</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="toolTip">
<string>Delete the currently selected address from the list. Only sending addresses can be deleted.</string>
</property>
<property name="text">
<string>&amp;Delete</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../bitcoin.qrc"/>
</resources>
<connections/>
</ui>

148
src/qt/forms/askpassphrasedialog.ui

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AskPassphraseDialog</class>
<widget class="QDialog" name="AskPassphraseDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>589</width>
<height>228</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>550</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="warningLabel">
<property name="text">
<string>TextLabel</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="passLabel1">
<property name="text">
<string>Enter passphrase</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="passEdit1">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="passLabel2">
<property name="text">
<string>New passphrase</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="passEdit2">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="passLabel3">
<property name="text">
<string>Repeat new passphrase</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="passEdit3">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AskPassphraseDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AskPassphraseDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

105
src/qt/forms/editaddressdialog.ui

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

161
src/qt/forms/overviewpage.ui

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OverviewPage</class>
<widget class="QWidget" name="OverviewPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>552</width>
<height>342</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>12</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Balance:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelBalance">
<property name="text">
<string>123.456 BTC</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Number of transactions:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="labelNumTransactions">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Unconfirmed:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="labelUnconfirmed">
<property name="text">
<string>0 BTC</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Wallet&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;b&gt;Recent transactions&lt;/b&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="listTransactions">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

122
src/qt/forms/sendcoinsdialog.ui

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SendCoinsDialog</class>
<widget class="QDialog" name="SendCoinsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>686</width>
<height>217</height>
</rect>
</property>
<property name="windowTitle">
<string>Send Coins</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>666</width>
<height>162</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="entries">
<property name="spacing">
<number>6</number>
</property>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QPushButton" name="addButton">
<property name="toolTip">
<string>Send to multiple recipients at once</string>
</property>
<property name="text">
<string>&amp;Add recipient...</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/add</normaloff>:/icons/add</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Confirm the send action</string>
</property>
<property name="text">
<string>&amp;Send</string>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/send</normaloff>:/icons/send</iconset>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../bitcoin.qrc"/>
</resources>
<connections/>
</ui>

178
src/qt/forms/sendcoinsentry.ui

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SendCoinsEntry</class>
<widget class="QFrame" name="SendCoinsEntry">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>729</width>
<height>136</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="spacing">
<number>12</number>
</property>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>A&amp;mount:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>payAmount</cstring>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Pay &amp;To:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>payTo</cstring>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="BitcoinAmountField" name="payAmount"/>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QValidatedLineEdit" name="addAsLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Enter a label for this address to add it to your address book</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>&amp;Label:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>addAsLabel</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QValidatedLineEdit" name="payTo">
<property name="toolTip">
<string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string>
</property>
<property name="maxLength">
<number>34</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addressBookButton">
<property name="toolTip">
<string>Choose adress from address book</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/address-book</normaloff>:/icons/address-book</iconset>
</property>
<property name="shortcut">
<string>Alt+A</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pasteButton">
<property name="toolTip">
<string>Paste address from clipboard</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/editpaste</normaloff>:/icons/editpaste</iconset>
</property>
<property name="shortcut">
<string>Alt+P</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="toolTip">
<string>Remove this recipient</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../bitcoin.qrc">
<normaloff>:/icons/remove</normaloff>:/icons/remove</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
<header>bitcoinamountfield.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qvalidatedlineedit.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../bitcoin.qrc"/>
</resources>
<connections/>
</ui>

71
src/qt/forms/transactiondescdialog.ui

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

23
src/qt/guiconstants.h

@ -0,0 +1,23 @@
#ifndef GUICONSTANTS_H
#define GUICONSTANTS_H
/* Milliseconds between model updates */
static const int MODEL_UPDATE_DELAY = 500;
/* Maximum passphrase length */
static const int MAX_PASSPHRASE_SIZE = 1024;
/* Size of icons in status bar */
static const int STATUSBAR_ICONSIZE = 16;
/* Invalid field background style */
#define STYLE_INVALID "background:#FF8080"
/* Transaction list -- unconfirmed transaction */
#define COLOR_UNCONFIRMED QColor(128, 128, 128)
/* Transaction list -- negative amount */
#define COLOR_NEGATIVE QColor(255, 0, 0)
/* Transaction list -- bare address (without label) */
#define COLOR_BAREADDRESS QColor(140, 140, 140)
#endif // GUICONSTANTS_H

74
src/qt/guiutil.cpp

@ -0,0 +1,74 @@
#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "headers.h"
#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
QString GUIUtil::dateTimeStr(qint64 nTime)
{
return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}
QString GUIUtil::dateTimeStr(const QDateTime &date)
{
return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}
QFont GUIUtil::bitcoinAddressFont()
{
QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter);
return font;
}
void GUIUtil::setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
widget->setValidator(new BitcoinAddressValidator(parent));
widget->setFont(bitcoinAddressFont());
}
void GUIUtil::setupAmountWidget(QLineEdit *widget, QWidget *parent)
{
QDoubleValidator *amountValidator = new QDoubleValidator(parent);
amountValidator->setDecimals(8);
amountValidator->setBottom(0.0);
widget->setValidator(amountValidator);
widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
}
bool GUIUtil::parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out)
{
if(url->scheme() != QString("bitcoin"))
return false;
SendCoinsRecipient rv;
rv.address = url->path();
rv.label = url->queryItemValue("label");
QString amount = url->queryItemValue("amount");
if(amount.isEmpty())
{
rv.amount = 0;
}
else // Amount is non-empty
{
if(!BitcoinUnits::parse(BitcoinUnits::BTC, amount, &rv.amount))
{
return false;
}
}
if(out)
{
*out = rv;
}
return true;
}

34
src/qt/guiutil.h

@ -0,0 +1,34 @@
#ifndef GUIUTIL_H
#define GUIUTIL_H
#include <QString>
QT_BEGIN_NAMESPACE
class QFont;
class QLineEdit;
class QWidget;
class QDateTime;
class QUrl;
QT_END_NAMESPACE
class SendCoinsRecipient;
class GUIUtil
{
public:
// Create human-readable string from date
static QString dateTimeStr(qint64 nTime);
static QString dateTimeStr(const QDateTime &datetime);
// Render bitcoin addresses in monospace font
static QFont bitcoinAddressFont();
// Set up widgets for address and amounts
static void setupAddressWidget(QLineEdit *widget, QWidget *parent);
static void setupAmountWidget(QLineEdit *widget, QWidget *parent);
// Parse "bitcoin:" URL into recipient object, return true on succesful parsing
// See Bitcoin URL definition discussion here: https://bitcointalk.org/index.php?topic=33490.0
static bool parseBitcoinURL(const QUrl *url, SendCoinsRecipient *out);
};
#endif // GUIUTIL_H

1155
src/qt/locale/bitcoin_de.ts

File diff suppressed because it is too large Load Diff

1441
src/qt/locale/bitcoin_nl.ts

File diff suppressed because it is too large Load Diff

2173
src/qt/locale/bitcoin_ru.ts

File diff suppressed because it is too large Load Diff

36
src/qt/monitoreddatamapper.cpp

@ -0,0 +1,36 @@
#include "monitoreddatamapper.h"
#include <QWidget>
#include <QMetaObject>
#include <QMetaProperty>
MonitoredDataMapper::MonitoredDataMapper(QObject *parent) :
QDataWidgetMapper(parent)
{
}
void MonitoredDataMapper::addMapping(QWidget *widget, int section)
{
QDataWidgetMapper::addMapping(widget, section);
addChangeMonitor(widget);
}
void MonitoredDataMapper::addMapping(QWidget *widget, int section, const QByteArray &propertyName)
{
QDataWidgetMapper::addMapping(widget, section, propertyName);
addChangeMonitor(widget);
}
void MonitoredDataMapper::addChangeMonitor(QWidget *widget)
{
// Watch user property of widget for changes, and connect
// the signal to our viewModified signal.
QMetaProperty prop = widget->metaObject()->userProperty();
int signal = prop.notifySignalIndex();
int method = this->metaObject()->indexOfMethod("viewModified()");
if(signal != -1 && method != -1)
{
QMetaObject::connect(widget, signal, this, method);
}
}

32
src/qt/monitoreddatamapper.h

@ -0,0 +1,32 @@
#ifndef MONITOREDDATAMAPPER_H
#define MONITOREDDATAMAPPER_H
#include <QDataWidgetMapper>
QT_BEGIN_NAMESPACE
class QWidget;
QT_END_NAMESPACE
/* Data <-> Widget mapper that watches for changes,
to be able to notify when 'dirty' (for example, to
enable a commit/apply button).
*/
class MonitoredDataMapper : public QDataWidgetMapper
{
Q_OBJECT
public:
explicit MonitoredDataMapper(QObject *parent=0);
void addMapping(QWidget *widget, int section);
void addMapping(QWidget *widget, int section, const QByteArray &propertyName);
private:
void addChangeMonitor(QWidget *widget);
signals:
void viewModified();
};
#endif // MONITOREDDATAMAPPER_H

224
src/qt/notificator.cpp

@ -0,0 +1,224 @@
#include "notificator.h"
#include <QMetaType>
#include <QVariant>
#include <QIcon>
#include <QApplication>
#include <QStyle>
#include <QByteArray>
#include <QSystemTrayIcon>
#include <QMessageBox>
#ifdef QT_DBUS
#include <QtDBus/QtDBus>
#include <stdint.h>
#endif
// https://wiki.ubuntu.com/NotificationDevelopmentGuidelines recommends at least 128
const int FREEDESKTOP_NOTIFICATION_ICON_SIZE = 128;
Notificator::Notificator(const QString &programName, QSystemTrayIcon *trayicon, QWidget *parent):
QObject(parent),
parent(parent),
programName(programName),
mode(None),
trayIcon(trayicon)
#ifdef QT_DBUS
,interface(0)
#endif
{
if(trayicon && trayicon->supportsMessages())
{
mode = QSystemTray;
}
#ifdef QT_DBUS
interface = new QDBusInterface("org.freedesktop.Notifications",
"/org/freedesktop/Notifications", "org.freedesktop.Notifications");
if(interface->isValid())
{
mode = Freedesktop;
}
#endif
}
Notificator::~Notificator()
{
#ifdef QT_DBUS
delete interface;
#endif
}
#ifdef QT_DBUS
// Loosely based on http://www.qtcentre.org/archive/index.php/t-25879.html
class FreedesktopImage
{
public:
FreedesktopImage() {}
FreedesktopImage(const QImage &img);
static int metaType();
// Image to variant that can be marshaled over DBus
static QVariant toVariant(const QImage &img);
private:
int width, height, stride;
bool hasAlpha;
int channels;
int bitsPerSample;
QByteArray image;
friend QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i);
friend const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i);
};
Q_DECLARE_METATYPE(FreedesktopImage);
// Image configuration settings
const int CHANNELS = 4;
const int BYTES_PER_PIXEL = 4;
const int BITS_PER_SAMPLE = 8;
FreedesktopImage::FreedesktopImage(const QImage &img):
width(img.width()),
height(img.height()),
stride(img.width() * BYTES_PER_PIXEL),
hasAlpha(true),
channels(CHANNELS),
bitsPerSample(BITS_PER_SAMPLE)
{
// Convert 00xAARRGGBB to RGBA bytewise (endian-independent) format
QImage tmp = img.convertToFormat(QImage::Format_ARGB32);
const uint32_t *data = reinterpret_cast<const uint32_t*>(tmp.constBits());
unsigned int num_pixels = width * height;
image.resize(num_pixels * BYTES_PER_PIXEL);
for(unsigned int ptr = 0; ptr < num_pixels; ++ptr)
{
image[ptr*BYTES_PER_PIXEL+0] = data[ptr] >> 16; // R
image[ptr*BYTES_PER_PIXEL+1] = data[ptr] >> 8; // G
image[ptr*BYTES_PER_PIXEL+2] = data[ptr]; // B
image[ptr*BYTES_PER_PIXEL+3] = data[ptr] >> 24; // A
}
}
QDBusArgument &operator<<(QDBusArgument &a, const FreedesktopImage &i)
{
a.beginStructure();
a << i.width << i.height << i.stride << i.hasAlpha << i.bitsPerSample << i.channels << i.image;
a.endStructure();
return a;
}
const QDBusArgument &operator>>(const QDBusArgument &a, FreedesktopImage &i)
{
a.beginStructure();
a >> i.width >> i.height >> i.stride >> i.hasAlpha >> i.bitsPerSample >> i.channels >> i.image;
a.endStructure();
return a;
}
int FreedesktopImage::metaType()
{
return qDBusRegisterMetaType<FreedesktopImage>();
}
QVariant FreedesktopImage::toVariant(const QImage &img)
{
FreedesktopImage fimg(img);
return QVariant(FreedesktopImage::metaType(), &fimg);
}
void Notificator::notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
Q_UNUSED(cls);
// Arguments for DBus call:
QList<QVariant> args;
// Program Name:
args.append(programName);
// Unique ID of this notification type:
args.append(0U);
// Application Icon, empty string
args.append(QString());
// Summary
args.append(title);
// Body
args.append(text);
// Actions (none, actions are deprecated)
QStringList actions;
args.append(actions);
// Hints
QVariantMap hints;
// If no icon specified, set icon based on class
QIcon tmpicon;
if(icon.isNull())
{
QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion;
switch(cls)
{
case Information: sicon = QStyle::SP_MessageBoxInformation; break;
case Warning: sicon = QStyle::SP_MessageBoxWarning; break;
case Critical: sicon = QStyle::SP_MessageBoxCritical; break;
default: break;
}
tmpicon = QApplication::style()->standardIcon(sicon);
}
else
{
tmpicon = icon;
}
hints["icon_data"] = FreedesktopImage::toVariant(tmpicon.pixmap(FREEDESKTOP_NOTIFICATION_ICON_SIZE).toImage());
args.append(hints);
// Timeout (in msec)
args.append(millisTimeout);
// "Fire and forget"
interface->callWithArgumentList(QDBus::NoBlock, "Notify", args);
}
#endif
void Notificator::notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
Q_UNUSED(icon);
QSystemTrayIcon::MessageIcon sicon = QSystemTrayIcon::NoIcon;
switch(cls) // Set icon based on class
{
case Information: sicon = QSystemTrayIcon::Information; break;
case Warning: sicon = QSystemTrayIcon::Warning; break;
case Critical: sicon = QSystemTrayIcon::Critical; break;
}
trayIcon->showMessage(title, text, sicon, millisTimeout);
}
void Notificator::notify(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout)
{
switch(mode)
{
#ifdef QT_DBUS
case Freedesktop:
notifyDBus(cls, title, text, icon, millisTimeout);
break;
#endif
case QSystemTray:
notifySystray(cls, title, text, icon, millisTimeout);
break;
default:
if(cls == Critical)
{
// Fall back to old fashioned popup dialog if critical and no other notification available
QMessageBox::critical(parent, title, text, QMessageBox::Ok, QMessageBox::Ok);
}
break;
}
}

63
src/qt/notificator.h

@ -0,0 +1,63 @@
#ifndef NOTIFICATOR_H
#define NOTIFICATOR_H
#include <QObject>
#include <QIcon>
QT_BEGIN_NAMESPACE
class QSystemTrayIcon;
#ifdef QT_DBUS
class QDBusInterface;
#endif
QT_END_NAMESPACE
// Cross-platform desktop notification client
class Notificator: public QObject
{
Q_OBJECT
public:
// Create a new notificator
// Ownership of trayIcon is not transferred to this object
Notificator(const QString &programName=QString(), QSystemTrayIcon *trayIcon=0, QWidget *parent=0);
~Notificator();
// Message class
enum Class
{
Information,
Warning,
Critical,
};
public slots:
/* Show notification message.
*
* cls: general message class
* title: title shown with message
* text: message content
* icon: optional icon to show with message
* millisTimeout: notification timeout in milliseconds (default 10 seconds)
*/
void notify(Class cls, const QString &title, const QString &text,
const QIcon &icon = QIcon(), int millisTimeout = 10000);
private:
QWidget *parent;
enum Mode {
None,
Freedesktop, // Use DBus org.freedesktop.Notifications
QSystemTray, // Use QSystemTray::showMessage
};
QString programName;
Mode mode;
QSystemTrayIcon *trayIcon;
#ifdef QT_DBUS
QDBusInterface *interface;
void notifyDBus(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
#endif
void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout);
};
#endif // NOTIFICATOR_H

277
src/qt/optionsdialog.cpp

@ -0,0 +1,277 @@
#include "optionsdialog.h"
#include "optionsmodel.h"
#include "bitcoinamountfield.h"
#include "monitoreddatamapper.h"
#include "guiutil.h"
#include "bitcoinunits.h"
#include "qvaluecombobox.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QStackedWidget>
#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QIntValidator>
#include <QDoubleValidator>
#include <QRegExpValidator>
#include <QDialogButtonBox>
/* First page of options */
class MainOptionsPage : public QWidget
{
public:
explicit MainOptionsPage(QWidget *parent=0);
void setMapper(MonitoredDataMapper *mapper);
private:
QCheckBox *bitcoin_at_startup;
QCheckBox *minimize_to_tray;
QCheckBox *map_port_upnp;
QCheckBox *minimize_on_close;
QCheckBox *connect_socks4;
QLineEdit *proxy_ip;
QLineEdit *proxy_port;
BitcoinAmountField *fee_edit;
signals:
public slots:
};
class DisplayOptionsPage : public QWidget
{
public:
explicit DisplayOptionsPage(QWidget *parent=0);
void setMapper(MonitoredDataMapper *mapper);
private:
QValueComboBox *unit;
QCheckBox *display_addresses;
signals:
public slots:
};
OptionsDialog::OptionsDialog(QWidget *parent):
QDialog(parent), contents_widget(0), pages_widget(0),
model(0), main_page(0), display_page(0)
{
contents_widget = new QListWidget();
contents_widget->setMaximumWidth(128);
pages_widget = new QStackedWidget();
pages_widget->setMinimumWidth(300);
QListWidgetItem *item_main = new QListWidgetItem(tr("Main"));
contents_widget->addItem(item_main);
main_page = new MainOptionsPage(this);
pages_widget->addWidget(main_page);
QListWidgetItem *item_display = new QListWidgetItem(tr("Display"));
contents_widget->addItem(item_display);
display_page = new DisplayOptionsPage(this);
pages_widget->addWidget(display_page);
contents_widget->setCurrentRow(0);
QHBoxLayout *main_layout = new QHBoxLayout();
main_layout->addWidget(contents_widget);
main_layout->addWidget(pages_widget, 1);
QVBoxLayout *layout = new QVBoxLayout();
layout->addLayout(main_layout);
QDialogButtonBox *buttonbox = new QDialogButtonBox();
buttonbox->setStandardButtons(QDialogButtonBox::Apply|QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
apply_button = buttonbox->button(QDialogButtonBox::Apply);
layout->addWidget(buttonbox);
setLayout(layout);
setWindowTitle(tr("Options"));
/* Widget-to-option mapper */
mapper = new MonitoredDataMapper(this);
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
mapper->setOrientation(Qt::Vertical);
/* enable apply button when data modified */
connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply()));
/* disable apply button when new data loaded */
connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply()));
/* Event bindings */
connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int)));
connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked()));
connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked()));
connect(buttonbox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked()));
}
void OptionsDialog::setModel(OptionsModel *model)
{
this->model = model;
mapper->setModel(model);
main_page->setMapper(mapper);
display_page->setMapper(mapper);
mapper->toFirst();
}
void OptionsDialog::changePage(int index)
{
pages_widget->setCurrentIndex(index);
}
void OptionsDialog::okClicked()
{
mapper->submit();
accept();
}
void OptionsDialog::cancelClicked()
{
reject();
}
void OptionsDialog::applyClicked()
{
mapper->submit();
apply_button->setEnabled(false);
}
void OptionsDialog::enableApply()
{
apply_button->setEnabled(true);
}
void OptionsDialog::disableApply()
{
apply_button->setEnabled(false);
}
MainOptionsPage::MainOptionsPage(QWidget *parent):
QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout();
bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup"));
bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on"));
layout->addWidget(bitcoin_at_startup);
minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar"));
minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window"));
layout->addWidget(minimize_to_tray);
map_port_upnp = new QCheckBox(tr("Map port using &UPnP"));
map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled."));
layout->addWidget(map_port_upnp);
minimize_on_close = new QCheckBox(tr("M&inimize on close"));
minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu."));
layout->addWidget(minimize_on_close);
connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:"));
connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)"));
layout->addWidget(connect_socks4);
QHBoxLayout *proxy_hbox = new QHBoxLayout();
proxy_hbox->addSpacing(18);
QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: "));
proxy_hbox->addWidget(proxy_ip_label);
proxy_ip = new QLineEdit();
proxy_ip->setMaximumWidth(140);
proxy_ip->setEnabled(false);
proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this));
proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)"));
proxy_ip_label->setBuddy(proxy_ip);
proxy_hbox->addWidget(proxy_ip);
QLabel *proxy_port_label = new QLabel(tr("&Port: "));
proxy_hbox->addWidget(proxy_port_label);
proxy_port = new QLineEdit();
proxy_port->setMaximumWidth(55);
proxy_port->setValidator(new QIntValidator(0, 65535, this));
proxy_port->setEnabled(false);
proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)"));
proxy_port_label->setBuddy(proxy_port);
proxy_hbox->addWidget(proxy_port);
proxy_hbox->addStretch(1);
layout->addLayout(proxy_hbox);
QLabel *fee_help = new QLabel(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended."));
fee_help->setWordWrap(true);
layout->addWidget(fee_help);
QHBoxLayout *fee_hbox = new QHBoxLayout();
fee_hbox->addSpacing(18);
QLabel *fee_label = new QLabel(tr("Pay transaction &fee"));
fee_hbox->addWidget(fee_label);
fee_edit = new BitcoinAmountField();
fee_edit->setToolTip(tr("Optional transaction fee per KB that helps make sure your transactions are processed quickly. Most transactions are 1KB. Fee 0.01 recommended."));
fee_label->setBuddy(fee_edit);
fee_hbox->addWidget(fee_edit);
fee_hbox->addStretch(1);
layout->addLayout(fee_hbox);
layout->addStretch(1); // Extra space at bottom
setLayout(layout);
connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool)));
connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool)));
#ifndef USE_UPNP
map_port_upnp->setDisabled(true);
#endif
}
void MainOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
// Map model to widgets
mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
mapper->addMapping(fee_edit, OptionsModel::Fee);
}
DisplayOptionsPage::DisplayOptionsPage(QWidget *parent):
QWidget(parent)
{
QVBoxLayout *layout = new QVBoxLayout();
QHBoxLayout *unit_hbox = new QHBoxLayout();
unit_hbox->addSpacing(18);
QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: "));
unit_hbox->addWidget(unit_label);
unit = new QValueComboBox(this);
unit->setModel(new BitcoinUnits(this));
unit->setToolTip(tr("Choose the default subdivision unit to show in the interface, and when sending coins"));
unit_label->setBuddy(unit);
unit_hbox->addWidget(unit);
layout->addLayout(unit_hbox);
display_addresses = new QCheckBox(tr("Display addresses in transaction list"), this);
layout->addWidget(display_addresses);
layout->addStretch();
setLayout(layout);
}
void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
mapper->addMapping(unit, OptionsModel::DisplayUnit);
mapper->addMapping(display_addresses, OptionsModel::DisplayAddresses);
}

50
src/qt/optionsdialog.h

@ -0,0 +1,50 @@
#ifndef OPTIONSDIALOG_H
#define OPTIONSDIALOG_H
#include <QDialog>
QT_BEGIN_NAMESPACE
class QStackedWidget;
class QListWidget;
class QListWidgetItem;
class QPushButton;
QT_END_NAMESPACE
class OptionsModel;
class MainOptionsPage;
class DisplayOptionsPage;
class MonitoredDataMapper;
class OptionsDialog : public QDialog
{
Q_OBJECT
public:
explicit OptionsDialog(QWidget *parent=0);
void setModel(OptionsModel *model);
signals:
public slots:
void changePage(int index);
private slots:
void okClicked();
void cancelClicked();
void applyClicked();
void enableApply();
void disableApply();
private:
QListWidget *contents_widget;
QStackedWidget *pages_widget;
OptionsModel *model;
MonitoredDataMapper *mapper;
QPushButton *apply_button;
// Pages
MainOptionsPage *main_page;
DisplayOptionsPage *display_page;
void setupMainPage();
};
#endif // OPTIONSDIALOG_H

162
src/qt/optionsmodel.cpp

@ -0,0 +1,162 @@
#include "optionsmodel.h"
#include "bitcoinunits.h"
#include "headers.h"
OptionsModel::OptionsModel(CWallet *wallet, QObject *parent) :
QAbstractListModel(parent),
wallet(wallet),
nDisplayUnit(BitcoinUnits::BTC),
bDisplayAddresses(false)
{
// Read our specific settings from the wallet db
CWalletDB walletdb(wallet->strWalletFile);
walletdb.ReadSetting("nDisplayUnit", nDisplayUnit);
walletdb.ReadSetting("bDisplayAddresses", bDisplayAddresses);
}
int OptionsModel::rowCount(const QModelIndex & parent) const
{
return OptionIDRowCount;
}
QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
if(role == Qt::EditRole)
{
switch(index.row())
{
case StartAtStartup:
return QVariant();
case MinimizeToTray:
return QVariant(fMinimizeToTray);
case MapPortUPnP:
return QVariant(fUseUPnP);
case MinimizeOnClose:
return QVariant(fMinimizeOnClose);
case ConnectSOCKS4:
return QVariant(fUseProxy);
case ProxyIP:
return QVariant(QString::fromStdString(addrProxy.ToStringIP()));
case ProxyPort:
return QVariant(QString::fromStdString(addrProxy.ToStringPort()));
case Fee:
return QVariant(nTransactionFee);
case DisplayUnit:
return QVariant(nDisplayUnit);
case DisplayAddresses:
return QVariant(bDisplayAddresses);
default:
return QVariant();
}
}
return QVariant();
}
bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
bool successful = true; /* set to false on parse error */
if(role == Qt::EditRole)
{
CWalletDB walletdb(wallet->strWalletFile);
switch(index.row())
{
case StartAtStartup:
successful = false; /*TODO*/
break;
case MinimizeToTray:
fMinimizeToTray = value.toBool();
walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
break;
case MapPortUPnP:
fUseUPnP = value.toBool();
walletdb.WriteSetting("fUseUPnP", fUseUPnP);
#ifdef USE_UPNP
MapPort(fUseUPnP);
#endif
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
break;
case ConnectSOCKS4:
fUseProxy = value.toBool();
walletdb.WriteSetting("fUseProxy", fUseProxy);
break;
case ProxyIP:
{
// Use CAddress to parse and check IP
CAddress addr(value.toString().toStdString() + ":1");
if (addr.ip != INADDR_NONE)
{
addrProxy.ip = addr.ip;
walletdb.WriteSetting("addrProxy", addrProxy);
}
else
{
successful = false;
}
}
break;
case ProxyPort:
{
int nPort = atoi(value.toString().toAscii().data());
if (nPort > 0 && nPort < USHRT_MAX)
{
addrProxy.port = htons(nPort);
walletdb.WriteSetting("addrProxy", addrProxy);
}
else
{
successful = false;
}
}
break;
case Fee: {
nTransactionFee = value.toLongLong();
walletdb.WriteSetting("nTransactionFee", nTransactionFee);
}
break;
case DisplayUnit: {
int unit = value.toInt();
nDisplayUnit = unit;
walletdb.WriteSetting("nDisplayUnit", nDisplayUnit);
emit displayUnitChanged(unit);
}
case DisplayAddresses: {
bDisplayAddresses = value.toBool();
walletdb.WriteSetting("bDisplayAddresses", bDisplayAddresses);
}
default:
break;
}
}
emit dataChanged(index, index);
return successful;
}
qint64 OptionsModel::getTransactionFee()
{
return nTransactionFee;
}
bool OptionsModel::getMinimizeToTray()
{
return fMinimizeToTray;
}
bool OptionsModel::getMinimizeOnClose()
{
return fMinimizeOnClose;
}
int OptionsModel::getDisplayUnit()
{
return nDisplayUnit;
}
bool OptionsModel::getDisplayAddresses()
{
return bDisplayAddresses;
}

56
src/qt/optionsmodel.h

@ -0,0 +1,56 @@
#ifndef OPTIONSMODEL_H
#define OPTIONSMODEL_H
#include <QAbstractListModel>
class CWallet;
/* Interface from QT to configuration data structure for bitcoin client.
To QT, the options are presented as a list with the different options
laid out vertically.
This can be changed to a tree once the settings become sufficiently
complex.
*/
class OptionsModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit OptionsModel(CWallet *wallet, QObject *parent = 0);
enum OptionID {
StartAtStartup, // bool
MinimizeToTray, // bool
MapPortUPnP, // bool
MinimizeOnClose, // bool
ConnectSOCKS4, // bool
ProxyIP, // QString
ProxyPort, // QString
Fee, // qint64
DisplayUnit, // BitcoinUnits::Unit
DisplayAddresses, // bool
OptionIDRowCount
};
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
/* Explicit getters */
qint64 getTransactionFee();
bool getMinimizeToTray();
bool getMinimizeOnClose();
int getDisplayUnit();
bool getDisplayAddresses();
private:
// Wallet stores persistent options
CWallet *wallet;
int nDisplayUnit;
bool bDisplayAddresses;
signals:
void displayUnitChanged(int unit);
public slots:
};
#endif // OPTIONSMODEL_H

173
src/qt/overviewpage.cpp

@ -0,0 +1,173 @@
#include "overviewpage.h"
#include "ui_overviewpage.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "optionsmodel.h"
#include "transactiontablemodel.h"
#include "transactionfilterproxy.h"
#include "guiutil.h"
#include "guiconstants.h"
#include <QAbstractItemDelegate>
#include <QPainter>
#define DECORATION_SIZE 64
#define NUM_ITEMS 3
class TxViewDelegate : public QAbstractItemDelegate
{
//Q_OBJECT
public:
TxViewDelegate(): QAbstractItemDelegate(), unit(BitcoinUnits::BTC)
{
}
inline void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
painter->save();
QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
QRect mainRect = option.rect;
QRect decorationRect(mainRect.topLeft(), QSize(DECORATION_SIZE, DECORATION_SIZE));
int xspace = DECORATION_SIZE + 8;
int ypad = 6;
int halfheight = (mainRect.height() - 2*ypad)/2;
QRect amountRect(mainRect.left() + xspace, mainRect.top()+ypad, mainRect.width() - xspace, halfheight);
QRect addressRect(mainRect.left() + xspace, mainRect.top()+ypad+halfheight, mainRect.width() - xspace, halfheight);
icon.paint(painter, decorationRect);
QDateTime date = index.data(TransactionTableModel::DateRole).toDateTime();
QString address = index.data(Qt::DisplayRole).toString();
qint64 amount = index.data(TransactionTableModel::AmountRole).toLongLong();
bool confirmed = index.data(TransactionTableModel::ConfirmedRole).toBool();
QVariant value = index.data(Qt::ForegroundRole);
QColor foreground = option.palette.color(QPalette::Text);
if(qVariantCanConvert<QColor>(value))
{
foreground = qvariant_cast<QColor>(value);
}
painter->setPen(foreground);
painter->drawText(addressRect, Qt::AlignLeft|Qt::AlignVCenter, address);
if(amount < 0)
{
foreground = COLOR_NEGATIVE;
}
else if(!confirmed)
{
foreground = COLOR_UNCONFIRMED;
}
else
{
foreground = option.palette.color(QPalette::Text);
}
painter->setPen(foreground);
QString amountText = BitcoinUnits::formatWithUnit(unit, amount, true);
if(!confirmed)
{
amountText = QString("[") + amountText + QString("]");
}
painter->drawText(amountRect, Qt::AlignRight|Qt::AlignVCenter, amountText);
painter->setPen(option.palette.color(QPalette::Text));
painter->drawText(amountRect, Qt::AlignLeft|Qt::AlignVCenter, GUIUtil::dateTimeStr(date));
painter->restore();
}
inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QSize(DECORATION_SIZE, DECORATION_SIZE);
}
int unit;
};
OverviewPage::OverviewPage(QWidget *parent) :
QWidget(parent),
ui(new Ui::OverviewPage),
currentBalance(-1),
currentUnconfirmedBalance(-1),
txdelegate(new TxViewDelegate())
{
ui->setupUi(this);
// Balance: <balance>
ui->labelBalance->setFont(QFont("Monospace", -1, QFont::Bold));
ui->labelBalance->setToolTip(tr("Your current balance"));
ui->labelBalance->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
// Unconfirmed balance: <balance>
ui->labelUnconfirmed->setFont(QFont("Monospace", -1, QFont::Bold));
ui->labelUnconfirmed->setToolTip(tr("Total of transactions that have yet to be confirmed, and do not yet count toward the current balance"));
ui->labelUnconfirmed->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
ui->labelNumTransactions->setToolTip(tr("Total number of transactions in wallet"));
// Recent transactions
ui->listTransactions->setStyleSheet("background:transparent");
ui->listTransactions->setItemDelegate(txdelegate);
ui->listTransactions->setIconSize(QSize(DECORATION_SIZE, DECORATION_SIZE));
ui->listTransactions->setSelectionMode(QAbstractItemView::NoSelection);
ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2));
connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SIGNAL(transactionClicked(QModelIndex)));
}
OverviewPage::~OverviewPage()
{
delete ui;
}
void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance)
{
int unit = model->getOptionsModel()->getDisplayUnit();
currentBalance = balance;
currentUnconfirmedBalance = unconfirmedBalance;
ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance));
}
void OverviewPage::setNumTransactions(int count)
{
ui->labelNumTransactions->setText(QLocale::system().toString(count));
}
void OverviewPage::setModel(WalletModel *model)
{
this->model = model;
// Set up transaction list
TransactionFilterProxy *filter = new TransactionFilterProxy();
filter->setSourceModel(model->getTransactionTableModel());
filter->setLimit(NUM_ITEMS);
filter->setDynamicSortFilter(true);
filter->setSortRole(Qt::EditRole);
filter->sort(TransactionTableModel::Status, Qt::DescendingOrder);
ui->listTransactions->setModel(filter);
ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress);
// Keep up to date with wallet
setBalance(model->getBalance(), model->getUnconfirmedBalance());
connect(model, SIGNAL(balanceChanged(qint64, qint64)), this, SLOT(setBalance(qint64, qint64)));
setNumTransactions(model->getNumTransactions());
connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(displayUnitChanged()));
}
void OverviewPage::displayUnitChanged()
{
if(currentBalance != -1)
setBalance(currentBalance, currentUnconfirmedBalance);
txdelegate->unit = model->getOptionsModel()->getDisplayUnit();
ui->listTransactions->update();
}

45
src/qt/overviewpage.h

@ -0,0 +1,45 @@
#ifndef OVERVIEWPAGE_H
#define OVERVIEWPAGE_H
#include <QWidget>
QT_BEGIN_NAMESPACE
class QModelIndex;
QT_END_NAMESPACE
namespace Ui {
class OverviewPage;
}
class WalletModel;
class TxViewDelegate;
class OverviewPage : public QWidget
{
Q_OBJECT
public:
explicit OverviewPage(QWidget *parent = 0);
~OverviewPage();
void setModel(WalletModel *model);
public slots:
void setBalance(qint64 balance, qint64 unconfirmedBalance);
void setNumTransactions(int count);
signals:
void transactionClicked(const QModelIndex &index);
private:
Ui::OverviewPage *ui;
WalletModel *model;
qint64 currentBalance;
qint64 currentUnconfirmedBalance;
TxViewDelegate *txdelegate;
private slots:
void displayUnitChanged();
};
#endif // OVERVIEWPAGE_H

45
src/qt/qvalidatedlineedit.cpp

@ -0,0 +1,45 @@
#include "qvalidatedlineedit.h"
#include "guiconstants.h"
QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) :
QLineEdit(parent), valid(true)
{
connect(this, SIGNAL(textChanged(QString)), this, SLOT(markValid()));
}
void QValidatedLineEdit::setValid(bool valid)
{
if(valid == this->valid)
{
return;
}
if(valid)
{
setStyleSheet("");
}
else
{
setStyleSheet(STYLE_INVALID);
}
this->valid = valid;
}
void QValidatedLineEdit::focusInEvent(QFocusEvent *evt)
{
// Clear invalid flag on focus
setValid(true);
QLineEdit::focusInEvent(evt);
}
void QValidatedLineEdit::markValid()
{
setValid(true);
}
void QValidatedLineEdit::clear()
{
setValid(true);
QLineEdit::clear();
}

28
src/qt/qvalidatedlineedit.h

@ -0,0 +1,28 @@
#ifndef QVALIDATEDLINEEDIT_H
#define QVALIDATEDLINEEDIT_H
#include <QLineEdit>
// Line edit that can be marked as "invalid". When marked as invalid,
// it will get a red background until it is focused.
class QValidatedLineEdit : public QLineEdit
{
Q_OBJECT
public:
explicit QValidatedLineEdit(QWidget *parent = 0);
void clear();
protected:
void focusInEvent(QFocusEvent *evt);
private:
bool valid;
public slots:
void setValid(bool valid);
private slots:
void markValid();
};
#endif // QVALIDATEDLINEEDIT_H

27
src/qt/qvaluecombobox.cpp

@ -0,0 +1,27 @@
#include "qvaluecombobox.h"
QValueComboBox::QValueComboBox(QWidget *parent) :
QComboBox(parent), role(Qt::UserRole)
{
connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(handleSelectionChanged(int)));
}
int QValueComboBox::value() const
{
return itemData(currentIndex(), role).toInt();
}
void QValueComboBox::setValue(int value)
{
setCurrentIndex(findData(value, role));
}
void QValueComboBox::setRole(int role)
{
this->role = role;
}
void QValueComboBox::handleSelectionChanged(int idx)
{
emit valueChanged();
}

33
src/qt/qvaluecombobox.h

@ -0,0 +1,33 @@
#ifndef QVALUECOMBOBOX_H
#define QVALUECOMBOBOX_H
#include <QComboBox>
// QComboBox that can be used with QDataWidgetMapper to select
// ordinal values from a model.
class QValueComboBox : public QComboBox
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged USER true);
public:
explicit QValueComboBox(QWidget *parent = 0);
int value() const;
void setValue(int value);
// Model role to use as value
void setRole(int role);
signals:
void valueChanged();
public slots:
private:
int role;
private slots:
void handleSelectionChanged(int idx);
};
#endif // QVALUECOMBOBOX_H

1
src/qt/res/bitcoin-qt.rc

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico"

BIN
src/qt/res/icons/add.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/qt/res/icons/address-book.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/qt/res/icons/bitcoin.icns

Binary file not shown.

BIN
src/qt/res/icons/bitcoin.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/qt/res/icons/bitcoin.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
src/qt/res/icons/bitcoin_testnet.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
src/qt/res/icons/clock1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

BIN
src/qt/res/icons/clock2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 B

BIN
src/qt/res/icons/clock3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

BIN
src/qt/res/icons/clock4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 B

BIN
src/qt/res/icons/clock5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

BIN
src/qt/res/icons/configure.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/qt/res/icons/connect0_16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

BIN
src/qt/res/icons/connect1_16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

BIN
src/qt/res/icons/connect2_16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

BIN
src/qt/res/icons/connect3_16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

BIN
src/qt/res/icons/connect4_16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

BIN
src/qt/res/icons/edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/qt/res/icons/editcopy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

BIN
src/qt/res/icons/editpaste.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/qt/res/icons/export.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/qt/res/icons/history.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

BIN
src/qt/res/icons/key.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/qt/res/icons/lock_closed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Loading…
Cancel
Save