changeset 2532:ad97a6d42cea draft

Merge pull request #1275 from Diapolo/askpassphrasedialog remove string "TextLabel" from warningLabel, as this is unneeded and as ...
author Wladimir J. van der Laan <laanwj@gmail.com>
date Sun, 13 May 2012 00:40:26 -0700
parents f11b57bbe4ea (diff) 280cb2f61cc4 (current diff)
children a8f0dc00de7d
files
diffstat 10 files changed, 135 insertions(+), 139 deletions(-) [+]
line wrap: on
line diff
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -2447,7 +2447,7 @@
     strMessageRet = "";
 
     // Read status
-    int nProto;
+    int nProto = 0;
     int nStatus = ReadHTTPStatus(stream, nProto);
 
     // Read header
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -192,7 +192,7 @@
             "  -timeout=<n>     \t  "   + _("Specify connection timeout (in milliseconds)") + "\n" +
             "  -proxy=<ip:port> \t  "   + _("Connect through socks proxy") + "\n" +
             "  -socks=<n>       \t  "   + _("Select the version of socks proxy to use (4 or 5, 5 is default)") + "\n" +
-            "  -noproxy=<net>   \t  "   + _("Do not use proxy for connections to network net (ipv4 or ipv6)") + "\n" +
+            "  -noproxy=<net>   \t  "   + _("Do not use proxy for connections to network <net> (IPv4 or IPv6)") + "\n" +
             "  -dns             \t  "   + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" +
             "  -proxydns        \t  "   + _("Pass DNS requests to (SOCKS5) proxy") + "\n" +
             "  -port=<port>     \t\t  " + _("Listen for connections on <port> (default: 8333 or testnet: 18333)") + "\n" +
@@ -201,7 +201,7 @@
             "  -connect=<ip>    \t\t  " + _("Connect only to the specified node") + "\n" +
             "  -seednode=<ip>   \t\t  " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n" +
             "  -externalip=<ip> \t  "   + _("Specify your own public address") + "\n" +
-            "  -blocknet=<net>  \t  "   + _("Do not connect to addresses in network net (ipv4, ipv6)") + "\n" +
+            "  -blocknet=<net>  \t  "   + _("Do not connect to addresses in network <net> (IPv4 or IPv6)") + "\n" +
             "  -discover        \t  "   + _("Try to discover public IP address (default: 1)") + "\n" +
             "  -irc             \t  "   + _("Find peers using internet relay chat (default: 0)") + "\n" +
             "  -listen          \t  "   + _("Accept connections from outside (default: 1)") + "\n" +
@@ -611,14 +611,14 @@
         std::string strError;
         if (mapArgs.count("-bind")) {
             BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
-                fBound |= Bind(CService(strBind, GetDefaultPort(), false));
+                fBound |= Bind(CService(strBind, GetListenPort(), false));
             }
         } else {
             struct in_addr inaddr_any;
             inaddr_any.s_addr = INADDR_ANY;
-            fBound |= Bind(CService(inaddr_any, GetDefaultPort()));
+            fBound |= Bind(CService(inaddr_any, GetListenPort()));
 #ifdef USE_IPV6
-            fBound |= Bind(CService(in6addr_any, GetDefaultPort()));
+            fBound |= Bind(CService(in6addr_any, GetListenPort()));
 #endif
         }
         if (!fBound)
@@ -628,7 +628,7 @@
     if (mapArgs.count("-externalip"))
     {
         BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"])
-            AddLocal(CNetAddr(strAddr, fNameLookup), LOCAL_MANUAL);
+            AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
     }
 
     if (mapArgs.count("-paytxfee"))
--- a/src/makefile.unix
+++ b/src/makefile.unix
@@ -82,8 +82,11 @@
 
 
 DEBUGFLAGS=-g
-CXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \
-    $(DEBUGFLAGS) $(DEFS) $(HARDENING)
+
+# CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only
+# adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work.
+xCXXFLAGS=-O2 -pthread -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \
+    $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS)
 
 OBJS= \
     obj/version.o \
