File indexing completed on 2024-05-19 05:42:26

0001 // ct_lvtqtw_errorview.cpp                                                              -*-C++-*-
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 #include <ct_lvtqtw_errorview.h>
0020 
0021 #include <ui_ct_lvtqtw_errorview.h>
0022 
0023 #include <ct_lvtmdl_errorsmodel.h>
0024 #include <ct_lvtmdl_simpletextmodel.h>
0025 
0026 #include <ct_lvtqtd_removedelegate.h>
0027 
0028 #include <QAction>
0029 #include <QApplication>
0030 #include <QClipboard>
0031 #include <QHeaderView>
0032 #include <QItemSelectionModel>
0033 #include <QMenu>
0034 #include <QSettings>
0035 
0036 namespace Codethink::lvtqtw {
0037 
0038 struct ErrorView::Private {
0039     Codethink::lvtmdl::ErrorModelFilter filterModel;
0040     Codethink::lvtmdl::SimpleTextModel ignoreGroupModel;
0041 
0042     // We don't own the debug model, it should be created before the mainwindow
0043     // to not lose debug information. We only hold the pointer to it. it should
0044     // always be valid, after being set, untill the destruction of this widget.
0045     Codethink::lvtmdl::ErrorsModel *model = nullptr;
0046 
0047     // TODO: Maybe move this to a QComboBox class
0048     QString currentEditedTextOnCategoryCombobox;
0049 };
0050 
0051 ErrorView::ErrorView(QWidget *parent):
0052     QWidget(parent), ui(std::make_unique<Ui::ErrorView>()), d(std::make_unique<ErrorView::Private>())
0053 {
0054     ui->setupUi(this);
0055 
0056     ui->tableView->setModel(&d->filterModel);
0057     ui->tableView->horizontalHeader()->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);
0058 
0059     ui->comboBox->setModel(&d->ignoreGroupModel);
0060 
0061     auto *delegate = new lvtqtd::RemoveDelegate();
0062     ui->comboBox->view()->setMouseTracking(true);
0063     ui->comboBox->view()->setItemDelegate(delegate);
0064     ui->comboBox->setInsertPolicy(QComboBox::InsertAtBottom);
0065     ui->comboBox->lineEdit()->setPlaceholderText(tr("Ignored Categories"));
0066 
0067     d->ignoreGroupModel.setStorageGroup("DebugIgnoreGroups");
0068 
0069     d->filterModel.setFilterCompilerMessages(ui->btnCompilerMessages->isChecked());
0070     d->filterModel.setFilterParseMessages(ui->btnParseMessages->isChecked());
0071     d->filterModel.setIgnoreCategoriesModel(&d->ignoreGroupModel);
0072 
0073     connect(ui->btnCompilerMessages,
0074             &QToolButton::toggled,
0075             &d->filterModel,
0076             &Codethink::lvtmdl::ErrorModelFilter::setFilterCompilerMessages);
0077     connect(ui->btnParseMessages,
0078             &QToolButton::toggled,
0079             &d->filterModel,
0080             &Codethink::lvtmdl::ErrorModelFilter::setFilterParseMessages);
0081     connect(ui->tableView->horizontalHeader(),
0082             &QWidget::customContextMenuRequested,
0083             this,
0084             &ErrorView::createColumnVisibilityMenu);
0085     connect(ui->filterLine, &QLineEdit::textChanged, &d->filterModel, &lvtmdl::ErrorModelFilter::setFilterString);
0086     connect(ui->invertFilterBtn, &QCheckBox::stateChanged, this, [this] {
0087         d->filterModel.setInvertMessageFilter(ui->invertFilterBtn->isChecked());
0088     });
0089 
0090     // The QComboBox loses the text as soon as there's a editingFinished, so we need to store the string
0091     // being edited, to use it later on.
0092     connect(ui->comboBox->lineEdit(), &QLineEdit::textEdited, this, [this] {
0093         d->currentEditedTextOnCategoryCombobox = ui->comboBox->lineEdit()->text();
0094     });
0095 
0096     connect(ui->comboBox->lineEdit(), &QLineEdit::editingFinished, this, [this] {
0097         if (!d->currentEditedTextOnCategoryCombobox.isEmpty()) {
0098             d->ignoreGroupModel.addString(d->currentEditedTextOnCategoryCombobox);
0099         }
0100         d->currentEditedTextOnCategoryCombobox = QString();
0101     });
0102 
0103     ui->tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
0104     ui->btnCompilerMessages->setIcon(QIcon(":/icons/critical"));
0105     ui->btnParseMessages->setIcon(QIcon(":/icons/info"));
0106 
0107     auto *copyAction = new QAction();
0108     copyAction->setShortcut(QKeySequence::Copy);
0109     copyAction->setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
0110     addAction(copyAction);
0111     connect(copyAction, &QAction::triggered, this, [this] {
0112         auto rows = ui->tableView->selectionModel()->selectedRows();
0113 
0114         QString result;
0115         for (const QModelIndex& idx : rows) {
0116             result += idx.data(Qt::ToolTipRole).toString();
0117         }
0118 
0119         qApp->clipboard()->setText(result);
0120     });
0121 }
0122 
0123 ErrorView::~ErrorView()
0124 {
0125     saveColumnVisibilityState();
0126 }
0127 
0128 void ErrorView::setModel(lvtmdl::ErrorsModel *model)
0129 {
0130     if (d->model) {
0131         disconnect(d->model, &QAbstractItemModel::rowsRemoved, ui->tableView, nullptr);
0132         disconnect(d->model, &QAbstractItemModel::rowsInserted, ui->tableView, nullptr);
0133     }
0134 
0135     d->filterModel.setSourceModel(model);
0136     d->model = model;
0137 
0138     connect(d->model, &QAbstractItemModel::rowsRemoved, ui->tableView, &QTableView::scrollToBottom);
0139     connect(d->model, &QAbstractItemModel::rowsInserted, ui->tableView, &QTableView::scrollToBottom);
0140 
0141     loadColumnVisibilityState();
0142 }
0143 
0144 void ErrorView::saveColumnVisibilityState()
0145 {
0146     QSettings settings;
0147     settings.beginGroup("DebugTable");
0148 
0149     const int columnCount = ui->tableView->model()->columnCount();
0150     for (int i = 0; i < columnCount; i++) {
0151         settings.setValue(QStringLiteral("column_%1_visible").arg(i), !ui->tableView->isColumnHidden(i));
0152     }
0153 }
0154 
0155 void ErrorView::loadColumnVisibilityState()
0156 {
0157     QSettings settings;
0158     settings.beginGroup("DebugTable");
0159 
0160     const int columnCount = ui->tableView->model()->columnCount();
0161     for (int i = 0; i < columnCount; i++) {
0162         const bool visible = settings.value(QStringLiteral("column_%1_visible").arg(i), true).toBool();
0163         ui->tableView->setColumnHidden(i, !visible);
0164     }
0165 }
0166 
0167 void ErrorView::createColumnVisibilityMenu(const QPoint& pos)
0168 {
0169     QMenu menu;
0170     const int columnCount = ui->tableView->model()->columnCount();
0171     for (int i = 0; i < columnCount; i++) {
0172         const QString textAt = ui->tableView->model()->headerData(i, Qt::Orientation::Horizontal).toString();
0173         QAction *toggleVisibility = menu.addAction(textAt);
0174         toggleVisibility->setCheckable(true);
0175         toggleVisibility->setChecked(!ui->tableView->isColumnHidden(i));
0176         connect(toggleVisibility, &QAction::toggled, this, [this, i] {
0177             ui->tableView->setColumnHidden(i, !ui->tableView->isColumnHidden(i));
0178         });
0179     }
0180     menu.exec(ui->tableView->horizontalHeader()->mapToGlobal(pos));
0181 }
0182 
0183 } // namespace Codethink::lvtqtw