Skip to content

Commit c6aa86a

Browse files
committed
Convert RPC console to QTextEdit instead of QTableView
* This allows copy/pasting whole or partial messages * Handle output more consistently in console * No more scrollbars-in-scrollbars: by setting per-pixel scrolling on the table, cells can have any height * Decorations for "request" and "reply" are changed to the txin and txout icons instead of colored squares
1 parent 97ec4e5 commit c6aa86a

File tree

3 files changed

+62
-89
lines changed

3 files changed

+62
-89
lines changed

src/qt/forms/rpcconsole.ui

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>706</width>
10-
<height>382</height>
10+
<height>446</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -327,30 +327,22 @@
327327
<number>3</number>
328328
</property>
329329
<item>
330-
<widget class="QTableWidget" name="messagesWidget">
330+
<widget class="QTextEdit" name="messagesWidget">
331331
<property name="minimumSize">
332332
<size>
333333
<width>0</width>
334334
<height>100</height>
335335
</size>
336336
</property>
337-
<property name="tabKeyNavigation">
338-
<bool>false</bool>
337+
<property name="readOnly">
338+
<bool>true</bool>
339339
</property>
340-
<property name="selectionBehavior">
341-
<enum>QAbstractItemView::SelectRows</enum>
340+
<property name="tabKeyNavigation" stdset="0">
341+
<bool>false</bool>
342342
</property>
343-
<property name="columnCount">
343+
<property name="columnCount" stdset="0">
344344
<number>2</number>
345345
</property>
346-
<attribute name="horizontalHeaderVisible">
347-
<bool>false</bool>
348-
</attribute>
349-
<attribute name="verticalHeaderVisible">
350-
<bool>false</bool>
351-
</attribute>
352-
<column/>
353-
<column/>
354346
</widget>
355347
</item>
356348
<item>

src/qt/rpcconsole.cpp

Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <QThread>
1111
#include <QTextEdit>
1212
#include <QKeyEvent>
13+
#include <QUrl>
1314

1415
#include <boost/tokenizer.hpp>
1516

@@ -19,6 +20,19 @@
1920
const int CONSOLE_SCROLLBACK = 50;
2021
const int CONSOLE_HISTORY = 50;
2122

23+
const QSize ICON_SIZE(24, 24);
24+
25+
const struct {
26+
const char *url;
27+
const char *source;
28+
} ICON_MAPPING[] = {
29+
{"cmd-request", ":/icons/tx_input"},
30+
{"cmd-reply", ":/icons/tx_output"},
31+
{"cmd-error", ":/icons/tx_output"},
32+
{"misc", ":/icons/tx_inout"},
33+
{NULL, NULL}
34+
};
35+
2236
/* Object for executing console RPC commands in a separate thread.
2337
*/
2438
class RPCExecutor: public QObject
@@ -83,12 +97,9 @@ void RPCExecutor::request(const QString &command)
8397
RPCConsole::RPCConsole(QWidget *parent) :
8498
QDialog(parent),
8599
ui(new Ui::RPCConsole),
86-
firstLayout(true),
87100
historyPtr(0)
88101
{
89102
ui->setupUi(this);
90-
ui->messagesWidget->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
91-
ui->messagesWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
92103

93104
#ifndef WIN32
94105
// Show Debug logfile label and Open button only for Windows
@@ -99,13 +110,6 @@ RPCConsole::RPCConsole(QWidget *parent) :
99110
// Install event filter for up and down arrow
100111
ui->lineEdit->installEventFilter(this);
101112

102-
// Add "Copy message" to context menu explicitly
103-
QAction *copyMessageAction = new QAction(tr("&Copy"), this);
104-
copyMessageAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_C));
105-
copyMessageAction->setShortcutContext(Qt::WidgetShortcut);
106-
connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage()));
107-
ui->messagesWidget->addAction(copyMessageAction);
108-
109113
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
110114
connect(ui->openDebugLogfileButton, SIGNAL(clicked()), this, SLOT(on_openDebugLogfileButton_clicked()));
111115

@@ -159,68 +163,62 @@ void RPCConsole::setClientModel(ClientModel *model)
159163
}
160164
}
161165

162-
static QColor categoryColor(int category)
166+
static QString categoryClass(int category)
163167
{
164168
switch(category)
165169
{
166-
case RPCConsole::MC_ERROR: return QColor(255,0,0); break;
167-
case RPCConsole::MC_DEBUG: return QColor(192,192,192); break;
168-
case RPCConsole::CMD_REQUEST: return QColor(128,128,128); break;
169-
case RPCConsole::CMD_REPLY: return QColor(128,255,128); break;
170-
case RPCConsole::CMD_ERROR: return QColor(255,128,128); break;
171-
default: return QColor(0,0,0);
170+
case RPCConsole::CMD_REQUEST: return "cmd-request"; break;
171+
case RPCConsole::CMD_REPLY: return "cmd-reply"; break;
172+
case RPCConsole::CMD_ERROR: return "cmd-error"; break;
173+
default: return "misc";
172174
}
173175
}
174176

175177
void RPCConsole::clear()
176178
{
177179
ui->messagesWidget->clear();
178-
ui->messagesWidget->setRowCount(0);
179180
ui->lineEdit->clear();
180181
ui->lineEdit->setFocus();
181182

182-
message(CMD_REPLY, tr("Welcome to the bitcoin RPC console.")+"\n"+
183-
tr("Use up and down arrows to navigate history, and Ctrl-L to clear screen.")+"\n"+
184-
tr("Type \"help\" for an overview of available commands."));
183+
// Add smoothly scaled icon images.
184+
// (when using width/height on an img, Qt uses nearest instead of linear interpolation)
185+
for(int i=0; ICON_MAPPING[i].url; ++i)
186+
{
187+
ui->messagesWidget->document()->addResource(
188+
QTextDocument::ImageResource,
189+
QUrl(ICON_MAPPING[i].url),
190+
QImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
191+
}
192+
193+
// Set default style sheet
194+
ui->messagesWidget->document()->setDefaultStyleSheet(
195+
"table { }"
196+
"td.time { color: #808080; padding-top: 3px; } "
197+
"td.message { font-family: Monospace; font-size: 12px; } "
198+
"td.cmd-request { color: #006060; } "
199+
"td.cmd-error { color: red; } "
200+
"b { color: #006060; } "
201+
);
202+
203+
message(CMD_REPLY, tr("Welcome to the Bitcoin RPC console.<br>"
204+
"Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.<br>"
205+
"Type <b>help</b> for an overview of available commands."), true);
185206
}
186207

187-
void RPCConsole::message(int category, const QString &message)
208+
void RPCConsole::message(int category, const QString &message, bool html)
188209
{
189-
// Add row to messages widget
190-
int row = ui->messagesWidget->rowCount();
191-
ui->messagesWidget->setRowCount(row+1);
192-
193210
QTime time = QTime::currentTime();
194-
QTableWidgetItem *newTime = new QTableWidgetItem(time.toString());
195-
newTime->setData(Qt::DecorationRole, categoryColor(category));
196-
newTime->setForeground(QColor(128,128,128));
197-
newTime->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); // make non-editable
198-
199-
int numLines = message.count("\n") + 1;
200-
// As Qt doesn't like very tall cells (they break scrolling) keep only short messages in
201-
// the cell text, longer messages trigger a display widget with scroll bar
202-
if(numLines < 5)
203-
{
204-
QTableWidgetItem *newItem = new QTableWidgetItem(message);
205-
newItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); // make non-editable
206-
if(category == CMD_ERROR) // Coloring error messages in red
207-
newItem->setForeground(QColor(255,16,16));
208-
ui->messagesWidget->setItem(row, 1, newItem);
209-
} else {
210-
QTextEdit *newWidget = new QTextEdit;
211-
newWidget->setText(message);
212-
newWidget->setMaximumHeight(100);
213-
newWidget->setReadOnly(true);
214-
ui->messagesWidget->setCellWidget(row, 1, newWidget);
215-
}
216-
217-
ui->messagesWidget->setItem(row, 0, newTime);
218-
ui->messagesWidget->resizeRowToContents(row);
219-
// Preserve only limited scrollback buffer
220-
while(ui->messagesWidget->rowCount() > CONSOLE_SCROLLBACK)
221-
ui->messagesWidget->removeRow(0);
222-
// Scroll to bottom after table is updated
223-
QTimer::singleShot(0, ui->messagesWidget, SLOT(scrollToBottom()));
211+
QString timeString = time.toString();
212+
QString out;
213+
out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
214+
out += "<td class=\"icon\" width=\"32\"><img src=\"" + categoryClass(category) + "\"></td>";
215+
out += "<td class=\"message " + categoryClass(category) + "\" valign=\"middle\">";
216+
if(html)
217+
out += message;
218+
else
219+
out += GUIUtil::HtmlEscape(message, true);
220+
out += "</td></tr></table>";
221+
ui->messagesWidget->append(out);
224222
}
225223

226224
void RPCConsole::setNumConnections(int count)
@@ -298,24 +296,10 @@ void RPCConsole::startExecutor()
298296
thread->start();
299297
}
300298

301-
void RPCConsole::copyMessage()
302-
{
303-
GUIUtil::copyEntryData(ui->messagesWidget, 1, Qt::EditRole);
304-
}
305-
306299
void RPCConsole::on_tabWidget_currentChanged(int index)
307300
{
308301
if(ui->tabWidget->widget(index) == ui->tab_console)
309302
{
310-
if(firstLayout)
311-
{
312-
// Work around QTableWidget issue:
313-
// Call resizeRowsToContents on first Layout request with widget visible,
314-
// to make sure multiline messages that were added before the console was shown
315-
// have the right height.
316-
firstLayout = false;
317-
ui->messagesWidget->resizeRowsToContents();
318-
}
319303
ui->lineEdit->setFocus();
320304
}
321305
}

src/qt/rpcconsole.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,13 @@ private slots:
3737

3838
public slots:
3939
void clear();
40-
void message(int category, const QString &message);
40+
void message(int category, const QString &message, bool html = false);
4141
/** Set number of connections shown in the UI */
4242
void setNumConnections(int count);
4343
/** Set number of blocks shown in the UI */
4444
void setNumBlocks(int count);
4545
/** Go forward or back in history */
4646
void browseHistory(int offset);
47-
/** Copy currently selected message to clipboard */
48-
void copyMessage();
4947

5048
signals:
5149
// For RPC command executor
@@ -55,7 +53,6 @@ public slots:
5553
private:
5654
Ui::RPCConsole *ui;
5755
ClientModel *clientModel;
58-
bool firstLayout;
5956
QStringList history;
6057
int historyPtr;
6158

0 commit comments

Comments
 (0)