@@ -121,26 +124,26 @@
 DEFS += -DHAVE_BUILD_INFO
 
 obj/%.o: %.cpp
-	$(CXX) -c $(CXXFLAGS) -MMD -o $@ $<
+	$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
 	@cp $(@:%.o=%.d) $(@:%.o=%.P); \
 	  sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
 	      -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
 	  rm -f $(@:%.o=%.d)
 
 bitcoind: $(OBJS:obj/%=obj/%)
-	$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
+	$(CXX) $(xCXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
 
 TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
 
 obj-test/%.o: test/%.cpp
-	$(CXX) -c $(TESTDEFS) $(CXXFLAGS) -MMD -o $@ $<
+	$(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -MMD -o $@ $<
 	@cp $(@:%.o=%.d) $(@:%.o=%.P); \
 	  sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
 	      -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
 	  rm -f $(@:%.o=%.d)
 
 test_bitcoin: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
-	$(CXX) $(CXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-B$(LMODE) -lboost_unit_test_framework $(LDFLAGS) $(LIBS)
+	$(CXX) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-B$(LMODE) -lboost_unit_test_framework $(LDFLAGS) $(LIBS)
 
 clean:
 	-rm -f bitcoind test_bitcoin
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -38,6 +38,10 @@
 bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
 
 
+struct LocalServiceInfo {
+    int nScore;
+    int nPort;
+};
 
 //
 // Global state variables
@@ -46,7 +50,7 @@
 static bool fUseUPnP = false;
 uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
 static CCriticalSection cs_mapLocalHost;
-static map<CService, int> mapLocalHost;
+static map<CNetAddr, LocalServiceInfo> mapLocalHost;
 static bool vfReachable[NET_MAX] = {};
 static bool vfLimited[NET_MAX] = {};
 static CNode* pnodeLocalHost = NULL;
@@ -98,23 +102,23 @@
     if (fUseProxy || mapArgs.count("-connect") || fNoListen)
         return false;
 
-    int nBestCount = -1;
+    int nBestScore = -1;
     int nBestReachability = -1;
     {
         LOCK(cs_mapLocalHost);
-        for (map<CService, int>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++)
+        for (map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++)
         {
-            int nCount = (*it).second;
+            int nScore = (*it).second.nScore;
             int nReachability = (*it).first.GetReachabilityFrom(paddrPeer);
-            if (nReachability > nBestReachability || (nReachability == nBestReachability && nCount > nBestCount))
+            if (nReachability > nBestReachability || (nReachability == nBestReachability && nScore > nBestScore))
             {
-                addr = (*it).first;
+                addr = CService((*it).first, (*it).second.nPort);
                 nBestReachability = nReachability;
-                nBestCount = nCount;
+                nBestScore = nScore;
             }
         }
     }
-    return nBestCount >= 0;
+    return nBestScore >= 0;
 }
 
 // get best local address for a particular peer as a CAddress
@@ -211,7 +215,12 @@
 
     {
         LOCK(cs_mapLocalHost);
-        mapLocalHost[addr] = std::max(nScore, mapLocalHost[addr]) + (mapLocalHost.count(addr) ? 1 : 0);
+        bool fAlready = mapLocalHost.count(addr) > 0;
+        LocalServiceInfo &info = mapLocalHost[addr];
+        if (!fAlready || nScore >= info.nScore) {
+            info.nScore = nScore;
+            info.nPort = addr.GetPort() + (fAlready ? 1 : 0);
+        }
         enum Network net = addr.GetNetwork();
         vfReachable[net] = true;
         if (net == NET_IPV6) vfReachable[NET_IPV4] = true;
@@ -222,11 +231,9 @@
     return true;
 }
 
-bool AddLocal(const CNetAddr& addr, int nScore, int port)
+bool AddLocal(const CNetAddr &addr, int nScore)
 {
-    if (port == -1)
-        port = GetListenPort();
-    return AddLocal(CService(addr, port), nScore);
+    return AddLocal(CService(addr, GetListenPort()), nScore);
 }
 
 /** Make a particular network entirely off-limits (no automatic connects to it) */
@@ -249,7 +256,7 @@
         LOCK(cs_mapLocalHost);
         if (mapLocalHost.count(addr) == 0)
             return false;
-        mapLocalHost[addr]++;
+        mapLocalHost[addr].nScore++;
     }
 
     AdvertizeLocal();
@@ -1796,7 +1803,7 @@
                 struct sockaddr_in* s4 = (struct sockaddr_in*)(ifa->ifa_addr);
                 CNetAddr addr(s4->sin_addr);
                 if (AddLocal(addr, LOCAL_IF))
-                    printf("ipv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
+                    printf("IPv4 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
             }
 #ifdef USE_IPV6
             else if (ifa->ifa_addr->sa_family == AF_INET6)
@@ -1804,7 +1811,7 @@
                 struct sockaddr_in6* s6 = (struct sockaddr_in6*)(ifa->ifa_addr);
                 CNetAddr addr(s6->sin6_addr);
                 if (AddLocal(addr, LOCAL_IF))
-                    printf("ipv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
+                    printf("IPv6 %s: %s\n", ifa->ifa_name, addr.ToString().c_str());
             }
 #endif
         }
@@ -1887,8 +1894,9 @@
     fShutdown = true;
     nTransactionsUpdated++;
     int64 nStart = GetTime();
-    for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
-         semOutbound->post();
+    if (semOutbound)
+        for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
+            semOutbound->post();
     do
     {
         int nThreadsRunning = 0;
--- a/src/net.h
+++ b/src/net.h
@@ -38,6 +38,7 @@
 CNode* FindNode(const CService& ip);
 CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL, int64 nTimeout=0);
 void MapPort(bool fMapPort);
+unsigned short GetListenPort();
 bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string()));
 void StartNode(void* parg);
 bool StopNode();
