@ -10,6 +10,7 @@
# include <QThread>
# include <QThread>
# include <QTextEdit>
# include <QTextEdit>
# include <QKeyEvent>
# include <QKeyEvent>
# include <QUrl>
# include <boost/tokenizer.hpp>
# include <boost/tokenizer.hpp>
@ -19,6 +20,19 @@
const int CONSOLE_SCROLLBACK = 50 ;
const int CONSOLE_SCROLLBACK = 50 ;
const int CONSOLE_HISTORY = 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.
/* Object for executing console RPC commands in a separate thread.
*/
*/
class RPCExecutor : public QObject
class RPCExecutor : public QObject
@ -83,12 +97,9 @@ void RPCExecutor::request(const QString &command)
RPCConsole : : RPCConsole ( QWidget * parent ) :
RPCConsole : : RPCConsole ( QWidget * parent ) :
QDialog ( parent ) ,
QDialog ( parent ) ,
ui ( new Ui : : RPCConsole ) ,
ui ( new Ui : : RPCConsole ) ,
firstLayout ( true ) ,
historyPtr ( 0 )
historyPtr ( 0 )
{
{
ui - > setupUi ( this ) ;
ui - > setupUi ( this ) ;
ui - > messagesWidget - > horizontalHeader ( ) - > setResizeMode ( 1 , QHeaderView : : Stretch ) ;
ui - > messagesWidget - > setContextMenuPolicy ( Qt : : ActionsContextMenu ) ;
# ifndef WIN32
# ifndef WIN32
// Show Debug logfile label and Open button only for Windows
// Show Debug logfile label and Open button only for Windows
@ -99,13 +110,6 @@ RPCConsole::RPCConsole(QWidget *parent) :
// Install event filter for up and down arrow
// Install event filter for up and down arrow
ui - > lineEdit - > installEventFilter ( this ) ;
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 - > clearButton , SIGNAL ( clicked ( ) ) , this , SLOT ( clear ( ) ) ) ;
connect ( ui - > openDebugLogfileButton , SIGNAL ( clicked ( ) ) , this , SLOT ( on_openDebugLogfileButton_clicked ( ) ) ) ;
connect ( ui - > openDebugLogfileButton , SIGNAL ( clicked ( ) ) , this , SLOT ( on_openDebugLogfileButton_clicked ( ) ) ) ;
@ -159,68 +163,62 @@ void RPCConsole::setClientModel(ClientModel *model)
}
}
}
}
static Q Color categoryColor ( int category )
static Q String categoryClass ( int category )
{
{
switch ( category )
switch ( category )
{
{
case RPCConsole : : MC_ERROR : return QColor ( 255 , 0 , 0 ) ; break ;
case RPCConsole : : CMD_REQUEST : return " cmd-request " ; break ;
case RPCConsole : : MC_DEBUG : return QColor ( 192 , 192 , 192 ) ; break ;
case RPCConsole : : CMD_REPLY : return " cmd-reply " ; break ;
case RPCConsole : : CMD_REQUEST : return QColor ( 128 , 128 , 128 ) ; break ;
case RPCConsole : : CMD_ERROR : return " cmd-error " ; break ;
case RPCConsole : : CMD_REPLY : return QColor ( 128 , 255 , 128 ) ; break ;
default : return " misc " ;
case RPCConsole : : CMD_ERROR : return QColor ( 255 , 128 , 128 ) ; break ;
default : return QColor ( 0 , 0 , 0 ) ;
}
}
}
}
void RPCConsole : : clear ( )
void RPCConsole : : clear ( )
{
{
ui - > messagesWidget - > clear ( ) ;
ui - > messagesWidget - > clear ( ) ;
ui - > messagesWidget - > setRowCount ( 0 ) ;
ui - > lineEdit - > clear ( ) ;
ui - > lineEdit - > clear ( ) ;
ui - > lineEdit - > setFocus ( ) ;
ui - > lineEdit - > setFocus ( ) ;
message ( CMD_REPLY , tr ( " Welcome to the bitcoin RPC console. " ) + " \n " +
// Add smoothly scaled icon images.
tr ( " Use up and down arrows to navigate history, and Ctrl-L to clear screen. " ) + " \n " +
// (when using width/height on an img, Qt uses nearest instead of linear interpolation)
tr ( " Type \" help \" for an overview of available commands. " ) ) ;
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 ( ) ;
QTime time = QTime : : currentTime ( ) ;
QTableWidgetItem * newTime = new QTableWidgetItem ( time . toString ( ) ) ;
QString timeString = time . toString ( ) ;
newTime - > setData ( Qt : : DecorationRole , categoryColor ( category ) ) ;
QString out ;
newTime - > setForeground ( QColor ( 128 , 128 , 128 ) ) ;
out + = " <table><tr><td class= \" time \" width= \" 65 \" > " + timeString + " </td> " ;
newTime - > setFlags ( Qt : : ItemIsSelectable | Qt : : ItemIsEnabled ) ; // make non-editable
out + = " <td class= \" icon \" width= \" 32 \" ><img src= \" " + categoryClass ( category ) + " \" ></td> " ;
out + = " <td class= \" message " + categoryClass ( category ) + " \" valign= \" middle \" > " ;
int numLines = message . count ( " \n " ) + 1 ;
if ( html )
// As Qt doesn't like very tall cells (they break scrolling) keep only short messages in
out + = message ;
// the cell text, longer messages trigger a display widget with scroll bar
else
if ( numLines < 5 )
out + = GUIUtil : : HtmlEscape ( message , true ) ;
{
out + = " </td></tr></table> " ;
QTableWidgetItem * newItem = new QTableWidgetItem ( message ) ;
ui - > messagesWidget - > append ( out ) ;
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 ( ) ) ) ;
}
}
void RPCConsole : : setNumConnections ( int count )
void RPCConsole : : setNumConnections ( int count )
@ -298,24 +296,10 @@ void RPCConsole::startExecutor()
thread - > start ( ) ;
thread - > start ( ) ;
}
}
void RPCConsole : : copyMessage ( )
{
GUIUtil : : copyEntryData ( ui - > messagesWidget , 1 , Qt : : EditRole ) ;
}
void RPCConsole : : on_tabWidget_currentChanged ( int index )
void RPCConsole : : on_tabWidget_currentChanged ( int index )
{
{
if ( ui - > tabWidget - > widget ( index ) = = ui - > tab_console )
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 ( ) ;
ui - > lineEdit - > setFocus ( ) ;
}
}
}
}