@ -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" |
@ -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} |
@ -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) |
|||
|
|||
|
@ -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 |
@ -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() |
@ -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() |
|||
|
|||
|
|||
|
@ -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} |
|||
|
@ -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(); |
|||
} |
@ -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
|
@ -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); |
|||
} |
|||
} |
@ -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
|
@ -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(); |
|||
} |
|||
} |
|||
|
@ -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
|
@ -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); |
|||
} |
@ -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
|
@ -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; |
|||
} |
@ -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> |
@ -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; |
|||
} |
@ -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
|
@ -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); |
|||
} |
@ -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
|
@ -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(); |
|||
} |
|||
} |
@ -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 |
@ -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"), |
|||
}; |
@ -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(); |
|||
} |
@ -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
|
@ -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()); |
|||
} |
@ -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
|
@ -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; |
|||
} |
|||
|
@ -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
|
@ -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; |
|||
} |
@ -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
|
@ -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><b>Bitcoin</b> 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> |
@ -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>&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>&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>&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> |
@ -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> |
@ -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>&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>&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> |
@ -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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> |
|||
<html><head><meta name="qrichtext" content="1" /><style type="text/css"> |
|||
p, li { white-space: pre-wrap; } |
|||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> |
|||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Wallet</span></p></body></html></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><b>Recent transactions</b></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> |
@ -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>&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>&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> |
@ -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&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 &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>&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> |
@ -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> |
@ -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
|
@ -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; |
|||
} |
@ -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
|
@ -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); |
|||
} |
|||
} |
@ -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
|
@ -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; |
|||
} |
|||
} |
@ -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
|
@ -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); |
|||
} |
@ -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
|
@ -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; |
|||
} |
@ -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
|
@ -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(); |
|||
} |
@ -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
|
@ -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(); |
|||
} |
@ -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
|
@ -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(); |
|||
} |
@ -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
|
@ -0,0 +1 @@ |
|||
IDI_ICON1 ICON DISCARDABLE "icons/bitcoin.ico" |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 946 B |
After Width: | Height: | Size: 944 B |
After Width: | Height: | Size: 946 B |
After Width: | Height: | Size: 962 B |
After Width: | Height: | Size: 956 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 702 B |
After Width: | Height: | Size: 612 B |
After Width: | Height: | Size: 623 B |
After Width: | Height: | Size: 625 B |
After Width: | Height: | Size: 611 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 879 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 746 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |