10
10
#include < QThread>
11
11
#include < QTextEdit>
12
12
#include < QKeyEvent>
13
+ #include < QUrl>
13
14
14
15
#include < boost/tokenizer.hpp>
15
16
19
20
const int CONSOLE_SCROLLBACK = 50 ;
20
21
const int CONSOLE_HISTORY = 50 ;
21
22
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
+
22
36
/* Object for executing console RPC commands in a separate thread.
23
37
*/
24
38
class RPCExecutor : public QObject
@@ -41,19 +55,26 @@ void RPCExecutor::start()
41
55
void RPCExecutor::request (const QString &command)
42
56
{
43
57
// Parse shell-like command line into separate arguments
44
- boost::escaped_list_separator<char > els (' \\ ' ,' ' ,' \" ' );
45
- std::string strCommand = command.toStdString ();
46
- boost::tokenizer<boost::escaped_list_separator<char > > tok (strCommand, els);
47
-
48
58
std::string strMethod;
49
59
std::vector<std::string> strParams;
50
- int n = 0 ;
51
- for (boost::tokenizer<boost::escaped_list_separator<char > >::iterator beg=tok.begin (); beg!=tok.end ();++beg,++n)
60
+ try {
61
+ boost::escaped_list_separator<char > els (' \\ ' ,' ' ,' \" ' );
62
+ std::string strCommand = command.toStdString ();
63
+ boost::tokenizer<boost::escaped_list_separator<char > > tok (strCommand, els);
64
+
65
+ int n = 0 ;
66
+ for (boost::tokenizer<boost::escaped_list_separator<char > >::iterator beg=tok.begin (); beg!=tok.end ();++beg,++n)
67
+ {
68
+ if (n == 0 ) // First parameter is the command
69
+ strMethod = *beg;
70
+ else
71
+ strParams.push_back (*beg);
72
+ }
73
+ }
74
+ catch (boost::escaped_list_error &e)
52
75
{
53
- if (n == 0 ) // First parameter is the command
54
- strMethod = *beg;
55
- else
56
- strParams.push_back (*beg);
76
+ emit reply (RPCConsole::CMD_ERROR, QString (" Parse error" ));
77
+ return ;
57
78
}
58
79
59
80
try {
@@ -83,12 +104,9 @@ void RPCExecutor::request(const QString &command)
83
104
RPCConsole::RPCConsole (QWidget *parent) :
84
105
QDialog(parent),
85
106
ui(new Ui::RPCConsole),
86
- firstLayout(true ),
87
107
historyPtr(0 )
88
108
{
89
109
ui->setupUi (this );
90
- ui->messagesWidget ->horizontalHeader ()->setResizeMode (1 , QHeaderView::Stretch);
91
- ui->messagesWidget ->setContextMenuPolicy (Qt::ActionsContextMenu);
92
110
93
111
#ifndef WIN32
94
112
// Show Debug logfile label and Open button only for Windows
@@ -99,13 +117,6 @@ RPCConsole::RPCConsole(QWidget *parent) :
99
117
// Install event filter for up and down arrow
100
118
ui->lineEdit ->installEventFilter (this );
101
119
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
-
109
120
connect (ui->clearButton , SIGNAL (clicked ()), this , SLOT (clear ()));
110
121
connect (ui->openDebugLogfileButton , SIGNAL (clicked ()), this , SLOT (on_openDebugLogfileButton_clicked ()));
111
122
@@ -159,68 +170,62 @@ void RPCConsole::setClientModel(ClientModel *model)
159
170
}
160
171
}
161
172
162
- static QColor categoryColor (int category)
173
+ static QString categoryClass (int category)
163
174
{
164
175
switch (category)
165
176
{
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 );
177
+ case RPCConsole::CMD_REQUEST: return " cmd-request" ; break ;
178
+ case RPCConsole::CMD_REPLY: return " cmd-reply" ; break ;
179
+ case RPCConsole::CMD_ERROR: return " cmd-error" ; break ;
180
+ default : return " misc" ;
172
181
}
173
182
}
174
183
175
184
void RPCConsole::clear ()
176
185
{
177
186
ui->messagesWidget ->clear ();
178
- ui->messagesWidget ->setRowCount (0 );
179
187
ui->lineEdit ->clear ();
180
188
ui->lineEdit ->setFocus ();
181
189
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." ));
190
+ // Add smoothly scaled icon images.
191
+ // (when using width/height on an img, Qt uses nearest instead of linear interpolation)
192
+ for (int i=0 ; ICON_MAPPING[i].url ; ++i)
193
+ {
194
+ ui->messagesWidget ->document ()->addResource (
195
+ QTextDocument::ImageResource,
196
+ QUrl (ICON_MAPPING[i].url ),
197
+ QImage (ICON_MAPPING[i].source ).scaled (ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
198
+ }
199
+
200
+ // Set default style sheet
201
+ ui->messagesWidget ->document ()->setDefaultStyleSheet (
202
+ " table { }"
203
+ " td.time { color: #808080; padding-top: 3px; } "
204
+ " td.message { font-family: Monospace; font-size: 12px; } "
205
+ " td.cmd-request { color: #006060; } "
206
+ " td.cmd-error { color: red; } "
207
+ " b { color: #006060; } "
208
+ );
209
+
210
+ message (CMD_REPLY, tr (" Welcome to the Bitcoin RPC console.<br>"
211
+ " Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.<br>"
212
+ " Type <b>help</b> for an overview of available commands." ), true );
185
213
}
186
214
187
- void RPCConsole::message (int category, const QString &message)
215
+ void RPCConsole::message (int category, const QString &message, bool html )
188
216
{
189
- // Add row to messages widget
190
- int row = ui->messagesWidget ->rowCount ();
191
- ui->messagesWidget ->setRowCount (row+1 );
192
-
193
217
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 ()));
218
+ QString timeString = time.toString ();
219
+ QString out;
220
+ out += " <table><tr><td class=\" time\" width=\" 65\" >" + timeString + " </td>" ;
221
+ out += " <td class=\" icon\" width=\" 32\" ><img src=\" " + categoryClass (category) + " \" ></td>" ;
222
+ out += " <td class=\" message " + categoryClass (category) + " \" valign=\" middle\" >" ;
223
+ if (html)
224
+ out += message;
225
+ else
226
+ out += GUIUtil::HtmlEscape (message, true );
227
+ out += " </td></tr></table>" ;
228
+ ui->messagesWidget ->append (out);
224
229
}
225
230
226
231
void RPCConsole::setNumConnections (int count)
@@ -298,24 +303,10 @@ void RPCConsole::startExecutor()
298
303
thread->start ();
299
304
}
300
305
301
- void RPCConsole::copyMessage ()
302
- {
303
- GUIUtil::copyEntryData (ui->messagesWidget , 1 , Qt::EditRole);
304
- }
305
-
306
306
void RPCConsole::on_tabWidget_currentChanged (int index)
307
307
{
308
308
if (ui->tabWidget ->widget (index) == ui->tab_console )
309
309
{
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
- }
319
310
ui->lineEdit ->setFocus ();
320
311
}
321
312
}
0 commit comments