Page MenuHomePhabricator

No OneTemporary

diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index e8df8ad7b..5dedc90d6 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -1,454 +1,452 @@
#include "guiutil.h"
#include "bitcoinaddressvalidator.h"
#include "walletmodel.h"
#include "bitcoinunits.h"
#include "util.h"
#include "init.h"
#include <QString>
#include <QDateTime>
#include <QDoubleValidator>
#include <QFont>
#include <QLineEdit>
#include <QUrl>
#include <QTextDocument> // For Qt::escape
#include <QAbstractItemView>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QDesktopServices>
#include <QThread>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "shlwapi.h"
#include "shlobj.h"
#include "shellapi.h"
#endif
namespace GUIUtil {
QString dateTimeStr(const QDateTime &date)
{
return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + date.toString("hh:mm");
}
QString dateTimeStr(qint64 nTime)
{
return dateTimeStr(QDateTime::fromTime_t((qint32)nTime));
}
QFont bitcoinAddressFont()
{
QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter);
return font;
}
void setupAddressWidget(QLineEdit *widget, QWidget *parent)
{
widget->setMaxLength(BitcoinAddressValidator::MaxAddressLength);
widget->setValidator(new BitcoinAddressValidator(parent));
widget->setFont(bitcoinAddressFont());
}
void 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 parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
{
if(uri.scheme() != QString("bitcoin"))
return false;
SendCoinsRecipient rv;
rv.address = uri.path();
rv.amount = 0;
QList<QPair<QString, QString> > items = uri.queryItems();
for (QList<QPair<QString, QString> >::iterator i = items.begin(); i != items.end(); i++)
{
bool fShouldReturnFalse = false;
if (i->first.startsWith("req-"))
{
i->first.remove(0, 4);
fShouldReturnFalse = true;
}
if (i->first == "label")
{
rv.label = i->second;
fShouldReturnFalse = false;
}
else if (i->first == "amount")
{
if(!i->second.isEmpty())
{
if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
{
return false;
}
}
fShouldReturnFalse = false;
}
if (fShouldReturnFalse)
return false;
}
if(out)
{
*out = rv;
}
return true;
}
bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
{
// Convert bitcoin:// to bitcoin:
//
// Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
// which will lowercase it (and thus invalidate the address).
if(uri.startsWith("bitcoin://"))
{
uri.replace(0, 10, "bitcoin:");
}
QUrl uriInstance(uri);
return parseBitcoinURI(uriInstance, out);
}
QString HtmlEscape(const QString& str, bool fMultiLine)
{
QString escaped = Qt::escape(str);
if(fMultiLine)
{
escaped = escaped.replace("\n", "<br>\n");
}
return escaped;
}
QString HtmlEscape(const std::string& str, bool fMultiLine)
{
return HtmlEscape(QString::fromStdString(str), fMultiLine);
}
void copyEntryData(QAbstractItemView *view, int column, int role)
{
if(!view || !view->selectionModel())
return;
QModelIndexList selection = view->selectionModel()->selectedRows(column);
if(!selection.isEmpty())
{
// Copy first item
QApplication::clipboard()->setText(selection.at(0).data(role).toString());
}
}
QString getSaveFileName(QWidget *parent, const QString &caption,
const QString &dir,
const QString &filter,
QString *selectedSuffixOut)
{
QString selectedFilter;
QString myDir;
if(dir.isEmpty()) // Default to user documents location
{
myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
}
else
{
myDir = dir;
}
QString result = QFileDialog::getSaveFileName(parent, caption, myDir, filter, &selectedFilter);
/* Extract first suffix from filter pattern "Description (*.foo)" or "Description (*.foo *.bar ...) */
QRegExp filter_re(".* \\(\\*\\.(.*)[ \\)]");
QString selectedSuffix;
if(filter_re.exactMatch(selectedFilter))
{
selectedSuffix = filter_re.cap(1);
}
/* Add suffix if needed */
QFileInfo info(result);
if(!result.isEmpty())
{
if(info.suffix().isEmpty() && !selectedSuffix.isEmpty())
{
/* No suffix specified, add selected suffix */
if(!result.endsWith("."))
result.append(".");
result.append(selectedSuffix);
}
}
/* Return selected suffix if asked to */
if(selectedSuffixOut)
{
*selectedSuffixOut = selectedSuffix;
}
return result;
}
Qt::ConnectionType blockingGUIThreadConnection()
{
if(QThread::currentThread() != QCoreApplication::instance()->thread())
{
return Qt::BlockingQueuedConnection;
}
else
{
return Qt::DirectConnection;
}
}
bool checkPoint(const QPoint &p, const QWidget *w)
{
QWidget *atW = qApp->widgetAt(w->mapToGlobal(p));
if(!atW) return false;
return atW->topLevelWidget() == w;
}
bool isObscured(QWidget *w)
{
return !(checkPoint(QPoint(0, 0), w)
&& checkPoint(QPoint(w->width() - 1, 0), w)
&& checkPoint(QPoint(0, w->height() - 1), w)
&& checkPoint(QPoint(w->width() - 1, w->height() - 1), w)
&& checkPoint(QPoint(w->width()/2, w->height()/2), w));
}
void openDebugLogfile()
{
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
-#ifdef WIN32
+ /* Open debug.log with the associated application */
if (boost::filesystem::exists(pathDebug))
- /* Open debug.log with the associated application */
- ShellExecuteA((HWND)0, (LPCSTR)"open", (LPCSTR)pathDebug.string().c_str(), NULL, NULL, SW_SHOWNORMAL);
-#endif
+ QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(pathDebug.string())));
}
ToolTipToRichTextFilter::ToolTipToRichTextFilter(int size_threshold, QObject *parent) :
QObject(parent), size_threshold(size_threshold)
{
}
bool ToolTipToRichTextFilter::eventFilter(QObject *obj, QEvent *evt)
{
if(evt->type() == QEvent::ToolTipChange)
{
QWidget *widget = static_cast<QWidget*>(obj);
QString tooltip = widget->toolTip();
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>
tooltip = "<qt/>" + HtmlEscape(tooltip, true);
widget->setToolTip(tooltip);
return true;
}
}
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
HelpMessageBox::HelpMessageBox(QWidget *parent) :
QMessageBox(parent)
{
header = tr("Bitcoin-Qt") + " " + tr("version") + " " +
QString::fromStdString(FormatFullVersion()) + "\n\n" +
tr("Usage:") + "\n" +
" bitcoin-qt [" + tr("command-line 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.
setText(header + QString(QChar(0x2003)).repeated(50));
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
}
} // namespace GUIUtil
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index f7b06b5b9..7b40db076 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1,337 +1,331 @@
#include "rpcconsole.h"
#include "ui_rpcconsole.h"
#include "clientmodel.h"
#include "bitcoinrpc.h"
#include "guiutil.h"
#include <QTime>
#include <QTimer>
#include <QThread>
#include <QTextEdit>
#include <QKeyEvent>
#include <QUrl>
#include <QScrollBar>
#include <boost/tokenizer.hpp>
// TODO: make it possible to filter out categories (esp debug messages when implemented)
// TODO: receive errors and debug messages through ClientModel
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
{
Q_OBJECT
public slots:
void start();
void request(const QString &command);
signals:
void reply(int category, const QString &command);
};
#include "rpcconsole.moc"
void RPCExecutor::start()
{
// Nothing to do
}
void RPCExecutor::request(const QString &command)
{
// Parse shell-like command line into separate arguments
std::string strMethod;
std::vector<std::string> strParams;
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)
{
emit reply(RPCConsole::CMD_ERROR, QString("Parse error"));
return;
}
try {
std::string strPrint;
json_spirit::Value result = tableRPC.execute(strMethod, RPCConvertValues(strMethod, strParams));
// Format result reply
if (result.type() == json_spirit::null_type)
strPrint = "";
else if (result.type() == json_spirit::str_type)
strPrint = result.get_str();
else
strPrint = write_string(result, true);
emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint));
}
catch (json_spirit::Object& objError)
{
emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(write_string(json_spirit::Value(objError), false)));
}
catch (std::exception& e)
{
emit reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what()));
}
}
RPCConsole::RPCConsole(QWidget *parent) :
QDialog(parent),
ui(new Ui::RPCConsole),
historyPtr(0)
{
ui->setupUi(this);
#ifndef Q_WS_MAC
ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export"));
ui->showCLOptionsButton->setIcon(QIcon(":/icons/options"));
#endif
-#ifndef WIN32
- // Hide Debug logfile label and Open button for non Windows-OSes
- ui->labelDebugLogfile->setVisible(false);
- ui->openDebugLogfileButton->setVisible(false);
-#endif
-
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
startExecutor();
clear();
}
RPCConsole::~RPCConsole()
{
emit stopExecutor();
delete ui;
}
bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
{
if(obj == ui->lineEdit)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent *key = static_cast<QKeyEvent*>(event);
switch(key->key())
{
case Qt::Key_Up: browseHistory(-1); return true;
case Qt::Key_Down: browseHistory(1); return true;
}
}
}
return QDialog::eventFilter(obj, event);
}
void RPCConsole::setClientModel(ClientModel *model)
{
this->clientModel = model;
if(model)
{
// Subscribe to information, replies, messages, errors
connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
connect(model, SIGNAL(numBlocksChanged(int,int)), this, SLOT(setNumBlocks(int,int)));
// Provide initial values
ui->clientVersion->setText(model->formatFullVersion());
ui->clientName->setText(model->clientName());
ui->buildDate->setText(model->formatBuildDate());
ui->startupTime->setText(model->formatClientStartupTime());
setNumConnections(model->getNumConnections());
ui->isTestNet->setChecked(model->isTestNet());
setNumBlocks(model->getNumBlocks(), model->getNumBlocksOfPeers());
}
}
static QString categoryClass(int category)
{
switch(category)
{
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->lineEdit->clear();
ui->lineEdit->setFocus();
// 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>" +
tr("Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.") + "<br>" +
tr("Type <b>help</b> for an overview of available commands.")), true);
}
void RPCConsole::message(int category, const QString &message, bool html)
{
QTime time = QTime::currentTime();
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)
{
ui->numberOfConnections->setText(QString::number(count));
}
void RPCConsole::setNumBlocks(int count, int countOfPeers)
{
ui->numberOfBlocks->setText(QString::number(count));
ui->totalBlocks->setText(QString::number(countOfPeers));
if(clientModel)
{
// 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());
}
}
void RPCConsole::on_lineEdit_returnPressed()
{
QString cmd = ui->lineEdit->text();
ui->lineEdit->clear();
if(!cmd.isEmpty())
{
message(CMD_REQUEST, cmd);
emit cmdRequest(cmd);
// Truncate history from current position
history.erase(history.begin() + historyPtr, history.end());
// Append command to history
history.append(cmd);
// Enforce maximum history size
while(history.size() > CONSOLE_HISTORY)
history.removeFirst();
// Set pointer to end of history
historyPtr = history.size();
// Scroll console view to end
scrollToEnd();
}
}
void RPCConsole::browseHistory(int offset)
{
historyPtr += offset;
if(historyPtr < 0)
historyPtr = 0;
if(historyPtr > history.size())
historyPtr = history.size();
QString cmd;
if(historyPtr < history.size())
cmd = history.at(historyPtr);
ui->lineEdit->setText(cmd);
}
void RPCConsole::startExecutor()
{
QThread* thread = new QThread;
RPCExecutor *executor = new RPCExecutor();
executor->moveToThread(thread);
// Notify executor when thread started (in executor thread)
connect(thread, SIGNAL(started()), executor, SLOT(start()));
// Replies from executor object must go to this object
connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString)));
// Requests from this object must go to executor
connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString)));
// On stopExecutor signal
// - queue executor for deletion (in execution thread)
// - quit the Qt event loop in the execution thread
connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater()));
connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit()));
// Queue the thread for deletion (in this thread) when it is finished
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
// Default implementation of QThread::run() simply spins up an event loop in the thread,
// which is what we want.
thread->start();
}
void RPCConsole::on_tabWidget_currentChanged(int index)
{
if(ui->tabWidget->widget(index) == ui->tab_console)
{
ui->lineEdit->setFocus();
}
}
void RPCConsole::on_openDebugLogfileButton_clicked()
{
GUIUtil::openDebugLogfile();
}
void RPCConsole::scrollToEnd()
{
QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
void RPCConsole::on_showCLOptionsButton_clicked()
{
GUIUtil::HelpMessageBox help;
help.exec();
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Mar 2, 10:19 (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5181678
Default Alt Text
(24 KB)

Event Timeline