@@ -58,7 +59,7 @@
 void SetLimited(enum Network net, bool fLimited = true);
 bool IsLimited(const CNetAddr& addr);
 bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
-bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE, int port = -1);
+bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
 bool SeenLocal(const CService& addr);
 bool IsLocal(const CService& addr);
 bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -11,6 +11,7 @@
 #endif
 
 #include "strlcpy.h"
+#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
 
 using namespace std;
 
@@ -27,6 +28,7 @@
 static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
 
 enum Network ParseNetwork(std::string net) {
+    boost::to_lower(net);
     if (net == "ipv4") return NET_IPV4;
     if (net == "ipv6") return NET_IPV6;
     if (net == "tor")  return NET_TOR;
@@ -464,12 +466,14 @@
     int port = portDefault;
 
     size_t colon = strDest.find_last_of(':');
-    char *endp = NULL;
-    int n = strtol(pszDest + colon + 1, &endp, 10);
-    if (endp && *endp == 0 && n >= 0) {
-        strDest = strDest.substr(0, colon);
-        if (n > 0 && n < 0x10000)
-            port = n;
+    if (colon != strDest.npos) {
+        char *endp = NULL;
+        int n = strtol(pszDest + colon + 1, &endp, 10);
+        if (endp && *endp == 0 && n >= 0) {
+            strDest = strDest.substr(0, colon);
+            if (n > 0 && n < 0x10000)
+                port = n;
+        }
     }
     if (strDest[0] == '[' && strDest[strDest.size()-1] == ']')
         strDest = strDest.substr(1, strDest.size()-2);
--- 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/forms/transactiondescdialog.ui
+++ b/src/qt/forms/transactiondescdialog.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>400</width>
-    <height>300</height>
+    <width>600</width>
+    <height>250</height>
    </rect>
   </property>
   <property name="windowTitle">
--- 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;