changeset 2520:4050cd49e4bc draft

Merge pull request #1269 from laanwj/2012_05_consoleimprovements UI console improvements
author Gregory Maxwell <gmaxwell@gmail.com>
date Sat, 12 May 2012 15:33:57 -0700
parents dda5f842648f (current diff) 9f54bce67f26 (diff)
children 9ceab6bfe212
files
diffstat 3 files changed, 79 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- 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">
@@ -327,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">
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="tabKeyNavigation" stdset="0">
           <bool>false</bool>
          </property>
-         <property name="selectionBehavior">
-          <enum>QAbstractItemView::SelectRows</enum>
-         </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>
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -10,6 +10,7 @@
 #include <QThread>
 #include <QTextEdit>
 #include <QKeyEvent>
+#include <QUrl>
 
 #include <boost/tokenizer.hpp>
 
@@ -19,6 +20,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 +55,26 @@
 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 +104,9 @@
 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 +117,6 @@
     // 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()));
 
@@ -159,68 +170,62 @@
     }
 }
 
-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)
@@ -298,24 +303,10 @@
     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();
     }
 }
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -37,15 +37,13 @@
 
 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();
 
 signals:
     // For RPC command executor
@@ -55,7 +53,6 @@
 private:
     Ui::RPCConsole *ui;
     ClientModel *clientModel;
-    bool firstLayout;
     QStringList history;
     int historyPtr;