diff options
Diffstat (limited to 'src/qt')
-rw-r--r-- | src/qt/bitcoin.cpp | 62 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 7 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 6 | ||||
-rw-r--r-- | src/qt/clientmodel.h | 1 | ||||
-rw-r--r-- | src/qt/csvmodelwriter.cpp | 5 | ||||
-rw-r--r-- | src/qt/forms/askpassphrasedialog.ui | 3 | ||||
-rw-r--r-- | src/qt/forms/qrcodedialog.ui | 4 | ||||
-rw-r--r-- | src/qt/forms/rpcconsole.ui | 148 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsdialog.ui | 2 | ||||
-rw-r--r-- | src/qt/forms/transactiondescdialog.ui | 4 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 147 | ||||
-rw-r--r-- | src/qt/guiutil.h | 3 | ||||
-rw-r--r-- | src/qt/messagepage.cpp | 1 | ||||
-rw-r--r-- | src/qt/optionsmodel.cpp | 5 | ||||
-rw-r--r-- | src/qt/overviewpage.cpp | 12 | ||||
-rw-r--r-- | src/qt/overviewpage.h | 3 | ||||
-rw-r--r-- | src/qt/qtipcserver.cpp | 1 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 164 | ||||
-rw-r--r-- | src/qt/rpcconsole.h | 8 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 10 | ||||
-rw-r--r-- | src/qt/transactionview.cpp | 10 | ||||
-rw-r--r-- | src/qt/transactionview.h | 1 |
22 files changed, 442 insertions, 165 deletions
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 91f6a56c82..ab0a37abff 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -129,6 +129,53 @@ static void handleRunawayException(std::exception *e) 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 <QSpacerItem> +#include <QGridLayout> +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=<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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 index 855fec3a8b..6b97d97656 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,8 +1,8 @@ /* * Qt4 bitcoin GUI. * - * W.J. van der Laan 20011-2012 - * The Bitcoin Developers 20011-2012 + * W.J. van der Laan 2011-2012 + * The Bitcoin Developers 2011-2012 */ #include "bitcoingui.h" #include "transactiontablemodel.h" @@ -72,6 +72,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): resize(850, 550); setWindowTitle(tr("Bitcoin Wallet")); #ifndef Q_WS_MAC + qApp->setWindowIcon(QIcon(":icons/bitcoin")); setWindowIcon(QIcon(":icons/bitcoin")); #else setUnifiedTitleAndToolBarOnMac(true); @@ -157,6 +158,7 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): // 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())); @@ -331,6 +333,7 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) { setWindowTitle(windowTitle() + QString(" ") + tr("[testnet]")); #ifndef Q_WS_MAC + qApp->setWindowIcon(QIcon(":icons/bitcoin_testnet")); setWindowIcon(QIcon(":icons/bitcoin_testnet")); #else MacDockIconHandler::instance()->setIcon(QIcon(":icons/bitcoin_testnet")); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index d7172fd9cd..85ab03612d 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -5,6 +5,7 @@ #include "transactiontablemodel.h" #include "main.h" +static const int64 nClientStartupTime = GetTime(); #include <QDateTime> @@ -98,3 +99,8 @@ QString ClientModel::clientName() const { return QString::fromStdString(CLIENT_NAME); } + +QDateTime ClientModel::formatClientStartupTime() const +{ + return QDateTime::fromTime_t(nClientStartupTime); +} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 74e0c0688f..67835db727 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -39,6 +39,7 @@ public: QString formatFullVersion() const; QString formatBuildDate() const; QString clientName() const; + QDateTime formatClientStartupTime() const; private: OptionsModel *optionsModel; diff --git a/src/qt/csvmodelwriter.cpp b/src/qt/csvmodelwriter.cpp index 84578b3322..8a50bbab3f 100644 --- a/src/qt/csvmodelwriter.cpp +++ b/src/qt/csvmodelwriter.cpp @@ -27,8 +27,9 @@ void CSVModelWriter::addColumn(const QString &title, int column, int role) static void writeValue(QTextStream &f, const QString &value) { - // TODO: quoting if " or \n in string - f << "\"" << value << "\""; + QString escaped = value; + escaped.replace('"', "\"\""); + f << "\"" << escaped << "\""; } static void writeSep(QTextStream &f) diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index 1383af7a70..e4d86f7cf9 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -28,9 +28,6 @@ <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> diff --git a/src/qt/forms/qrcodedialog.ui b/src/qt/forms/qrcodedialog.ui index ef21841c26..6199b68749 100644 --- a/src/qt/forms/qrcodedialog.ui +++ b/src/qt/forms/qrcodedialog.ui @@ -7,11 +7,11 @@ <x>0</x> <y>0</y> <width>334</width> - <height>423</height> + <height>425</height> </rect> </property> <property name="windowTitle"> - <string>QR-Code Dialog</string> + <string>QR Code Dialog</string> </property> <layout class="QVBoxLayout" name="verticalLayout_3"> <item> diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index 02164f76b5..cded274792 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>706</width> - <height>382</height> + <height>446</height> </rect> </property> <property name="windowTitle"> @@ -27,6 +27,19 @@ <property name="horizontalSpacing"> <number>12</number> </property> + <item row="0" column="0"> + <widget class="QLabel" name="label_9"> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>Client</string> + </property> + </widget> + </item> <item row="1" column="0"> <widget class="QLabel" name="label_5"> <property name="text"> @@ -36,6 +49,9 @@ </item> <item row="1" column="1"> <widget class="QLabel" name="clientName"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> <property name="text"> <string>N/A</string> </property> @@ -56,6 +72,9 @@ </item> <item row="2" column="1"> <widget class="QLabel" name="clientVersion"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> <property name="text"> <string>N/A</string> </property> @@ -67,20 +86,53 @@ </property> </widget> </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_9"> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> + <item row="3" column="0"> + <widget class="QLabel" name="label_12"> + <property name="text"> + <string>Build date</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLabel" name="buildDate"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> </property> <property name="text"> - <string>Version</string> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> </property> </widget> </item> <item row="4" column="0"> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>Startup time</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QLabel" name="startupTime"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> + <property name="text"> + <string>N/A</string> + </property> + <property name="textFormat"> + <enum>Qt::PlainText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item row="5" column="0"> <widget class="QLabel" name="label_11"> <property name="font"> <font> @@ -93,15 +145,18 @@ </property> </widget> </item> - <item row="5" column="0"> + <item row="6" column="0"> <widget class="QLabel" name="label_7"> <property name="text"> <string>Number of connections</string> </property> </widget> </item> - <item row="5" column="1"> + <item row="6" column="1"> <widget class="QLabel" name="numberOfConnections"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> <property name="text"> <string>N/A</string> </property> @@ -113,14 +168,14 @@ </property> </widget> </item> - <item row="6" column="0"> + <item row="7" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> <string>On testnet</string> </property> </widget> </item> - <item row="6" column="1"> + <item row="7" column="1"> <widget class="QCheckBox" name="isTestNet"> <property name="enabled"> <bool>false</bool> @@ -130,7 +185,7 @@ </property> </widget> </item> - <item row="7" column="0"> + <item row="8" column="0"> <widget class="QLabel" name="label_10"> <property name="font"> <font> @@ -143,15 +198,18 @@ </property> </widget> </item> - <item row="8" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>Current number of blocks</string> </property> </widget> </item> - <item row="8" column="1"> + <item row="9" column="1"> <widget class="QLabel" name="numberOfBlocks"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> <property name="text"> <string>N/A</string> </property> @@ -163,15 +221,18 @@ </property> </widget> </item> - <item row="9" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Estimated total blocks</string> </property> </widget> </item> - <item row="9" column="1"> + <item row="10" column="1"> <widget class="QLabel" name="totalBlocks"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> <property name="text"> <string>N/A</string> </property> @@ -183,15 +244,18 @@ </property> </widget> </item> - <item row="10" column="0"> + <item row="11" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Last block time</string> </property> </widget> </item> - <item row="10" column="1"> + <item row="11" column="1"> <widget class="QLabel" name="lastBlockTime"> + <property name="cursor"> + <cursorShape>IBeamCursor</cursorShape> + </property> <property name="text"> <string>N/A</string> </property> @@ -203,7 +267,7 @@ </property> </widget> </item> - <item row="11" column="0"> + <item row="12" column="0"> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -216,7 +280,7 @@ </property> </spacer> </item> - <item row="12" column="0"> + <item row="13" column="0"> <widget class="QLabel" name="labelDebugLogfile"> <property name="font"> <font> @@ -229,7 +293,7 @@ </property> </widget> </item> - <item row="13" column="0"> + <item row="14" column="0"> <widget class="QPushButton" name="openDebugLogfileButton"> <property name="toolTip"> <string>Open the Bitcoin debug logfile from the current data directory. This can take a few seconds for large logfiles.</string> @@ -239,7 +303,7 @@ </property> </widget> </item> - <item row="14" column="0"> + <item row="15" column="0"> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -252,20 +316,6 @@ </property> </spacer> </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_12"> - <property name="text"> - <string>Build date</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="buildDate"> - <property name="text"> - <string>N/A</string> - </property> - </widget> - </item> </layout> </widget> <widget class="QWidget" name="tab_console"> @@ -277,30 +327,22 @@ <number>3</number> </property> <item> - <widget class="QTableWidget" name="messagesWidget"> + <widget class="QTextEdit" name="messagesWidget"> <property name="minimumSize"> <size> <width>0</width> <height>100</height> </size> </property> - <property name="tabKeyNavigation"> - <bool>false</bool> + <property name="readOnly"> + <bool>true</bool> </property> - <property name="selectionBehavior"> - <enum>QAbstractItemView::SelectRows</enum> + <property name="tabKeyNavigation" stdset="0"> + <bool>false</bool> </property> - <property name="columnCount"> + <property name="columnCount" stdset="0"> <number>2</number> </property> - <attribute name="horizontalHeaderVisible"> - <bool>false</bool> - </attribute> - <attribute name="verticalHeaderVisible"> - <bool>false</bool> - </attribute> - <column/> - <column/> </widget> </item> <item> @@ -311,7 +353,7 @@ <item> <widget class="QLabel" name="label"> <property name="text"> - <string>></string> + <string notr="true">></string> </property> </widget> </item> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 49b4580dcf..023a72ac2b 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -84,7 +84,7 @@ <string>Remove all transaction fields</string> </property> <property name="text"> - <string>Clear all</string> + <string>Clear &All</string> </property> <property name="icon"> <iconset resource="../bitcoin.qrc"> diff --git a/src/qt/forms/transactiondescdialog.ui b/src/qt/forms/transactiondescdialog.ui index 9a9f6db158..039cb082cc 100644 --- a/src/qt/forms/transactiondescdialog.ui +++ b/src/qt/forms/transactiondescdialog.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>600</width> + <height>250</height> </rect> </property> <property name="windowTitle"> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 67cbc51bd0..22c0bfeebe 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -19,6 +19,7 @@ #include <QThread> #include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> #ifdef WIN32 #ifdef _WIN32_WINNT @@ -256,7 +257,7 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) { QWidget *widget = static_cast<QWidget*>(obj); QString tooltip = widget->toolTip(); - if(!Qt::mightBeRichText(tooltip) && tooltip.size() > size_threshold) + if(tooltip.size() > size_threshold && !tooltip.startsWith("<qt/>") && !Qt::mightBeRichText(tooltip)) { // Prefix <qt/> to make sure Qt detects this as rich text // Escape the current message as HTML and replace \n by <br> @@ -268,5 +269,149 @@ bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt) 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<void**>(&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<void**>(&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 index 8d1a01e07e..f30d5db35b 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -90,6 +90,9 @@ namespace GUIUtil int size_threshold; }; + bool GetStartOnSystemStartup(); + bool SetStartOnSystemStartup(bool fAutoStart); + } // namespace GUIUtil #endif // GUIUTIL_H diff --git a/src/qt/messagepage.cpp b/src/qt/messagepage.cpp index c04d8b2c78..1f895e28ff 100644 --- a/src/qt/messagepage.cpp +++ b/src/qt/messagepage.cpp @@ -10,7 +10,6 @@ #include "main.h" #include "wallet.h" #include "init.h" -#include "util.h" #include "messagepage.h" #include "ui_messagepage.h" diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 78448d3ee1..181dec4400 100644 --- 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 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const switch(index.row()) { case StartAtStartup: - return QVariant(GetStartOnSystemStartup()); + return QVariant(GUIUtil::GetStartOnSystemStartup()); case MinimizeToTray: return QVariant(fMinimizeToTray); case MapPortUPnP: @@ -146,7 +147,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in 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 index 259f819deb..d0ba377967 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -94,7 +94,7 @@ OverviewPage::OverviewPage(QWidget *parent) : ui(new Ui::OverviewPage), currentBalance(-1), currentUnconfirmedBalance(-1), - txdelegate(new TxViewDelegate()) + txdelegate(new TxViewDelegate()), filter(0) { ui->setupUi(this); @@ -104,7 +104,13 @@ OverviewPage::OverviewPage(QWidget *parent) : 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 @@ void OverviewPage::setModel(WalletModel *model) 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 index 1199227168..1acd1b7f39 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -12,6 +12,7 @@ namespace Ui { } class WalletModel; class TxViewDelegate; +class TransactionFilterProxy; /** Overview ("home") page widget */ class OverviewPage : public QWidget @@ -38,9 +39,11 @@ private: qint64 currentUnconfirmedBalance; TxViewDelegate *txdelegate; + TransactionFilterProxy *filter; private slots: void displayUnitChanged(); + void handleTransactionClicked(const QModelIndex &index); }; #endif // OVERVIEWPAGE_H diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp index 102ac0ff4e..06ada5aaca 100644 --- a/src/qt/qtipcserver.cpp +++ b/src/qt/qtipcserver.cpp @@ -8,7 +8,6 @@ #include <boost/date_time/posix_time/posix_time.hpp> #include "ui_interface.h" -#include "util.h" #include "qtipcserver.h" using namespace boost::interprocess; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 85f79309f3..51051f72f0 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -10,6 +10,8 @@ #include <QThread> #include <QTextEdit> #include <QKeyEvent> +#include <QUrl> +#include <QScrollBar> #include <boost/tokenizer.hpp> @@ -19,6 +21,19 @@ const int CONSOLE_SCROLLBACK = 50; const int CONSOLE_HISTORY = 50; +const QSize ICON_SIZE(24, 24); + +const struct { + const char *url; + const char *source; +} ICON_MAPPING[] = { + {"cmd-request", ":/icons/tx_input"}, + {"cmd-reply", ":/icons/tx_output"}, + {"cmd-error", ":/icons/tx_output"}, + {"misc", ":/icons/tx_inout"}, + {NULL, NULL} +}; + /* Object for executing console RPC commands in a separate thread. */ class RPCExecutor: public QObject @@ -41,19 +56,26 @@ void RPCExecutor::start() void RPCExecutor::request(const QString &command) { // Parse shell-like command line into separate arguments - boost::escaped_list_separator<char> els('\\',' ','\"'); - std::string strCommand = command.toStdString(); - boost::tokenizer<boost::escaped_list_separator<char> > tok(strCommand, els); - std::string strMethod; std::vector<std::string> strParams; - int n = 0; - for(boost::tokenizer<boost::escaped_list_separator<char> >::iterator beg=tok.begin(); beg!=tok.end();++beg,++n) + try { + boost::escaped_list_separator<char> els('\\',' ','\"'); + std::string strCommand = command.toStdString(); + boost::tokenizer<boost::escaped_list_separator<char> > tok(strCommand, els); + + int n = 0; + for(boost::tokenizer<boost::escaped_list_separator<char> >::iterator beg=tok.begin(); beg!=tok.end();++beg,++n) + { + if(n == 0) // First parameter is the command + strMethod = *beg; + else + strParams.push_back(*beg); + } + } + catch(boost::escaped_list_error &e) { - if(n == 0) // First parameter is the command - strMethod = *beg; - else - strParams.push_back(*beg); + emit reply(RPCConsole::CMD_ERROR, QString("Parse error")); + return; } try { @@ -83,12 +105,9 @@ void RPCExecutor::request(const QString &command) RPCConsole::RPCConsole(QWidget *parent) : QDialog(parent), ui(new Ui::RPCConsole), - firstLayout(true), historyPtr(0) { ui->setupUi(this); - ui->messagesWidget->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch); - ui->messagesWidget->setContextMenuPolicy(Qt::ActionsContextMenu); #ifndef WIN32 // Show Debug logfile label and Open button only for Windows @@ -99,13 +118,6 @@ RPCConsole::RPCConsole(QWidget *parent) : // Install event filter for up and down arrow ui->lineEdit->installEventFilter(this); - // Add "Copy message" to context menu explicitly - QAction *copyMessageAction = new QAction(tr("&Copy"), this); - copyMessageAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_C)); - copyMessageAction->setShortcutContext(Qt::WidgetShortcut); - connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage())); - ui->messagesWidget->addAction(copyMessageAction); - connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear())); connect(ui->openDebugLogfileButton, SIGNAL(clicked()), this, SLOT(on_openDebugLogfileButton_clicked())); @@ -150,6 +162,7 @@ void RPCConsole::setClientModel(ClientModel *model) ui->clientVersion->setText(model->formatFullVersion()); ui->clientName->setText(model->clientName()); ui->buildDate->setText(model->formatBuildDate()); + ui->startupTime->setText(model->formatClientStartupTime().toString()); setNumConnections(model->getNumConnections()); ui->isTestNet->setChecked(model->isTestNet()); @@ -158,68 +171,62 @@ void RPCConsole::setClientModel(ClientModel *model) } } -static QColor categoryColor(int category) +static QString categoryClass(int category) { switch(category) { - case RPCConsole::MC_ERROR: return QColor(255,0,0); break; - case RPCConsole::MC_DEBUG: return QColor(192,192,192); break; - case RPCConsole::CMD_REQUEST: return QColor(128,128,128); break; - case RPCConsole::CMD_REPLY: return QColor(128,255,128); break; - case RPCConsole::CMD_ERROR: return QColor(255,128,128); break; - default: return QColor(0,0,0); + case RPCConsole::CMD_REQUEST: return "cmd-request"; break; + case RPCConsole::CMD_REPLY: return "cmd-reply"; break; + case RPCConsole::CMD_ERROR: return "cmd-error"; break; + default: return "misc"; } } void RPCConsole::clear() { ui->messagesWidget->clear(); - ui->messagesWidget->setRowCount(0); ui->lineEdit->clear(); ui->lineEdit->setFocus(); - message(CMD_REPLY, tr("Welcome to the bitcoin RPC console.")+"\n"+ - tr("Use up and down arrows to navigate history, and Ctrl-L to clear screen.")+"\n"+ - tr("Type \"help\" for an overview of available commands.")); + // Add smoothly scaled icon images. + // (when using width/height on an img, Qt uses nearest instead of linear interpolation) + for(int i=0; ICON_MAPPING[i].url; ++i) + { + ui->messagesWidget->document()->addResource( + QTextDocument::ImageResource, + QUrl(ICON_MAPPING[i].url), + QImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + + // Set default style sheet + ui->messagesWidget->document()->setDefaultStyleSheet( + "table { }" + "td.time { color: #808080; padding-top: 3px; } " + "td.message { font-family: Monospace; font-size: 12px; } " + "td.cmd-request { color: #006060; } " + "td.cmd-error { color: red; } " + "b { color: #006060; } " + ); + + message(CMD_REPLY, tr("Welcome to the Bitcoin RPC console.<br>" + "Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.<br>" + "Type <b>help</b> for an overview of available commands."), true); } -void RPCConsole::message(int category, const QString &message) +void RPCConsole::message(int category, const QString &message, bool html) { - // Add row to messages widget - int row = ui->messagesWidget->rowCount(); - ui->messagesWidget->setRowCount(row+1); - QTime time = QTime::currentTime(); - QTableWidgetItem *newTime = new QTableWidgetItem(time.toString()); - newTime->setData(Qt::DecorationRole, categoryColor(category)); - newTime->setForeground(QColor(128,128,128)); - newTime->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); // make non-editable - - int numLines = message.count("\n") + 1; - // As Qt doesn't like very tall cells (they break scrolling) keep only short messages in - // the cell text, longer messages trigger a display widget with scroll bar - if(numLines < 5) - { - QTableWidgetItem *newItem = new QTableWidgetItem(message); - newItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); // make non-editable - if(category == CMD_ERROR) // Coloring error messages in red - newItem->setForeground(QColor(255,16,16)); - ui->messagesWidget->setItem(row, 1, newItem); - } else { - QTextEdit *newWidget = new QTextEdit; - newWidget->setText(message); - newWidget->setMaximumHeight(100); - newWidget->setReadOnly(true); - ui->messagesWidget->setCellWidget(row, 1, newWidget); - } - - ui->messagesWidget->setItem(row, 0, newTime); - ui->messagesWidget->resizeRowToContents(row); - // Preserve only limited scrollback buffer - while(ui->messagesWidget->rowCount() > CONSOLE_SCROLLBACK) - ui->messagesWidget->removeRow(0); - // Scroll to bottom after table is updated - QTimer::singleShot(0, ui->messagesWidget, SLOT(scrollToBottom())); + QString timeString = time.toString(); + QString out; + out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>"; + out += "<td class=\"icon\" width=\"32\"><img src=\"" + categoryClass(category) + "\"></td>"; + out += "<td class=\"message " + categoryClass(category) + "\" valign=\"middle\">"; + if(html) + out += message; + else + out += GUIUtil::HtmlEscape(message, true); + out += "</td></tr></table>"; + ui->messagesWidget->append(out); } void RPCConsole::setNumConnections(int count) @@ -232,7 +239,8 @@ void RPCConsole::setNumBlocks(int count) ui->numberOfBlocks->setText(QString::number(count)); if(clientModel) { - ui->totalBlocks->setText(QString::number(clientModel->getNumBlocksOfPeers())); + // If there is no current number available display N/A instead of 0, which can't ever be true + ui->totalBlocks->setText(clientModel->getNumBlocksOfPeers() == 0 ? tr("N/A") : QString::number(clientModel->getNumBlocksOfPeers())); ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString()); } } @@ -255,6 +263,8 @@ void RPCConsole::on_lineEdit_returnPressed() history.removeFirst(); // Set pointer to end of history historyPtr = history.size(); + // Scroll console view to end + scrollToEnd(); } } @@ -296,24 +306,10 @@ void RPCConsole::startExecutor() thread->start(); } -void RPCConsole::copyMessage() -{ - GUIUtil::copyEntryData(ui->messagesWidget, 1, Qt::EditRole); -} - void RPCConsole::on_tabWidget_currentChanged(int index) { if(ui->tabWidget->widget(index) == ui->tab_console) { - if(firstLayout) - { - // Work around QTableWidget issue: - // Call resizeRowsToContents on first Layout request with widget visible, - // to make sure multiline messages that were added before the console was shown - // have the right height. - firstLayout = false; - ui->messagesWidget->resizeRowsToContents(); - } ui->lineEdit->setFocus(); } } @@ -322,3 +318,9 @@ void RPCConsole::on_openDebugLogfileButton_clicked() { GUIUtil::openDebugLogfile(); } + +void RPCConsole::scrollToEnd() +{ + QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar(); + scrollbar->setValue(scrollbar->maximum()); +} diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 30948eaad2..0a7b10f4a2 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -37,16 +37,15 @@ private slots: public slots: void clear(); - void message(int category, const QString &message); + void message(int category, const QString &message, bool html = false); /** Set number of connections shown in the UI */ void setNumConnections(int count); /** Set number of blocks shown in the UI */ void setNumBlocks(int count); /** Go forward or back in history */ void browseHistory(int offset); - /** Copy currently selected message to clipboard */ - void copyMessage(); - + /** Scroll console view to end */ + void scrollToEnd(); signals: // For RPC command executor void stopExecutor(); @@ -55,7 +54,6 @@ signals: private: Ui::RPCConsole *ui; ClientModel *clientModel; - bool firstLayout; QStringList history; int historyPtr; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b4029aa0d2..f6a3047a2b 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -130,28 +130,28 @@ void SendCoinsDialog::on_sendButton_clicked() 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 index 1c427d6fe4..a0e7dd4e77 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -417,3 +417,13 @@ void TransactionView::dateRangeChanged() 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 index bc6e1e4e05..4ade3ecd5f 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -75,6 +75,7 @@ public slots: void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked(); + void focusTransaction(const QModelIndex&); }; |