diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,8 @@ dbaccess.cpp dbbrowser.cpp dbconsole.cpp + debugfiltermodel.cpp + debugmodel.cpp debugwidget.cpp instanceselector.cpp logging.cpp diff --git a/src/connectionpage.h b/src/connectionpage.h --- a/src/connectionpage.h +++ b/src/connectionpage.h @@ -24,8 +24,14 @@ #include +class DebugModel; +class DebugFilterModel; +class QAbstractItemModel; class QTableView; -class QStandardItemModel; + +namespace KPIM { +class KCheckComboBox; +} class ConnectionPage : public QWidget { @@ -37,17 +43,23 @@ void showAllConnections(bool); QString toHtml() const; + QString toHtmlFiltered() const; public Q_SLOTS: void clear(); + void clearFiltered(); private Q_SLOTS: void connectionDataInput(const QString &, const QString &); void connectionDataOutput(const QString &, const QString &); private: - QStandardItemModel *mModel = nullptr; + QString toHtml(QAbstractItemModel *model) const; + + DebugModel *mModel = nullptr; + DebugFilterModel *mFilterModel = nullptr; QTableView *mDataView = nullptr; + KPIM::KCheckComboBox *mSenderFilter = nullptr; QString mIdentifier; bool mShowAllConnections; }; diff --git a/src/connectionpage.cpp b/src/connectionpage.cpp --- a/src/connectionpage.cpp +++ b/src/connectionpage.cpp @@ -21,6 +21,12 @@ #include "connectionpage.h" +#include "debugfiltermodel.h" +#include "debugmodel.h" + +#include + +#include #include #include @@ -30,16 +36,28 @@ #include "tracernotificationinterface.h" #include +Q_DECLARE_METATYPE(DebugModel::Message) + ConnectionPage::ConnectionPage(const QString &identifier, QWidget *parent) : QWidget(parent), mIdentifier(identifier), mShowAllConnections(false) { QVBoxLayout *layout = new QVBoxLayout(this); + auto h = new QHBoxLayout; + layout->addLayout(h); + + h->addWidget(new QLabel(QStringLiteral("Programs:"))); + h->addWidget(mSenderFilter = new KPIM::KCheckComboBox()); + h->setStretchFactor(mSenderFilter, 2); + + mModel = new DebugModel(this); + mModel->setSenderFilterModel(qobject_cast(mSenderFilter->model())); - mModel = new QStandardItemModel(0,3,this); - mModel->setHorizontalHeaderLabels({ QStringLiteral("Sender"), QStringLiteral("Direction"), QStringLiteral("Message") }); + mFilterModel = new DebugFilterModel(this); + mFilterModel->setSourceModel(mModel); + mFilterModel->setSenderFilter(mSenderFilter); auto mDataView = new QTableView(this); - mDataView->setModel(mModel); + mDataView->setModel(mFilterModel); mDataView->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); mDataView->horizontalHeader()->setStretchLastSection(true); @@ -53,54 +71,62 @@ this, &ConnectionPage::connectionDataOutput); connect(mDataView->horizontalHeader(), &QHeaderView::sectionResized, mDataView, &QTableView::resizeRowsToContents); + connect(mFilterModel, &QAbstractItemModel::modelReset, + mDataView, &QTableView::resizeRowsToContents); + connect(mFilterModel, &QAbstractItemModel::layoutChanged, + mDataView, &QTableView::resizeRowsToContents); } void ConnectionPage::connectionDataInput(const QString &identifier, const QString &msg) { - QString str = QStringLiteral("%2").arg(identifier) + QLatin1Char(' '); if (mShowAllConnections || identifier == mIdentifier) { - str += QStringLiteral("%1").arg(msg.toHtmlEscaped()); - - auto out = new QStandardItem(QStringLiteral("<-")); - out->setForeground(Qt::red); - mModel->appendRow(QList() << new QStandardItem(identifier) << out << new QStandardItem(msg.trimmed())); + mModel->addMessage(identifier, DebugModel::ClientToServer, msg); } } void ConnectionPage::connectionDataOutput(const QString &identifier, const QString &msg) { - QString str = QStringLiteral("%2").arg(identifier) + QLatin1Char(' '); if (mShowAllConnections || identifier == mIdentifier) { - str += msg.toHtmlEscaped(); - - auto out = new QStandardItem(QStringLiteral("->")); - out->setForeground(Qt::green); - mModel->appendRow({new QStandardItem(identifier), out, new QStandardItem(msg.trimmed())}); + mModel->addMessage(identifier, DebugModel::ServerToClient, msg); } } void ConnectionPage::showAllConnections(bool show) { mShowAllConnections = show; } -QString ConnectionPage::toHtml() const +QString ConnectionPage::toHtml(QAbstractItemModel *model) const { QString ret; - int anz = mModel->rowCount(); + int anz = model->rowCount(); for (int row=0; row < anz; row++) { - const auto &identifier = mModel->data(mModel->index(row,0)).toString(); - const auto &direction = mModel->data(mModel->index(row,1)).toString(); - const auto &msg = mModel->data(mModel->index(row,2)).toString(); + const auto message = model->data(model->index(row, 0), DebugModel::MessageRole).value(); + const auto &sender = model->data(model->index(row, DebugModel::SenderColumn)).toString(); - ret += identifier + direction + msg + QStringLiteral("\n"); + ret += (message.direction==DebugModel::ClientToServer ? QStringLiteral("<- ") : QStringLiteral("-> ")); + ret += sender + QStringLiteral(" ") + message.message + QStringLiteral("\n"); } return ret; } +QString ConnectionPage::toHtmlFiltered() const +{ + return toHtml(mFilterModel); +} + +QString ConnectionPage::toHtml() const +{ + return toHtml(mModel); +} + void ConnectionPage::clear() { mModel->removeRows(0, mModel->rowCount()); } +void ConnectionPage::clearFiltered() +{ + mFilterModel->removeRows(0, mFilterModel->rowCount()); +} diff --git a/src/debugfiltermodel.h b/src/debugfiltermodel.h new file mode 100644 --- /dev/null +++ b/src/debugfiltermodel.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2018 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + + +#ifndef DEBUGFILTERMODEL_H_ +#define DEBUGFILTERMODEL_H_ + +#include +#include +#include + +namespace KPIM { +class KCheckComboBox; +} + +class DebugFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit DebugFilterModel(QObject *parent = nullptr); + ~DebugFilterModel(); + + void setSenderFilter(KPIM::KCheckComboBox *appFilter); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override; + +private: + KPIM::KCheckComboBox *mSenderFilter = nullptr; + QSet mCheckedSenders; + QTimer mInvalidateTimer; +}; + +#endif diff --git a/src/debugfiltermodel.cpp b/src/debugfiltermodel.cpp new file mode 100644 --- /dev/null +++ b/src/debugfiltermodel.cpp @@ -0,0 +1,69 @@ +/* + Copyright (c) 2018 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "debugfiltermodel.h" +#include "debugmodel.h" +#include "akonadiconsole_debug.h" + +#include + +Q_DECLARE_METATYPE(DebugModel::Message) + +using namespace KPIM; + +DebugFilterModel::DebugFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ + mInvalidateTimer.setInterval(50); + mInvalidateTimer.setSingleShot(true); + connect(&mInvalidateTimer, &QTimer::timeout, + this, &DebugFilterModel::invalidate); +} + +DebugFilterModel::~DebugFilterModel() +{ +} + +void DebugFilterModel::setSenderFilter(KCheckComboBox *senderFilter) +{ + if (mSenderFilter) { + mSenderFilter->disconnect(this); + } + mSenderFilter = senderFilter; + connect(mSenderFilter, &KCheckComboBox::checkedItemsChanged, + this, [this](const QStringList &_items) { + Q_UNUSED(_items); + const auto items = mSenderFilter->checkedItems(DebugModel::IdentifierRole); + mCheckedSenders.clear(); + mCheckedSenders.reserve(items.count()); + for (const auto &item : items) { + mCheckedSenders.insert(item); + } + if (!mInvalidateTimer.isActive()) { + mInvalidateTimer.start(); + } + }); +} + +bool DebugFilterModel::filterAcceptsRow(int source_row, const QModelIndex &) const +{ + const auto source_idx = sourceModel()->index(source_row, 0); + const auto msg = sourceModel()->data(source_idx, DebugModel::MessageRole).value(); + return mCheckedSenders.contains(msg.sender); +} diff --git a/src/debugmodel.h b/src/debugmodel.h new file mode 100644 --- /dev/null +++ b/src/debugmodel.h @@ -0,0 +1,84 @@ +/* + Copyright (c) 2018 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef AKONADICONSOLE_DEBUGMODEL_H_ +#define AKONADICONSOLE_DEBUGMODEL_H_ + +#include +#include + +class QStandardItemModel; + +class DebugModel : public QAbstractItemModel +{ + Q_OBJECT +public: + enum Direction { + ClientToServer, + ServerToClient + }; + + struct Message { + QString sender; + Direction direction; + QString message; + }; + + enum Roles { + MessageRole = Qt::UserRole, + IdentifierRole + }; + + enum Columns { + DirectionColumn, + SenderColumn, + MessageColumn, + + _ColumnCount + }; + + + explicit DebugModel(QObject *parent = nullptr); + ~DebugModel() override; + + void addMessage(const QString &sender, Direction direction, const QString &message); + + void setSenderFilterModel(QStandardItemModel *senderFilterModel); + + int rowCount(const QModelIndex &parent = {}) const override; + int columnCount(const QModelIndex &parent = {}) const override; + + QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; + QModelIndex parent(const QModelIndex &child = {}) const override; + + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant data(const QModelIndex &index, int role) const override; + + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + +private: + QString cacheString(const QString &str, QMap &cache, QStandardItemModel *model = nullptr); + QString displaySender(const QString &identifer) const; + + QVector mMessages; + QStandardItemModel *mSenderFilterModel = nullptr; + QMap mSenderCache; +}; + +#endif diff --git a/src/debugmodel.cpp b/src/debugmodel.cpp new file mode 100644 --- /dev/null +++ b/src/debugmodel.cpp @@ -0,0 +1,208 @@ +/* + Copyright (c) 2018 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "debugmodel.h" + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(DebugModel::Message) + +DebugModel::DebugModel(QObject *parent) + : QAbstractItemModel(parent) +{ +} + +DebugModel::~DebugModel() +{ +} + +QString DebugModel::displaySender(const QString &identifer) const +{ + if (mSenderCache[identifer].isEmpty()) { + return identifer; + } else { + return QString(mSenderCache[identifer] + QStringLiteral(" (") + identifer + QStringLiteral(")")); + } +} + +QString DebugModel::cacheString(const QString &str, QMap &cache, QStandardItemModel *model) +{ + auto pos = str.lastIndexOf(QLatin1Char('(')); + auto identifier = str; + QString name; + if (pos != -1) { + identifier = str.mid(pos+1,str.size()-pos-2); + name = str.mid(0,pos-1); + } + if (! cache.contains(identifier)) { + cache.insert(identifier, name); + if (model) { + auto item = new QStandardItem(displaySender(identifier)); + item->setData(identifier, IdentifierRole); + item->setCheckState(Qt::Checked); + model->appendRow(item); + } + return identifier; + } else if (cache[identifier].isEmpty()) { + cache[identifier] = name; + auto item = model->findItems(identifier).first(); + item->setData(displaySender(identifier), Qt::DisplayRole); + } + return identifier; +} + +void DebugModel::addMessage(const QString& sender, DebugModel::Direction direction, const QString& message) +{ + beginInsertRows({}, mMessages.count(), mMessages.count()); + mMessages.push_back({ cacheString(sender, mSenderCache, mSenderFilterModel), direction, + message }); + endInsertRows(); +} + +bool DebugModel::removeRows(int row, int count, const QModelIndex& parent) +{ + if (parent.isValid()) { + return false; + } + + beginRemoveRows(parent, row, row + count); + mMessages.remove(row, count); + + QVector toDelete; + + // find elements that needs to be deleted. + for(const auto &identifer: mSenderCache.keys()) { + bool found = false; + for(const auto &msg: mMessages) { + if (msg.sender == identifer) { + found = true; + break; + } + } + if (!found) { + toDelete.push_back(identifer); + } + } + + // Update senderCache and senderFilterModel + for(const auto &i: toDelete) { + + const auto &item = mSenderFilterModel->findItems(displaySender(i)); + if (!item.isEmpty()) { + const auto &index = item.first()->index(); + mSenderFilterModel->removeRows(index.row(),1); + } + mSenderCache.remove(i); + } + endRemoveRows(); + return true; +} + + +void DebugModel::setSenderFilterModel(QStandardItemModel *senderFilterModel) +{ + mSenderFilterModel = senderFilterModel; +} + +int DebugModel::rowCount(const QModelIndex& parent) const +{ + return parent.isValid() ? 0 : mMessages.count(); +} + +int DebugModel::columnCount(const QModelIndex &) const +{ + return _ColumnCount; +} + +QModelIndex DebugModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid() || row < 0 || row >= mMessages.count() || column < 0 || column >= _ColumnCount) { + return {}; + } + + return createIndex(row, column); +} + +QModelIndex DebugModel::parent(const QModelIndex &) const +{ + return {}; +} + +QVariant DebugModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { + return {}; + } + + switch (section) { + case SenderColumn: + return QStringLiteral("Sender"); + case DirectionColumn: + return QStringLiteral("Direction"); + case MessageColumn: + return QStringLiteral("Message"); + } + return {}; +} + +QVariant DebugModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= mMessages.count() || index.column() >= _ColumnCount) { + return {}; + } + + const auto message = mMessages.at(index.row()); + if (role == Qt::DisplayRole) { + switch (index.column()) { + case SenderColumn: + return displaySender(message.sender); + case DirectionColumn: + switch (message.direction) { + case ClientToServer: + return QStringLiteral("<-"); + case ServerToClient: + return QStringLiteral("->"); + } + return {}; + case MessageColumn: + return message.message; + } + } else if (role == Qt::ToolTipRole) { + switch (index.column()) { + case MessageColumn: + return message.message; + } + } else if (role == Qt::ForegroundRole && index.column() != MessageColumn) { + if (message.direction == ClientToServer) { + return QColor(Qt::red); + } else { + return QColor(Qt::green); + } + } else if (role == MessageRole) { + return QVariant::fromValue(message); + } else if (role == IdentifierRole) { + return message.sender; + } + + return {}; +} diff --git a/src/debugwidget.h b/src/debugwidget.h --- a/src/debugwidget.h +++ b/src/debugwidget.h @@ -27,7 +27,6 @@ #include #include -class QTabWidget; class KTextEdit; class ConnectionPage; @@ -40,24 +39,18 @@ explicit DebugWidget(QWidget *parent = nullptr); private Q_SLOTS: - void connectionStarted(const QString &, const QString &); - void connectionEnded(const QString &, const QString &); void signalEmitted(const QString &, const QString &); void warningEmitted(const QString &, const QString &); void errorEmitted(const QString &, const QString &); void enableDebugger(bool enable); - void tabCloseRequested(int index); - void clearAllTabs(); - void clearCurrentTab(); void saveRichText(); - void closeAllTabs(); + void saveEverythingRichText(); private: KTextEdit *mGeneralView = nullptr; - QTabWidget *mConnectionPages = nullptr; - QHash mPageHash; + ConnectionPage *mConnectionPage = nullptr; org::freedesktop::Akonadi::DebugInterface *mDebugInterface = nullptr; }; diff --git a/src/debugwidget.cpp b/src/debugwidget.cpp --- a/src/debugwidget.cpp +++ b/src/debugwidget.cpp @@ -57,136 +57,43 @@ splitter->setObjectName(QStringLiteral("debugSplitter")); layout->addWidget(splitter); - mConnectionPages = new QTabWidget(splitter); - mConnectionPages->setTabsClosable(true); + mConnectionPage = new ConnectionPage(QStringLiteral("All"), splitter); + mConnectionPage->showAllConnections(true); mGeneralView = new KTextEdit(splitter); mGeneralView->setReadOnly(true); - ConnectionPage *page = new ConnectionPage(QStringLiteral("All")); - page->showAllConnections(true); - mConnectionPages->addTab(page, QStringLiteral("All")); - - connect(mConnectionPages, &QTabWidget::tabCloseRequested, this, &DebugWidget::tabCloseRequested); org::freedesktop::Akonadi::TracerNotification *iface = new org::freedesktop::Akonadi::TracerNotification(QString(), QStringLiteral("/tracing/notifications"), QDBusConnection::sessionBus(), this); - connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionStarted, this, &DebugWidget::connectionStarted); - connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionEnded, this, &DebugWidget::connectionEnded); connect(iface, &org::freedesktop::Akonadi::TracerNotification::signalEmitted, this, &DebugWidget::signalEmitted); connect(iface, &org::freedesktop::Akonadi::TracerNotification::warningEmitted, this, &DebugWidget::warningEmitted); connect(iface, &org::freedesktop::Akonadi::TracerNotification::errorEmitted, this, &DebugWidget::errorEmitted); - // in case we started listening when the connection is already ongoing - connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionDataInput, this, &DebugWidget::connectionStarted); - connect(iface, &org::freedesktop::Akonadi::TracerNotification::connectionDataOutput, this, &DebugWidget::connectionStarted); - QHBoxLayout *buttonLayout = new QHBoxLayout; layout->addLayout(buttonLayout); - QPushButton *clearAllButton = new QPushButton(QStringLiteral("Clear All Tabs"), this); - QPushButton *clearCurrentButton = new QPushButton(QStringLiteral("Clear Current Tab"), this); QPushButton *clearGeneralButton = new QPushButton(QStringLiteral("Clear Information View"), this); - QPushButton *closeAllTabsButton = new QPushButton(QStringLiteral("Close All Tabs"), this); - QPushButton *saveRichtextButton = new QPushButton(QStringLiteral("Save as RichText..."), this); + QPushButton *clearFilteredButton = new QPushButton(QStringLiteral("Clear Filtered Messages"), this); + QPushButton *clearAllButton = new QPushButton(QStringLiteral("Clear All Messages"), this); + QPushButton *saveRichtextButton = new QPushButton(QStringLiteral("Save Filtered Messages ..."), this); + QPushButton *saveRichtextEverythingButton = new QPushButton(QStringLiteral("Save All Messages ..."), this); + buttonLayout->addWidget(clearFilteredButton); buttonLayout->addWidget(clearAllButton); - buttonLayout->addWidget(clearCurrentButton); buttonLayout->addWidget(clearGeneralButton); - buttonLayout->addWidget(closeAllTabsButton); buttonLayout->addWidget(saveRichtextButton); + buttonLayout->addWidget(saveRichtextEverythingButton); - connect(clearAllButton, &QPushButton::clicked, this, &DebugWidget::clearAllTabs); - connect(clearCurrentButton, &QPushButton::clicked, this, &DebugWidget::clearCurrentTab); + connect(clearFilteredButton, &QPushButton::clicked, mConnectionPage, &ConnectionPage::clearFiltered); + connect(clearAllButton, &QPushButton::clicked, mConnectionPage, &ConnectionPage::clear); connect(clearGeneralButton, &QPushButton::clicked, mGeneralView, &KTextEdit::clear); - connect(closeAllTabsButton, &QPushButton::clicked, this, &DebugWidget::closeAllTabs); connect(saveRichtextButton, &QPushButton::clicked, this, &DebugWidget::saveRichText); + connect(saveRichtextEverythingButton, &QPushButton::clicked, this, &DebugWidget::saveEverythingRichText); Akonadi::ControlGui::widgetNeedsAkonadi(this); } -void DebugWidget::connectionStarted(const QString &identifier, const QString &msg) -{ - Q_UNUSED(msg); - if (mPageHash.contains(identifier)) { - return; - } - - ConnectionPage *page = new ConnectionPage(identifier); - mConnectionPages->addTab(page, identifier); - - mPageHash.insert(identifier, page); -} - -void DebugWidget::connectionEnded(const QString &identifier, const QString &) -{ - if (!mPageHash.contains(identifier)) { - return; - } - - QWidget *widget = mPageHash[ identifier ]; - - mConnectionPages->removeTab(mConnectionPages->indexOf(widget)); - - mPageHash.remove(identifier); - delete widget; -} - -void DebugWidget::tabCloseRequested(int index) -{ - if (index != 0) { - QWidget *page = mConnectionPages->widget(index); - QMutableHashIterator it(mPageHash); - while (it.hasNext()) { - it.next(); - if (it.value() == page) { - it.remove(); - break; - } - } - - mConnectionPages->removeTab(index); - delete page; - } -} - -void DebugWidget::clearAllTabs() -{ - ConnectionPage *page = qobject_cast(mConnectionPages->widget(0)); - if (page) { - page->clear(); - } - - QMutableHashIterator it(mPageHash); - while (it.hasNext()) { - it.next().value()->clear(); - } -} - -void DebugWidget::clearCurrentTab() -{ - ConnectionPage *page = qobject_cast(mConnectionPages->currentWidget()); - if (!page) { - return; - } - - page->clear(); -} - -void DebugWidget::closeAllTabs() -{ - ConnectionPage *page = qobject_cast(mConnectionPages->widget(0)); - if (page) { - page->clear(); - } - - while (mConnectionPages->count() > 1) { - mConnectionPages->removeTab(1); - } - qDeleteAll(mPageHash); - mPageHash.clear(); -} - void DebugWidget::signalEmitted(const QString &signalName, const QString &msg) { mGeneralView->append(QStringLiteral("%1 ( %2 )").arg(signalName, msg)); @@ -209,11 +116,22 @@ void DebugWidget::saveRichText() { - ConnectionPage *page = qobject_cast(mConnectionPages->currentWidget()); - if (!page) { + const QString fileName = QFileDialog::getSaveFileName(this); + if (fileName.isEmpty()) { + return; + } + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { return; } + file.write(mConnectionPage->toHtmlFiltered().toUtf8()); + file.close(); +} + +void DebugWidget::saveEverythingRichText() +{ const QString fileName = QFileDialog::getSaveFileName(this); if (fileName.isEmpty()) { return; @@ -224,7 +142,6 @@ return; } - file.write(page->toHtml().toUtf8()); + file.write(mConnectionPage->toHtml().toUtf8()); file.close(); } -