]> Git Repo - VerusCoin.git/blob - src/qt/rpcconsole.cpp
print the caught error instead of raising an error
[VerusCoin.git] / src / qt / rpcconsole.cpp
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include "rpcconsole.h"
6 #include "ui_rpcconsole.h"
7
8 #include "clientmodel.h"
9 #include "guiutil.h"
10 #include "peertablemodel.h"
11
12 #include "main.h"
13 #include "chainparams.h"
14 #include "rpcserver.h"
15 #include "rpcclient.h"
16 #include "util.h"
17
18 #include "json/json_spirit_value.h"
19
20 #include <openssl/crypto.h>
21
22 #ifdef ENABLE_WALLET
23 #include <db_cxx.h>
24 #endif
25
26 #include <QKeyEvent>
27 #include <QScrollBar>
28 #include <QThread>
29 #include <QTime>
30
31 #if QT_VERSION < 0x050000
32 #include <QUrl>
33 #endif
34
35 // TODO: add a scrollback limit, as there is currently none
36 // TODO: make it possible to filter out categories (esp debug messages when implemented)
37 // TODO: receive errors and debug messages through ClientModel
38
39 const int CONSOLE_HISTORY = 50;
40 const QSize ICON_SIZE(24, 24);
41
42 const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
43
44 const struct {
45     const char *url;
46     const char *source;
47 } ICON_MAPPING[] = {
48     {"cmd-request", ":/icons/tx_input"},
49     {"cmd-reply", ":/icons/tx_output"},
50     {"cmd-error", ":/icons/tx_output"},
51     {"misc", ":/icons/tx_inout"},
52     {NULL, NULL}
53 };
54
55 /* Object for executing console RPC commands in a separate thread.
56 */
57 class RPCExecutor : public QObject
58 {
59     Q_OBJECT
60
61 public slots:
62     void request(const QString &command);
63
64 signals:
65     void reply(int category, const QString &command);
66 };
67
68 #include "rpcconsole.moc"
69
70 /**
71  * Split shell command line into a list of arguments. Aims to emulate \c bash and friends.
72  *
73  * - Arguments are delimited with whitespace
74  * - Extra whitespace at the beginning and end and between arguments will be ignored
75  * - Text can be "double" or 'single' quoted
76  * - The backslash \c \ is used as escape character
77  *   - Outside quotes, any character can be escaped
78  *   - Within double quotes, only escape \c " and backslashes before a \c " or another backslash
79  *   - Within single quotes, no escaping is possible and no special interpretation takes place
80  *
81  * @param[out]   args        Parsed arguments will be appended to this list
82  * @param[in]    strCommand  Command line to split
83  */
84 bool parseCommandLine(std::vector<std::string> &args, const std::string &strCommand)
85 {
86     enum CmdParseState
87     {
88         STATE_EATING_SPACES,
89         STATE_ARGUMENT,
90         STATE_SINGLEQUOTED,
91         STATE_DOUBLEQUOTED,
92         STATE_ESCAPE_OUTER,
93         STATE_ESCAPE_DOUBLEQUOTED
94     } state = STATE_EATING_SPACES;
95     std::string curarg;
96     foreach(char ch, strCommand)
97     {
98         switch(state)
99         {
100         case STATE_ARGUMENT: // In or after argument
101         case STATE_EATING_SPACES: // Handle runs of whitespace
102             switch(ch)
103             {
104             case '"': state = STATE_DOUBLEQUOTED; break;
105             case '\'': state = STATE_SINGLEQUOTED; break;
106             case '\\': state = STATE_ESCAPE_OUTER; break;
107             case ' ': case '\n': case '\t':
108                 if(state == STATE_ARGUMENT) // Space ends argument
109                 {
110                     args.push_back(curarg);
111                     curarg.clear();
112                 }
113                 state = STATE_EATING_SPACES;
114                 break;
115             default: curarg += ch; state = STATE_ARGUMENT;
116             }
117             break;
118         case STATE_SINGLEQUOTED: // Single-quoted string
119             switch(ch)
120             {
121             case '\'': state = STATE_ARGUMENT; break;
122             default: curarg += ch;
123             }
124             break;
125         case STATE_DOUBLEQUOTED: // Double-quoted string
126             switch(ch)
127             {
128             case '"': state = STATE_ARGUMENT; break;
129             case '\\': state = STATE_ESCAPE_DOUBLEQUOTED; break;
130             default: curarg += ch;
131             }
132             break;
133         case STATE_ESCAPE_OUTER: // '\' outside quotes
134             curarg += ch; state = STATE_ARGUMENT;
135             break;
136         case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
137             if(ch != '"' && ch != '\\') curarg += '\\'; // keep '\' for everything but the quote and '\' itself
138             curarg += ch; state = STATE_DOUBLEQUOTED;
139             break;
140         }
141     }
142     switch(state) // final state
143     {
144     case STATE_EATING_SPACES:
145         return true;
146     case STATE_ARGUMENT:
147         args.push_back(curarg);
148         return true;
149     default: // ERROR to end in one of the other states
150         return false;
151     }
152 }
153
154 void RPCExecutor::request(const QString &command)
155 {
156     std::vector<std::string> args;
157     if(!parseCommandLine(args, command.toStdString()))
158     {
159         emit reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \""));
160         return;
161     }
162     if(args.empty())
163         return; // Nothing to do
164     try
165     {
166         std::string strPrint;
167         // Convert argument list to JSON objects in method-dependent way,
168         // and pass it along with the method name to the dispatcher.
169         json_spirit::Value result = tableRPC.execute(
170             args[0],
171             RPCConvertValues(args[0], std::vector<std::string>(args.begin() + 1, args.end())));
172
173         // Format result reply
174         if (result.type() == json_spirit::null_type)
175             strPrint = "";
176         else if (result.type() == json_spirit::str_type)
177             strPrint = result.get_str();
178         else
179             strPrint = write_string(result, true);
180
181         emit reply(RPCConsole::CMD_REPLY, QString::fromStdString(strPrint));
182     }
183     catch (json_spirit::Object& objError)
184     {
185         try // Nice formatting for standard-format error
186         {
187             int code = find_value(objError, "code").get_int();
188             std::string message = find_value(objError, "message").get_str();
189             emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(message) + " (code " + QString::number(code) + ")");
190         }
191         catch(std::runtime_error &) // raised when converting to invalid type, i.e. missing code or message
192         {   // Show raw JSON object
193             emit reply(RPCConsole::CMD_ERROR, QString::fromStdString(write_string(json_spirit::Value(objError), false)));
194         }
195     }
196     catch (std::exception& e)
197     {
198         emit reply(RPCConsole::CMD_ERROR, QString("Error: ") + QString::fromStdString(e.what()));
199     }
200 }
201
202 RPCConsole::RPCConsole(QWidget *parent) :
203     QDialog(parent),
204     ui(new Ui::RPCConsole),
205     clientModel(0),
206     historyPtr(0),
207     cachedNodeid(-1)
208 {
209     ui->setupUi(this);
210     GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
211
212 #ifndef Q_OS_MAC
213     ui->openDebugLogfileButton->setIcon(QIcon(":/icons/export"));
214 #endif
215
216     // Install event filter for up and down arrow
217     ui->lineEdit->installEventFilter(this);
218     ui->messagesWidget->installEventFilter(this);
219
220     connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
221     connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph, SLOT(clear()));
222
223     // set library version labels
224     ui->openSSLVersion->setText(SSLeay_version(SSLEAY_VERSION));
225 #ifdef ENABLE_WALLET
226     ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0));
227 #else
228     ui->label_berkeleyDBVersion->hide();
229     ui->berkeleyDBVersion->hide();
230 #endif
231
232     startExecutor();
233     setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
234
235     ui->detailWidget->hide();
236     ui->peerHeading->setText(tr("Select a peer to view detailed information."));
237
238     clear();
239 }
240
241 RPCConsole::~RPCConsole()
242 {
243     GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
244     emit stopExecutor();
245     delete ui;
246 }
247
248 bool RPCConsole::eventFilter(QObject* obj, QEvent *event)
249 {
250     if(event->type() == QEvent::KeyPress) // Special key handling
251     {
252         QKeyEvent *keyevt = static_cast<QKeyEvent*>(event);
253         int key = keyevt->key();
254         Qt::KeyboardModifiers mod = keyevt->modifiers();
255         switch(key)
256         {
257         case Qt::Key_Up: if(obj == ui->lineEdit) { browseHistory(-1); return true; } break;
258         case Qt::Key_Down: if(obj == ui->lineEdit) { browseHistory(1); return true; } break;
259         case Qt::Key_PageUp: /* pass paging keys to messages widget */
260         case Qt::Key_PageDown:
261             if(obj == ui->lineEdit)
262             {
263                 QApplication::postEvent(ui->messagesWidget, new QKeyEvent(*keyevt));
264                 return true;
265             }
266             break;
267         default:
268             // Typing in messages widget brings focus to line edit, and redirects key there
269             // Exclude most combinations and keys that emit no text, except paste shortcuts
270             if(obj == ui->messagesWidget && (
271                   (!mod && !keyevt->text().isEmpty() && key != Qt::Key_Tab) ||
272                   ((mod & Qt::ControlModifier) && key == Qt::Key_V) ||
273                   ((mod & Qt::ShiftModifier) && key == Qt::Key_Insert)))
274             {
275                 ui->lineEdit->setFocus();
276                 QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt));
277                 return true;
278             }
279         }
280     }
281     return QDialog::eventFilter(obj, event);
282 }
283
284 void RPCConsole::setClientModel(ClientModel *model)
285 {
286     clientModel = model;
287     ui->trafficGraph->setClientModel(model);
288     if(model)
289     {
290         // Keep up to date with client
291         setNumConnections(model->getNumConnections());
292         connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
293
294         setNumBlocks(model->getNumBlocks());
295         connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int)));
296
297         updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
298         connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
299
300         // set up peer table
301         ui->peerWidget->setModel(model->getPeerTableModel());
302         ui->peerWidget->verticalHeader()->hide();
303         ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
304         ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
305         ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection);
306         ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
307         ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
308         ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
309
310         // connect the peerWidget selection model to our peerSelected() handler
311         connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
312              this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
313         connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
314
315         // Provide initial values
316         ui->clientVersion->setText(model->formatFullVersion());
317         ui->clientName->setText(model->clientName());
318         ui->buildDate->setText(model->formatBuildDate());
319         ui->startupTime->setText(model->formatClientStartupTime());
320
321         ui->networkName->setText(QString::fromStdString(Params().NetworkIDString()));
322     }
323 }
324
325 static QString categoryClass(int category)
326 {
327     switch(category)
328     {
329     case RPCConsole::CMD_REQUEST:  return "cmd-request"; break;
330     case RPCConsole::CMD_REPLY:    return "cmd-reply"; break;
331     case RPCConsole::CMD_ERROR:    return "cmd-error"; break;
332     default:                       return "misc";
333     }
334 }
335
336 void RPCConsole::clear()
337 {
338     ui->messagesWidget->clear();
339     history.clear();
340     historyPtr = 0;
341     ui->lineEdit->clear();
342     ui->lineEdit->setFocus();
343
344     // Add smoothly scaled icon images.
345     // (when using width/height on an img, Qt uses nearest instead of linear interpolation)
346     for(int i=0; ICON_MAPPING[i].url; ++i)
347     {
348         ui->messagesWidget->document()->addResource(
349                     QTextDocument::ImageResource,
350                     QUrl(ICON_MAPPING[i].url),
351                     QImage(ICON_MAPPING[i].source).scaled(ICON_SIZE, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
352     }
353
354     // Set default style sheet
355     ui->messagesWidget->document()->setDefaultStyleSheet(
356                 "table { }"
357                 "td.time { color: #808080; padding-top: 3px; } "
358                 "td.message { font-family: monospace; font-size: 12px; } " // Todo: Remove fixed font-size
359                 "td.cmd-request { color: #006060; } "
360                 "td.cmd-error { color: red; } "
361                 "b { color: #006060; } "
362                 );
363
364     message(CMD_REPLY, (tr("Welcome to the Bitcoin RPC console.") + "<br>" +
365                         tr("Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen.") + "<br>" +
366                         tr("Type <b>help</b> for an overview of available commands.")), true);
367 }
368
369 void RPCConsole::reject()
370 {
371     // Ignore escape keypress if this is not a seperate window
372     if(windowType() != Qt::Widget)
373         QDialog::reject();
374 }
375
376 void RPCConsole::message(int category, const QString &message, bool html)
377 {
378     QTime time = QTime::currentTime();
379     QString timeString = time.toString();
380     QString out;
381     out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
382     out += "<td class=\"icon\" width=\"32\"><img src=\"" + categoryClass(category) + "\"></td>";
383     out += "<td class=\"message " + categoryClass(category) + "\" valign=\"middle\">";
384     if(html)
385         out += message;
386     else
387         out += GUIUtil::HtmlEscape(message, true);
388     out += "</td></tr></table>";
389     ui->messagesWidget->append(out);
390 }
391
392 void RPCConsole::setNumConnections(int count)
393 {
394     if (!clientModel)
395         return;
396
397     QString connections = QString::number(count) + " (";
398     connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
399     connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
400
401     ui->numberOfConnections->setText(connections);
402 }
403
404 void RPCConsole::setNumBlocks(int count)
405 {
406     ui->numberOfBlocks->setText(QString::number(count));
407     if(clientModel)
408         ui->lastBlockTime->setText(clientModel->getLastBlockDate().toString());
409 }
410
411 void RPCConsole::on_lineEdit_returnPressed()
412 {
413     QString cmd = ui->lineEdit->text();
414     ui->lineEdit->clear();
415
416     if(!cmd.isEmpty())
417     {
418         message(CMD_REQUEST, cmd);
419         emit cmdRequest(cmd);
420         // Remove command, if already in history
421         history.removeOne(cmd);
422         // Append command to history
423         history.append(cmd);
424         // Enforce maximum history size
425         while(history.size() > CONSOLE_HISTORY)
426             history.removeFirst();
427         // Set pointer to end of history
428         historyPtr = history.size();
429         // Scroll console view to end
430         scrollToEnd();
431     }
432 }
433
434 void RPCConsole::browseHistory(int offset)
435 {
436     historyPtr += offset;
437     if(historyPtr < 0)
438         historyPtr = 0;
439     if(historyPtr > history.size())
440         historyPtr = history.size();
441     QString cmd;
442     if(historyPtr < history.size())
443         cmd = history.at(historyPtr);
444     ui->lineEdit->setText(cmd);
445 }
446
447 void RPCConsole::startExecutor()
448 {
449     QThread *thread = new QThread;
450     RPCExecutor *executor = new RPCExecutor();
451     executor->moveToThread(thread);
452
453     // Replies from executor object must go to this object
454     connect(executor, SIGNAL(reply(int,QString)), this, SLOT(message(int,QString)));
455     // Requests from this object must go to executor
456     connect(this, SIGNAL(cmdRequest(QString)), executor, SLOT(request(QString)));
457
458     // On stopExecutor signal
459     // - queue executor for deletion (in execution thread)
460     // - quit the Qt event loop in the execution thread
461     connect(this, SIGNAL(stopExecutor()), executor, SLOT(deleteLater()));
462     connect(this, SIGNAL(stopExecutor()), thread, SLOT(quit()));
463     // Queue the thread for deletion (in this thread) when it is finished
464     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
465
466     // Default implementation of QThread::run() simply spins up an event loop in the thread,
467     // which is what we want.
468     thread->start();
469 }
470
471 void RPCConsole::on_tabWidget_currentChanged(int index)
472 {
473     if(ui->tabWidget->widget(index) == ui->tab_console)
474     {
475         ui->lineEdit->setFocus();
476     }
477 }
478
479 void RPCConsole::on_openDebugLogfileButton_clicked()
480 {
481     GUIUtil::openDebugLogfile();
482 }
483
484 void RPCConsole::scrollToEnd()
485 {
486     QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
487     scrollbar->setValue(scrollbar->maximum());
488 }
489
490 void RPCConsole::on_sldGraphRange_valueChanged(int value)
491 {
492     const int multiplier = 5; // each position on the slider represents 5 min
493     int mins = value * multiplier;
494     setTrafficGraphRange(mins);
495 }
496
497 QString RPCConsole::FormatBytes(quint64 bytes)
498 {
499     if(bytes < 1024)
500         return QString(tr("%1 B")).arg(bytes);
501     if(bytes < 1024 * 1024)
502         return QString(tr("%1 KB")).arg(bytes / 1024);
503     if(bytes < 1024 * 1024 * 1024)
504         return QString(tr("%1 MB")).arg(bytes / 1024 / 1024);
505
506     return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
507 }
508
509 void RPCConsole::setTrafficGraphRange(int mins)
510 {
511     ui->trafficGraph->setGraphRangeMins(mins);
512     ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
513 }
514
515 void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
516 {
517     ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
518     ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
519 }
520
521 void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected)
522 {
523     Q_UNUSED(deselected);
524
525     if (!clientModel || selected.indexes().isEmpty())
526         return;
527
528     const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
529     if (stats)
530         updateNodeDetail(stats);
531 }
532
533 void RPCConsole::peerLayoutChanged()
534 {
535     if (!clientModel)
536         return;
537
538     const CNodeCombinedStats *stats = NULL;
539     bool fUnselect = false;
540     bool fReselect = false;
541
542     if (cachedNodeid == -1) // no node selected yet
543         return;
544
545     // find the currently selected row
546     int selectedRow;
547     QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes();
548     if (selectedModelIndex.isEmpty())
549         selectedRow = -1;
550     else
551         selectedRow = selectedModelIndex.first().row();
552
553     // check if our detail node has a row in the table (it may not necessarily
554     // be at selectedRow since its position can change after a layout change)
555     int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid);
556
557     if (detailNodeRow < 0)
558     {
559         // detail node dissapeared from table (node disconnected)
560         fUnselect = true;
561         cachedNodeid = -1;
562         ui->detailWidget->hide();
563         ui->peerHeading->setText(tr("Select a peer to view detailed information."));
564     }
565     else
566     {
567         if (detailNodeRow != selectedRow)
568         {
569             // detail node moved position
570             fUnselect = true;
571             fReselect = true;
572         }
573
574         // get fresh stats on the detail node.
575         stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
576     }
577
578     if (fUnselect && selectedRow >= 0)
579     {
580         ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()),
581             QItemSelectionModel::Deselect);
582     }
583
584     if (fReselect)
585     {
586         ui->peerWidget->selectRow(detailNodeRow);
587     }
588
589     if (stats)
590         updateNodeDetail(stats);
591 }
592
593 void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
594 {
595     // Update cached nodeid
596     cachedNodeid = stats->nodeStats.nodeid;
597
598     // update the detail ui with latest node information
599     QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName));
600     if (!stats->nodeStats.addrLocal.empty())
601         peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
602     ui->peerHeading->setText(peerAddrDetails);
603     ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
604     ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastSend) : tr("never"));
605     ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastRecv) : tr("never"));
606     ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes));
607     ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes));
608     ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected));
609     ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
610     ui->peerVersion->setText(QString("%1").arg(stats->nodeStats.nVersion));
611     ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
612     ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
613     ui->peerHeight->setText(QString("%1").arg(stats->nodeStats.nStartingHeight));
614
615     // This check fails for example if the lock was busy and
616     // nodeStateStats couldn't be fetched.
617     if (stats->fNodeStateStatsAvailable) {
618         // Ban score is init to 0
619         ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
620
621         // Sync height is init to -1
622         if (stats->nodeStateStats.nSyncHeight > -1)
623             ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
624         else
625             ui->peerSyncHeight->setText(tr("Unknown"));
626     } else {
627         ui->peerBanScore->setText(tr("Fetching..."));
628         ui->peerSyncHeight->setText(tr("Fetching..."));
629     }
630
631     ui->detailWidget->show();
632 }
633
634 void RPCConsole::resizeEvent(QResizeEvent *event)
635 {
636     QWidget::resizeEvent(event);
637 }
638
639 void RPCConsole::showEvent(QShowEvent *event)
640 {
641     QWidget::showEvent(event);
642
643     if (!clientModel)
644         return;
645
646     // start PeerTableModel auto refresh
647     clientModel->getPeerTableModel()->startAutoRefresh();
648 }
649
650 void RPCConsole::hideEvent(QHideEvent *event)
651 {
652     QWidget::hideEvent(event);
653
654     if (!clientModel)
655         return;
656
657     // stop PeerTableModel auto refresh
658     clientModel->getPeerTableModel()->stopAutoRefresh();
659 }
This page took 0.062066 seconds and 4 git commands to generate.