# HG changeset patch # User Wladimir J. van der Laan # Date 1336939460 25200 # Node ID b0131c75604aca0d5274198f4a364d240a258d72 # Parent c7049fd91250c407e32499fc7355a04de7bfa555# Parent dfb7a0208d3afa52e727fc3f15c52c7799d48e9a Merge pull request #1283 from laanwj/2012_05_prevent_tooltip_infiniteloop Prevent tooltip filter from ever causing infinite loops diff --git a/doc/release-process.txt b/doc/release-process.txt --- a/doc/release-process.txt +++ b/doc/release-process.txt @@ -93,6 +93,7 @@ * create SHA256SUMS for builds, and PGP-sign it * update bitcoin.org version + make sure all OS download links go to the right versions * update forum version diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -100,14 +100,52 @@ return 1; } -#endif bool AppInit(int argc, char* argv[]) { bool fRet = false; try { - fRet = AppInit2(argc, argv); + // + // Parameters + // + // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() + ParseParameters(argc, argv); + if (!boost::filesystem::is_directory(GetDataDir(false))) + { + fprintf(stderr, "Error: Specified directory does not exist\n"); + Shutdown(NULL); + } + ReadConfigFile(mapArgs, mapMultiArgs); + + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + // First part of help message is specific to bitcoind / RPC client + std::string strUsage = _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + + _("Usage:") + "\n" + + " bitcoind [options] " + "\n" + + " bitcoind [options] [params] " + _("Send command to -server or bitcoind") + "\n" + + " bitcoind [options] help " + _("List commands") + "\n" + + " bitcoind [options] help " + _("Get help for a command") + "\n"; + + strUsage += "\n" + HelpMessage(); + + fprintf(stderr, "%s", strUsage.c_str()); + return false; + } + + // Command-line RPC + for (int i = 1; i < argc; i++) + if (!IsSwitchChar(argv[i][0]) && !(strlen(argv[i]) > 7 && strncasecmp(argv[i], "bitcoin:", 8) == 0)) + fCommandLine = true; + + if (fCommandLine) + { + int ret = CommandLineRPC(argc, argv); + exit(ret); + } + + fRet = AppInit2(); } catch (std::exception& e) { PrintException(&e, "AppInit()"); @@ -118,20 +156,114 @@ Shutdown(NULL); return fRet; } +#endif + +bool static InitError(const std::string &str) +{ + ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxMODAL); + return false; + +} + +bool static InitWarning(const std::string &str) +{ + ThreadSafeMessageBox(str, _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL); + return true; +} + bool static Bind(const CService &addr) { if (IsLimited(addr)) return false; std::string strError; if (!BindListenPort(addr, strError)) - { - ThreadSafeMessageBox(strError, _("Bitcoin"), wxOK | wxMODAL); - return false; - } + return InitError(strError); return true; } -bool AppInit2(int argc, char* argv[]) +// Core-specific options shared between UI and daemon +std::string HelpMessage() +{ + string strUsage = _("Options:") + "\n" + + " -conf= " + _("Specify configuration file (default: bitcoin.conf)") + "\n" + + " -pid= " + _("Specify pid file (default: bitcoind.pid)") + "\n" + + " -gen " + _("Generate coins") + "\n" + + " -gen=0 " + _("Don't generate coins") + "\n" + + " -datadir= " + _("Specify data directory") + "\n" + + " -dbcache= " + _("Set database cache size in megabytes (default: 25)") + "\n" + + " -dblogsize= " + _("Set database disk log size in megabytes (default: 100)") + "\n" + + " -timeout= " + _("Specify connection timeout (in milliseconds)") + "\n" + + " -proxy= " + _("Connect through socks proxy") + "\n" + + " -socks= " + _("Select the version of socks proxy to use (4 or 5, 5 is default)") + "\n" + + " -noproxy= " + _("Do not use proxy for connections to network (IPv4 or IPv6)") + "\n" + + " -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + + " -proxydns " + _("Pass DNS requests to (SOCKS5) proxy") + "\n" + + " -port= " + _("Listen for connections on (default: 8333 or testnet: 18333)") + "\n" + + " -maxconnections= " + _("Maintain at most connections to peers (default: 125)") + "\n" + + " -addnode= " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + + " -connect= " + _("Connect only to the specified node") + "\n" + + " -seednode= " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + + " -externalip= " + _("Specify your own public address") + "\n" + + " -blocknet= " + _("Do not connect to addresses in network (IPv4 or IPv6)") + "\n" + + " -discover " + _("Try to discover public IP address (default: 1)") + "\n" + + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + + " -listen " + _("Accept connections from outside (default: 1)") + "\n" + + " -bind= " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + + " -dnsseed " + _("Find peers using DNS lookup (default: 1)") + "\n" + + " -banscore= " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + + " -bantime= " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + + " -maxreceivebuffer= " + _("Maximum per-connection receive buffer, *1000 bytes (default: 10000)") + "\n" + + " -maxsendbuffer= " + _("Maximum per-connection send buffer, *1000 bytes (default: 10000)") + "\n" + +#ifdef USE_UPNP +#if USE_UPNP + " -upnp " + _("Use Universal Plug and Play to map the listening port (default: 1)") + "\n" + +#else + " -upnp " + _("Use Universal Plug and Play to map the listening port (default: 0)") + "\n" + +#endif +#endif + " -detachdb " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + + " -paytxfee= " + _("Fee per KB to add to transactions you send") + "\n" + +#ifdef QT_GUI + " -server " + _("Accept command line and JSON-RPC commands") + "\n" + +#endif +#if !defined(WIN32) && !defined(QT_GUI) + " -daemon " + _("Run in the background as a daemon and accept commands") + "\n" + +#endif + " -testnet " + _("Use the test network") + "\n" + + " -debug " + _("Output extra debugging information") + "\n" + + " -logtimestamps " + _("Prepend debug output with timestamp") + "\n" + + " -printtoconsole " + _("Send trace/debug info to console instead of debug.log file") + "\n" + +#ifdef WIN32 + " -printtodebugger " + _("Send trace/debug info to debugger") + "\n" + +#endif + " -rpcuser= " + _("Username for JSON-RPC connections") + "\n" + + " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n" + + " -rpcport= " + _("Listen for JSON-RPC connections on (default: 8332)") + "\n" + + " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + + " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + + " -upgradewallet " + _("Upgrade wallet to latest format") + "\n" + + " -keypool= " + _("Set key pool size to (default: 100)") + "\n" + + " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" + + " -checkblocks= " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + + " -checklevel= " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + + " -loadblock= " + _("Imports blocks from external blk000?.dat file") + "\n" + + " -? " + _("This help message") + "\n"; + + strUsage += string() + + _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n" + + " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + + " -rpcsslcertificatechainfile= " + _("Server certificate file (default: server.cert)") + "\n" + + " -rpcsslprivatekeyfile= " + _("Server private key (default: server.pem)") + "\n" + + " -rpcsslciphers= " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; + + return strUsage; +} + +/** Initialize bitcoin. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInit2() { #ifdef _MSC_VER // Turn off microsoft heap dump noise @@ -156,120 +288,6 @@ sigaction(SIGHUP, &sa, NULL); #endif - // - // Parameters - // - // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() -#if !defined(QT_GUI) - ParseParameters(argc, argv); - if (!boost::filesystem::is_directory(GetDataDir(false))) - { - fprintf(stderr, "Error: Specified directory does not exist\n"); - Shutdown(NULL); - } - ReadConfigFile(mapArgs, mapMultiArgs); -#endif - - if (mapArgs.count("-?") || mapArgs.count("--help")) - { - string strUsage = string() + - _("Bitcoin version") + " " + FormatFullVersion() + "\n\n" + - _("Usage:") + "\t\t\t\t\t\t\t\t\t\t\n" + - " bitcoind [options] \t " + "\n" + - " bitcoind [options] [params]\t " + _("Send command to -server or bitcoind") + "\n" + - " bitcoind [options] help \t\t " + _("List commands") + "\n" + - " bitcoind [options] help \t\t " + _("Get help for a command") + "\n" + - _("Options:") + "\n" + - " -conf= \t\t " + _("Specify configuration file (default: bitcoin.conf)") + "\n" + - " -pid= \t\t " + _("Specify pid file (default: bitcoind.pid)") + "\n" + - " -gen \t\t " + _("Generate coins") + "\n" + - " -gen=0 \t\t " + _("Don't generate coins") + "\n" + - " -min \t\t " + _("Start minimized") + "\n" + - " -splash \t\t " + _("Show splash screen on startup (default: 1)") + "\n" + - " -datadir= \t\t " + _("Specify data directory") + "\n" + - " -dbcache= \t\t " + _("Set database cache size in megabytes (default: 25)") + "\n" + - " -dblogsize= \t\t " + _("Set database disk log size in megabytes (default: 100)") + "\n" + - " -timeout= \t " + _("Specify connection timeout (in milliseconds)") + "\n" + - " -proxy= \t " + _("Connect through socks proxy") + "\n" + - " -socks= \t " + _("Select the version of socks proxy to use (4 or 5, 5 is default)") + "\n" + - " -noproxy= \t " + _("Do not use proxy for connections to network (IPv4 or IPv6)") + "\n" + - " -dns \t " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" + - " -proxydns \t " + _("Pass DNS requests to (SOCKS5) proxy") + "\n" + - " -port= \t\t " + _("Listen for connections on (default: 8333 or testnet: 18333)") + "\n" + - " -maxconnections=\t " + _("Maintain at most connections to peers (default: 125)") + "\n" + - " -addnode= \t " + _("Add a node to connect to and attempt to keep the connection open") + "\n" + - " -connect= \t\t " + _("Connect only to the specified node") + "\n" + - " -seednode= \t\t " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" + - " -externalip= \t " + _("Specify your own public address") + "\n" + - " -blocknet= \t " + _("Do not connect to addresses in network (IPv4 or IPv6)") + "\n" + - " -discover \t " + _("Try to discover public IP address (default: 1)") + "\n" + - " -irc \t " + _("Find peers using internet relay chat (default: 0)") + "\n" + - " -listen \t " + _("Accept connections from outside (default: 1)") + "\n" + - " -bind= \t " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + -#ifdef QT_GUI - " -lang= \t\t " + _("Set language, for example \"de_DE\" (default: system locale)") + "\n" + -#endif - " -dnsseed \t " + _("Find peers using DNS lookup (default: 1)") + "\n" + - " -banscore= \t " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" + - " -bantime= \t " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" + - " -maxreceivebuffer=\t " + _("Maximum per-connection receive buffer, *1000 bytes (default: 10000)") + "\n" + - " -maxsendbuffer=\t " + _("Maximum per-connection send buffer, *1000 bytes (default: 10000)") + "\n" + -#ifdef USE_UPNP -#if USE_UPNP - " -upnp \t " + _("Use Universal Plug and Play to map the listening port (default: 1)") + "\n" + -#else - " -upnp \t " + _("Use Universal Plug and Play to map the listening port (default: 0)") + "\n" + -#endif - " -detachdb \t " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + -#endif - " -paytxfee= \t " + _("Fee per KB to add to transactions you send") + "\n" + -#ifdef QT_GUI - " -server \t\t " + _("Accept command line and JSON-RPC commands") + "\n" + -#endif -#if !defined(WIN32) && !defined(QT_GUI) - " -daemon \t\t " + _("Run in the background as a daemon and accept commands") + "\n" + -#endif - " -testnet \t\t " + _("Use the test network") + "\n" + - " -debug \t\t " + _("Output extra debugging information") + "\n" + - " -logtimestamps \t " + _("Prepend debug output with timestamp") + "\n" + - " -printtoconsole \t " + _("Send trace/debug info to console instead of debug.log file") + "\n" + -#ifdef WIN32 - " -printtodebugger \t " + _("Send trace/debug info to debugger") + "\n" + -#endif - " -rpcuser= \t " + _("Username for JSON-RPC connections") + "\n" + - " -rpcpassword=\t " + _("Password for JSON-RPC connections") + "\n" + - " -rpcport= \t\t " + _("Listen for JSON-RPC connections on (default: 8332)") + "\n" + - " -rpcallowip= \t\t " + _("Allow JSON-RPC connections from specified IP address") + "\n" + - " -rpcconnect= \t " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + - " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + - " -upgradewallet \t " + _("Upgrade wallet to latest format") + "\n" + - " -keypool= \t " + _("Set key pool size to (default: 100)") + "\n" + - " -rescan \t " + _("Rescan the block chain for missing wallet transactions") + "\n" + - " -checkblocks= \t\t " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + - " -checklevel= \t\t " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + - " -loadblock=\t " + _("Imports blocks from external blk000?.dat file") + "\n"; - - strUsage += string() + - _("\nSSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n" + - " -rpcssl \t " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n" + - " -rpcsslcertificatechainfile=\t " + _("Server certificate file (default: server.cert)") + "\n" + - " -rpcsslprivatekeyfile= \t " + _("Server private key (default: server.pem)") + "\n" + - " -rpcsslciphers= \t " + _("Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)") + "\n"; - - strUsage += string() + - " -? \t\t " + _("This help message") + "\n"; - - // Remove tabs - strUsage.erase(std::remove(strUsage.begin(), strUsage.end(), '\t'), strUsage.end()); -#if defined(QT_GUI) && defined(WIN32) - // On windows, show a message box, as there is no stderr - ThreadSafeMessageBox(strUsage, _("Usage"), wxOK | wxMODAL); -#else - fprintf(stderr, "%s", strUsage.c_str()); -#endif - return false; - } - fTestNet = GetBoolArg("-testnet"); if (fTestNet) { @@ -298,18 +316,6 @@ fPrintToDebugger = GetBoolArg("-printtodebugger"); fLogTimestamps = GetBoolArg("-logtimestamps"); -#ifndef QT_GUI - for (int i = 1; i < argc; i++) - if (!IsSwitchChar(argv[i][0]) && !(strlen(argv[i]) > 7 && strncasecmp(argv[i], "bitcoin:", 8) == 0)) - fCommandLine = true; - - if (fCommandLine) - { - int ret = CommandLineRPC(argc, argv); - exit(ret); - } -#endif - #if !defined(WIN32) && !defined(QT_GUI) if (fDaemon) { @@ -352,10 +358,7 @@ if (file) fclose(file); static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) - { - ThreadSafeMessageBox(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().string().c_str()), _("Bitcoin"), wxOK|wxMODAL); - return false; - } + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), GetDataDir().string().c_str())); std::ostringstream strErrors; // @@ -414,8 +417,7 @@ { strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n"; printf("%s", strErrors.str().c_str()); - ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL); - return false; + return InitError(strErrors.str()); } else strErrors << _("Error loading wallet.dat") << "\n"; @@ -485,10 +487,7 @@ printf("mapAddressBook.size() = %d\n", pwalletMain->mapAddressBook.size()); if (!strErrors.str().empty()) - { - ThreadSafeMessageBox(strErrors.str(), _("Bitcoin"), wxOK | wxICON_ERROR | wxMODAL); - return false; - } + return InitError(strErrors.str()); // Add wallet transactions that aren't already in a block to mapTransactions pwalletMain->ReacceptWalletTransactions(); @@ -541,20 +540,15 @@ fUseProxy = true; addrProxy = CService(mapArgs["-proxy"], 9050); if (!addrProxy.IsValid()) - { - ThreadSafeMessageBox(_("Invalid -proxy address"), _("Bitcoin"), wxOK | wxMODAL); - return false; - } + return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"].c_str())); } if (mapArgs.count("-noproxy")) { BOOST_FOREACH(std::string snet, mapMultiArgs["-noproxy"]) { enum Network net = ParseNetwork(snet); - if (net == NET_UNROUTABLE) { - ThreadSafeMessageBox(_("Unknown network specified in -noproxy"), _("Bitcoin"), wxOK | wxMODAL); - return false; - } + if (net == NET_UNROUTABLE) + return InitError(strprintf(_("Unknown network specified in -noproxy: '%s'"), snet.c_str())); SetNoProxy(net); } } @@ -581,10 +575,8 @@ if (mapArgs.count("-blocknet")) { BOOST_FOREACH(std::string snet, mapMultiArgs["-blocknet"]) { enum Network net = ParseNetwork(snet); - if (net == NET_UNROUTABLE) { - ThreadSafeMessageBox(_("Unknown network specified in -blocknet"), _("Bitcoin"), wxOK | wxMODAL); - return false; - } + if (net == NET_UNROUTABLE) + return InitError(strprintf(_("Unknown network specified in -blocknet: '%s'"), snet.c_str())); SetLimited(net); } } @@ -595,6 +587,8 @@ fNameLookup = true; fNoListen = !GetBoolArg("-listen", true); nSocksVersion = GetArg("-socks", 5); + if (nSocksVersion != 4 && nSocksVersion != 5) + return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion)); BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) AddOneShot(strDest); @@ -611,7 +605,10 @@ std::string strError; if (mapArgs.count("-bind")) { BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { - fBound |= Bind(CService(strBind, GetListenPort(), false)); + CService addrBind(strBind, GetListenPort(), false); + if (!addrBind.IsValid()) + return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); + fBound |= Bind(addrBind); } } else { struct in_addr inaddr_any; @@ -627,19 +624,20 @@ if (mapArgs.count("-externalip")) { - BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) + BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { + CService addrLocal(strAddr, GetListenPort(), fNameLookup); + if (!addrLocal.IsValid()) + return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr.c_str())); AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); + } } if (mapArgs.count("-paytxfee")) { if (!ParseMoney(mapArgs["-paytxfee"], nTransactionFee)) - { - ThreadSafeMessageBox(_("Invalid amount for -paytxfee="), _("Bitcoin"), wxOK | wxMODAL); - return false; - } + return InitError(strprintf(_("Invalid amount for -paytxfee=: '%s'"), mapArgs["-paytxfee"].c_str())); if (nTransactionFee > 0.25 * COIN) - ThreadSafeMessageBox(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction."), _("Bitcoin"), wxOK | wxICON_EXCLAMATION | wxMODAL); + InitWarning(_("Warning: -paytxfee is set very high. This is the transaction fee you will pay if you send a transaction.")); } // @@ -651,17 +649,14 @@ RandAddSeedPerfmon(); if (!CreateThread(StartNode, NULL)) - ThreadSafeMessageBox(_("Error: CreateThread(StartNode) failed"), _("Bitcoin"), wxOK | wxMODAL); + InitError(_("Error: could not start node")); if (fServer) CreateThread(ThreadRPCServer, NULL); -#ifdef QT_GUI - if (GetStartOnSystemStartup()) - SetStartOnSystemStartup(true); // Remove startup links -#endif - #if !defined(QT_GUI) + // Loop until process is exit()ed from shutdown() function, + // called from ThreadRPCServer thread when a "stop" command is received. while (1) Sleep(5000); #endif diff --git a/src/init.h b/src/init.h --- a/src/init.h +++ b/src/init.h @@ -11,6 +11,7 @@ void Shutdown(void* parg); bool AppInit(int argc, char* argv[]); -bool AppInit2(int argc, char* argv[]); +bool AppInit2(); +std::string HelpMessage(); #endif diff --git a/src/irc.cpp b/src/irc.cpp --- a/src/irc.cpp +++ b/src/irc.cpp @@ -246,11 +246,12 @@ return; } + CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses CService addrLocal; string strMyName; - if (GetLocal(addrLocal, &addrConnect)) + if (GetLocal(addrLocal, &addrIPv4)) strMyName = EncodeAddress(GetLocalAddress(&addrConnect)); - else + if (strMyName == "") strMyName = strprintf("x%u", GetRand(1000000000)); Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str()); diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -211,6 +211,12 @@ if (!addr.IsRoutable()) return false; + if (!GetBoolArg("-discover", true) && nScore < LOCAL_MANUAL) + return false; + + if (!IsLimited(addr)) + return false; + printf("AddLocal(%s,%i)\n", addr.ToString().c_str(), nScore); { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -129,6 +129,53 @@ exit(1); } +/** Help message for Bitcoin-Qt, shown with --help. */ +class HelpMessageBox: public QMessageBox +{ +public: + HelpMessageBox(QWidget *parent = 0); + + void exec(); +private: + QString header; + QString coreOptions; + QString uiOptions; +}; +#include +#include +HelpMessageBox::HelpMessageBox(QWidget *parent): + QMessageBox(parent) +{ + header = tr("Bitcoin-Qt") + " " + tr("version") + " " + + QString::fromStdString(FormatFullVersion()) + "\n\n" + + tr("Usage:") + "\n" + + " bitcoin-qt [options] " + "\n"; + coreOptions = QString::fromStdString(HelpMessage()); + uiOptions = tr("UI options") + ":\n" + + " -lang= " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" + + " -min " + tr("Start minimized") + "\n" + + " -splash " + tr("Show splash screen on startup (default: 1)") + "\n"; + + setWindowTitle(tr("Bitcoin-Qt")); + setTextFormat(Qt::PlainText); + // setMinimumWidth is ignored for QMessageBox so put in nonbreaking spaces to make it wider. + QChar em_space(0x2003); + setText(header + QString(em_space).repeated(40)); + setDetailedText(coreOptions + "\n" + uiOptions); +} + +void HelpMessageBox::exec() +{ +#if defined(WIN32) + // On windows, show a message box, as there is no stderr in windowed applications + QMessageBox::exec(); +#else + // On other operating systems, the expected action is to print the message to the console. + QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions; + fprintf(stderr, "%s", strUsage.toStdString().c_str()); +#endif +} + #ifdef WIN32 #define strncasecmp strnicmp #endif @@ -218,6 +265,15 @@ if (translator.load(lang_territory, ":/translations/")) app.installTranslator(&translator); + // Show help message immediately after parsing command-line options (for "-lang") and setting locale, + // but before showing splash screen. + if (mapArgs.count("-?") || mapArgs.count("--help")) + { + HelpMessageBox help; + help.exec(); + return 1; + } + QSplashScreen splash(QPixmap(":/images/splash"), 0); if (GetBoolArg("-splash", true) && !GetBoolArg("-min")) { @@ -232,9 +288,13 @@ try { + // Regenerate startup link, to fix links to old versions + if (GUIUtil::GetStartOnSystemStartup()) + GUIUtil::SetStartOnSystemStartup(true); + BitcoinGUI window; guiref = &window; - if(AppInit2(argc, argv)) + if(AppInit2()) { { // Put this in a block, so that the Model objects are cleaned up before diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -157,6 +157,7 @@ // Clicking on a transaction on the overview page simply sends you to transaction history page connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), this, SLOT(gotoHistoryPage())); + connect(overviewPage, SIGNAL(transactionClicked(QModelIndex)), transactionView, SLOT(focusTransaction(QModelIndex))); // Doubleclicking on a transaction on the transaction history page shows details connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails())); diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -28,9 +28,6 @@ - - TextLabel - Qt::RichText diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui --- a/src/qt/forms/qrcodedialog.ui +++ b/src/qt/forms/qrcodedialog.ui @@ -7,11 +7,11 @@ 0 0 334 - 423 + 425 - QR-Code Dialog + QR Code Dialog diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -19,6 +19,7 @@ #include #include +#include #ifdef WIN32 #ifdef _WIN32_WINNT @@ -268,5 +269,149 @@ return QObject::eventFilter(obj, evt); } +#ifdef WIN32 +boost::filesystem::path static StartupShortcutPath() +{ + return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; +} + +bool GetStartOnSystemStartup() +{ + // check for Bitcoin.lnk + return boost::filesystem::exists(StartupShortcutPath()); +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + // If the shortcut exists already, remove it for updating + boost::filesystem::remove(StartupShortcutPath()); + + if (fAutoStart) + { + CoInitialize(NULL); + + // Get a pointer to the IShellLink interface. + IShellLink* psl = NULL; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&psl)); + + if (SUCCEEDED(hres)) + { + // Get the current executable path + TCHAR pszExePath[MAX_PATH]; + GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); + + TCHAR pszArgs[5] = TEXT("-min"); + + // Set the path to the shortcut target + psl->SetPath(pszExePath); + PathRemoveFileSpec(pszExePath); + psl->SetWorkingDirectory(pszExePath); + psl->SetShowCmd(SW_SHOWMINNOACTIVE); + psl->SetArguments(pszArgs); + + // Query IShellLink for the IPersistFile interface for + // saving the shortcut in persistent storage. + IPersistFile* ppf = NULL; + hres = psl->QueryInterface(IID_IPersistFile, + reinterpret_cast(&ppf)); + if (SUCCEEDED(hres)) + { + WCHAR pwsz[MAX_PATH]; + // Ensure that the string is ANSI. + MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(pwsz, TRUE); + ppf->Release(); + psl->Release(); + CoUninitialize(); + return true; + } + psl->Release(); + } + CoUninitialize(); + return false; + } + return true; +} + +#elif defined(LINUX) + +// Follow the Desktop Application Autostart Spec: +// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + +boost::filesystem::path static GetAutostartDir() +{ + namespace fs = boost::filesystem; + + char* pszConfigHome = getenv("XDG_CONFIG_HOME"); + if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; + char* pszHome = getenv("HOME"); + if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; + return fs::path(); +} + +boost::filesystem::path static GetAutostartFilePath() +{ + return GetAutostartDir() / "bitcoin.desktop"; +} + +bool GetStartOnSystemStartup() +{ + boost::filesystem::ifstream optionFile(GetAutostartFilePath()); + if (!optionFile.good()) + return false; + // Scan through file for "Hidden=true": + std::string line; + while (!optionFile.eof()) + { + getline(optionFile, line); + if (line.find("Hidden") != std::string::npos && + line.find("true") != std::string::npos) + return false; + } + optionFile.close(); + + return true; +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + if (!fAutoStart) + boost::filesystem::remove(GetAutostartFilePath()); + else + { + char pszExePath[MAX_PATH+1]; + memset(pszExePath, 0, sizeof(pszExePath)); + if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + return false; + + boost::filesystem::create_directories(GetAutostartDir()); + + boost::filesystem::ofstream optionFile(GetAutostartFilePath(), std::ios_base::out|std::ios_base::trunc); + if (!optionFile.good()) + return false; + // Write a bitcoin.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + optionFile << "Name=Bitcoin\n"; + optionFile << "Exec=" << pszExePath << " -min\n"; + optionFile << "Terminal=false\n"; + optionFile << "Hidden=false\n"; + optionFile.close(); + } + return true; +} +#else + +// TODO: OSX startup stuff; see: +// http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html + +bool GetStartOnSystemStartup() { return false; } +bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + +#endif + } // namespace GUIUtil diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -90,6 +90,9 @@ int size_threshold; }; + bool GetStartOnSystemStartup(); + bool SetStartOnSystemStartup(bool fAutoStart); + } // namespace GUIUtil #endif // GUIUTIL_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -4,6 +4,7 @@ #include "init.h" #include "walletdb.h" +#include "guiutil.h" OptionsModel::OptionsModel(QObject *parent) : QAbstractListModel(parent) @@ -107,7 +108,7 @@ switch(index.row()) { case StartAtStartup: - return QVariant(GetStartOnSystemStartup()); + return QVariant(GUIUtil::GetStartOnSystemStartup()); case MinimizeToTray: return QVariant(fMinimizeToTray); case MapPortUPnP: @@ -146,7 +147,7 @@ switch(index.row()) { case StartAtStartup: - successful = SetStartOnSystemStartup(value.toBool()); + successful = GUIUtil::SetStartOnSystemStartup(value.toBool()); break; case MinimizeToTray: fMinimizeToTray = value.toBool(); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -94,7 +94,7 @@ ui(new Ui::OverviewPage), currentBalance(-1), currentUnconfirmedBalance(-1), - txdelegate(new TxViewDelegate()) + txdelegate(new TxViewDelegate()), filter(0) { ui->setupUi(this); @@ -104,7 +104,13 @@ ui->listTransactions->setMinimumHeight(NUM_ITEMS * (DECORATION_SIZE + 2)); ui->listTransactions->setAttribute(Qt::WA_MacShowFocusRect, false); - connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SIGNAL(transactionClicked(QModelIndex))); + connect(ui->listTransactions, SIGNAL(clicked(QModelIndex)), this, SLOT(handleTransactionClicked(QModelIndex))); +} + +void OverviewPage::handleTransactionClicked(const QModelIndex &index) +{ + if(filter) + emit transactionClicked(filter->mapToSource(index)); } OverviewPage::~OverviewPage() @@ -132,7 +138,7 @@ if(model) { // Set up transaction list - TransactionFilterProxy *filter = new TransactionFilterProxy(); + filter = new TransactionFilterProxy(); filter->setSourceModel(model->getTransactionTableModel()); filter->setLimit(NUM_ITEMS); filter->setDynamicSortFilter(true); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -12,6 +12,7 @@ } class WalletModel; class TxViewDelegate; +class TransactionFilterProxy; /** Overview ("home") page widget */ class OverviewPage : public QWidget @@ -38,9 +39,11 @@ qint64 currentUnconfirmedBalance; TxViewDelegate *txdelegate; + TransactionFilterProxy *filter; private slots: void displayUnitChanged(); + void handleTransactionClicked(const QModelIndex &index); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -130,28 +130,28 @@ break; case WalletModel::AmountExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), - tr("Amount exceeds your balance"), + tr("The amount exceeds your balance."), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), - tr("Total exceeds your balance when the %1 transaction fee is included"). + tr("The total exceeds your balance when the %1 transaction fee is included."). arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::DuplicateAddress: QMessageBox::warning(this, tr("Send Coins"), - tr("Duplicate address found, can only send to each address once in one send operation"), + tr("Duplicate address found, can only send to each address once per send operation."), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::TransactionCreationFailed: QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed "), + tr("Error: Transaction creation failed."), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::TransactionCommitFailed: QMessageBox::warning(this, tr("Send Coins"), - tr("Error: 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."), + tr("Error: 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."), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::Aborted: // User aborted, nothing to do diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -417,3 +417,13 @@ QDateTime(dateFrom->date()), QDateTime(dateTo->date()).addDays(1)); } + +void TransactionView::focusTransaction(const QModelIndex &idx) +{ + if(!transactionProxyModel) + return; + QModelIndex targetIdx = transactionProxyModel->mapFromSource(idx); + transactionView->scrollTo(targetIdx); + transactionView->setCurrentIndex(targetIdx); + transactionView->setFocus(); +} diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -75,6 +75,7 @@ void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked(); + void focusTransaction(const QModelIndex&); }; diff --git a/src/util.cpp b/src/util.cpp --- a/src/util.cpp +++ b/src/util.cpp @@ -48,7 +48,6 @@ #define NOMINMAX #endif #include "shlobj.h" -#include "shlwapi.h" #endif using namespace std; @@ -1078,146 +1077,4 @@ printf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); return fs::path(""); } - -boost::filesystem::path static StartupShortcutPath() -{ - return GetSpecialFolderPath(CSIDL_STARTUP) / "Bitcoin.lnk"; -} - -bool GetStartOnSystemStartup() -{ - // check for Bitcoin.lnk - return boost::filesystem::exists(StartupShortcutPath()); -} - -bool SetStartOnSystemStartup(bool fAutoStart) -{ - // If the shortcut exists already, remove it for updating - boost::filesystem::remove(StartupShortcutPath()); - - if (fAutoStart) - { - CoInitialize(NULL); - - // Get a pointer to the IShellLink interface. - IShellLink* psl = NULL; - HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, - CLSCTX_INPROC_SERVER, IID_IShellLink, - reinterpret_cast(&psl)); - - if (SUCCEEDED(hres)) - { - // Get the current executable path - TCHAR pszExePath[MAX_PATH]; - GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); - - TCHAR pszArgs[5] = TEXT("-min"); - - // Set the path to the shortcut target - psl->SetPath(pszExePath); - PathRemoveFileSpec(pszExePath); - psl->SetWorkingDirectory(pszExePath); - psl->SetShowCmd(SW_SHOWMINNOACTIVE); - psl->SetArguments(pszArgs); - - // Query IShellLink for the IPersistFile interface for - // saving the shortcut in persistent storage. - IPersistFile* ppf = NULL; - hres = psl->QueryInterface(IID_IPersistFile, - reinterpret_cast(&ppf)); - if (SUCCEEDED(hres)) - { - WCHAR pwsz[MAX_PATH]; - // Ensure that the string is ANSI. - MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); - // Save the link by calling IPersistFile::Save. - hres = ppf->Save(pwsz, TRUE); - ppf->Release(); - psl->Release(); - CoUninitialize(); - return true; - } - psl->Release(); - } - CoUninitialize(); - return false; - } - return true; -} - -#elif defined(LINUX) - -// Follow the Desktop Application Autostart Spec: -// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html - -boost::filesystem::path static GetAutostartDir() -{ - namespace fs = boost::filesystem; - - char* pszConfigHome = getenv("XDG_CONFIG_HOME"); - if (pszConfigHome) return fs::path(pszConfigHome) / "autostart"; - char* pszHome = getenv("HOME"); - if (pszHome) return fs::path(pszHome) / ".config" / "autostart"; - return fs::path(); -} - -boost::filesystem::path static GetAutostartFilePath() -{ - return GetAutostartDir() / "bitcoin.desktop"; -} - -bool GetStartOnSystemStartup() -{ - boost::filesystem::ifstream optionFile(GetAutostartFilePath()); - if (!optionFile.good()) - return false; - // Scan through file for "Hidden=true": - string line; - while (!optionFile.eof()) - { - getline(optionFile, line); - if (line.find("Hidden") != string::npos && - line.find("true") != string::npos) - return false; - } - optionFile.close(); - - return true; -} - -bool SetStartOnSystemStartup(bool fAutoStart) -{ - if (!fAutoStart) - boost::filesystem::remove(GetAutostartFilePath()); - else - { - char pszExePath[MAX_PATH+1]; - memset(pszExePath, 0, sizeof(pszExePath)); - if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) - return false; - - boost::filesystem::create_directories(GetAutostartDir()); - - boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc); - if (!optionFile.good()) - return false; - // Write a bitcoin.desktop file to the autostart directory: - optionFile << "[Desktop Entry]\n"; - optionFile << "Type=Application\n"; - optionFile << "Name=Bitcoin\n"; - optionFile << "Exec=" << pszExePath << " -min\n"; - optionFile << "Terminal=false\n"; - optionFile << "Hidden=false\n"; - optionFile.close(); - } - return true; -} -#else - -// TODO: OSX startup stuff; see: -// http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html - -bool GetStartOnSystemStartup() { return false; } -bool SetStartOnSystemStartup(bool fAutoStart) { return false; } - #endif diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -162,8 +162,6 @@ #ifdef WIN32 boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif -bool GetStartOnSystemStartup(); -bool SetStartOnSystemStartup(bool fAutoStart); void ShrinkDebugFile(); int GetRandInt(int nMax); uint64 GetRand(uint64 nMax); @@ -182,8 +180,6 @@ - - inline std::string i64tostr(int64 n) { return strprintf("%"PRI64d, n);