summaryrefslogtreecommitdiffstats
path: root/ui/qt/coloring_rules_dialog.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /ui/qt/coloring_rules_dialog.cpp
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ui/qt/coloring_rules_dialog.cpp')
-rw-r--r--ui/qt/coloring_rules_dialog.cpp459
1 files changed, 459 insertions, 0 deletions
diff --git a/ui/qt/coloring_rules_dialog.cpp b/ui/qt/coloring_rules_dialog.cpp
new file mode 100644
index 0000000..20eb72b
--- /dev/null
+++ b/ui/qt/coloring_rules_dialog.cpp
@@ -0,0 +1,459 @@
+/* coloring_rules_dialog.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "coloring_rules_dialog.h"
+#include <ui_coloring_rules_dialog.h>
+
+#include "ui/simple_dialog.h"
+#include "epan/prefs.h"
+
+#include <wsutil/utf8_entities.h>
+
+#include "wsutil/filesystem.h"
+#include "epan/dfilter/dfilter.h"
+
+#include "main_application.h"
+
+#include "ui/qt/utils/qt_ui_utils.h"
+#include "ui/qt/widgets/copy_from_profile_button.h"
+#include "ui/qt/widgets/wireshark_file_dialog.h"
+
+#include <functional>
+#include <QColorDialog>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QUrl>
+
+/*
+ * @file Coloring Rules dialog
+ *
+ * Coloring rule editor for the current profile.
+ */
+
+// To do:
+// - Make the filter column narrower? It's easy to run into Qt's annoying
+// habit of horizontally scrolling QTreeWidgets here.
+
+ColoringRulesDialog::ColoringRulesDialog(QWidget *parent, QString add_filter) :
+ GeometryStateDialog(parent),
+ ui(new Ui::ColoringRulesDialog),
+ colorRuleModel_(palette().color(QPalette::Text), palette().color(QPalette::Base), this),
+ colorRuleDelegate_(this)
+{
+ ui->setupUi(this);
+ if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 4 / 5);
+
+ setWindowTitle(mainApp->windowTitleString(tr("Coloring Rules %1").arg(get_profile_name())));
+
+ ui->coloringRulesTreeView->setModel(&colorRuleModel_);
+ ui->coloringRulesTreeView->setItemDelegate(&colorRuleDelegate_);
+
+ ui->coloringRulesTreeView->viewport()->setAcceptDrops(true);
+
+ for (int i = 0; i < colorRuleModel_.columnCount(); i++) {
+ ui->coloringRulesTreeView->resizeColumnToContents(i);
+ }
+
+ ui->newToolButton->setStockIcon("list-add");
+ ui->deleteToolButton->setStockIcon("list-remove");
+ ui->copyToolButton->setStockIcon("list-copy");
+ ui->clearToolButton->setStockIcon("list-clear");
+
+#ifdef Q_OS_MAC
+ ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true);
+ ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true);
+ ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true);
+ ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true);
+ ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true);
+#endif
+
+ connect(ui->coloringRulesTreeView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ this, SLOT(colorRuleSelectionChanged(const QItemSelection &, const QItemSelection &)));
+ connect(&colorRuleDelegate_, SIGNAL(invalidField(const QModelIndex&, const QString&)),
+ this, SLOT(invalidField(const QModelIndex&, const QString&)));
+ connect(&colorRuleDelegate_, SIGNAL(validField(const QModelIndex&)),
+ this, SLOT(validField(const QModelIndex&)));
+ connect(ui->coloringRulesTreeView, &QTreeView::clicked, this, &ColoringRulesDialog::treeItemClicked);
+ connect(&colorRuleModel_, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowCountChanged()));
+ connect(&colorRuleModel_, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(rowCountChanged()));
+
+ rowCountChanged();
+
+ import_button_ = ui->buttonBox->addButton(tr("Import…"), QDialogButtonBox::ApplyRole);
+ import_button_->setToolTip(tr("Select a file and add its filters to the end of the list."));
+ export_button_ = ui->buttonBox->addButton(tr("Export…"), QDialogButtonBox::ApplyRole);
+ export_button_->setToolTip(tr("Save filters in a file."));
+
+ CopyFromProfileButton * copy_button = new CopyFromProfileButton(this, COLORFILTERS_FILE_NAME, tr("Copy coloring rules from another profile."));
+ ui->buttonBox->addButton(copy_button, QDialogButtonBox::ActionRole);
+ connect(copy_button, &CopyFromProfileButton::copyProfile, this, &ColoringRulesDialog::copyFromProfile);
+
+ QString abs_path = gchar_free_to_qstring(get_persconffile_path(COLORFILTERS_FILE_NAME, TRUE));
+ if (file_exists(abs_path.toUtf8().constData())) {
+ ui->pathLabel->setText(abs_path);
+ ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString());
+ ui->pathLabel->setToolTip(tr("Open ") + COLORFILTERS_FILE_NAME);
+ ui->pathLabel->setEnabled(true);
+ }
+
+ if (!add_filter.isEmpty()) {
+ colorRuleModel_.addColor(false, add_filter, palette().color(QPalette::Text), palette().color(QPalette::Base));
+
+ //setup the buttons appropriately
+ ui->coloringRulesTreeView->setCurrentIndex(colorRuleModel_.index(0, 0));
+
+ //set edit on display filter
+ ui->coloringRulesTreeView->edit(colorRuleModel_.index(0, 1));
+ }else {
+ ui->coloringRulesTreeView->setCurrentIndex(QModelIndex());
+ }
+
+ updateHint();
+}
+
+ColoringRulesDialog::~ColoringRulesDialog()
+{
+ delete ui;
+}
+
+void ColoringRulesDialog::copyFromProfile(QString filename)
+{
+ QString err;
+
+ if (!colorRuleModel_.importColors(filename, err)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData());
+ }
+
+ for (int i = 0; i < colorRuleModel_.columnCount(); i++) {
+ ui->coloringRulesTreeView->resizeColumnToContents(i);
+ }
+}
+
+void ColoringRulesDialog::showEvent(QShowEvent *)
+{
+ ui->fGPushButton->setFixedHeight(ui->copyToolButton->geometry().height());
+ ui->bGPushButton->setFixedHeight(ui->copyToolButton->geometry().height());
+#ifndef Q_OS_MAC
+ ui->displayFilterPushButton->setFixedHeight(ui->copyToolButton->geometry().height());
+#endif
+}
+
+void ColoringRulesDialog::rowCountChanged()
+{
+ ui->clearToolButton->setEnabled(colorRuleModel_.rowCount() > 0);
+}
+
+bool ColoringRulesDialog::isValidFilter(QString filter, QString * error)
+{
+ dfilter_t *dfp = NULL;
+ df_error_t *df_err = NULL;
+
+ if (dfilter_compile(filter.toUtf8().constData(), &dfp, &df_err)) {
+ dfilter_free(dfp);
+ return true;
+ }
+
+ if (df_err)
+ {
+ error->append(df_err->msg);
+ df_error_free(&df_err);
+ }
+
+ return false;
+}
+
+void ColoringRulesDialog::treeItemClicked(const QModelIndex &index)
+{
+ QModelIndex idx = ui->coloringRulesTreeView->model()->index(index.row(), ColoringRulesModel::colFilter);
+ QString filter = idx.data(Qt::DisplayRole).toString();
+ QString err;
+ if (! isValidFilter(filter, &err) && index.data(Qt::CheckStateRole).toInt() == Qt::Checked)
+ {
+ errors_.insert(index, err);
+ updateHint(index);
+ }
+ else
+ {
+ QList<QModelIndex> keys = errors_.keys();
+ bool update = false;
+ foreach (QModelIndex key, keys)
+ {
+ if (key.row() == index.row())
+ {
+ errors_.remove(key);
+ update = true;
+ }
+ }
+
+ if (update)
+ updateHint(index);
+ }
+}
+
+void ColoringRulesDialog::invalidField(const QModelIndex &index, const QString& errMessage)
+{
+ errors_.insert(index, errMessage);
+ updateHint(index);
+}
+
+void ColoringRulesDialog::validField(const QModelIndex &index)
+{
+ QList<QModelIndex> keys = errors_.keys();
+ bool update = false;
+ foreach (QModelIndex key, keys)
+ {
+ if (key.row() == index.row())
+ {
+ errors_.remove(key);
+ update = true;
+ }
+ }
+
+ if (update)
+ updateHint(index);
+}
+
+void ColoringRulesDialog::updateHint(QModelIndex idx)
+{
+ QString hint = "<small><i>";
+ QString error_text;
+ bool enable_save = true;
+
+ if (errors_.count() > 0) {
+ //take the list of QModelIndexes and sort them so first color rule error is displayed
+ //This isn't the most efficent algorithm, but the list shouldn't be large to matter
+ QList<QModelIndex> keys = errors_.keys();
+
+ //list is not guaranteed to be sorted, so force it
+ std::sort(keys.begin(), keys.end());
+ const QModelIndex& error_key = keys[0];
+ error_text = QString("%1: %2")
+ .arg(colorRuleModel_.data(colorRuleModel_.index(error_key.row(), ColoringRulesModel::colName), Qt::DisplayRole).toString())
+ .arg(errors_[error_key]);
+ }
+
+ if (error_text.isEmpty()) {
+ hint += tr("Double click to edit. Drag to move. Rules are processed in order until a match is found.");
+ } else {
+ hint += error_text;
+ if (idx.isValid())
+ {
+ QModelIndex fiIdx = ui->coloringRulesTreeView->model()->index(idx.row(), ColoringRulesModel::colName);
+ if (fiIdx.data(Qt::CheckStateRole).toInt() == Qt::Checked)
+ enable_save = false;
+ }
+ else
+ enable_save = false;
+ }
+
+ hint += "</i></small>";
+ ui->hintLabel->setText(hint);
+
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable_save);
+}
+
+void ColoringRulesDialog::setColorButtons(QModelIndex &index)
+{
+ QString color_button_ss =
+ "QPushButton {"
+ " border: 1px solid palette(Dark);"
+ " padding-left: %1px;"
+ " padding-right: %1px;"
+ " color: %2;"
+ " background-color: %3;"
+ "}";
+
+ int one_em = fontMetrics().height();
+ QVariant fg = colorRuleModel_.data(index, Qt::ForegroundRole);
+ QVariant bg = colorRuleModel_.data(index, Qt::BackgroundRole);
+ if (fg.isNull() || bg.isNull()) {
+ //should never happen
+ ui->fGPushButton->setVisible(false);
+ ui->bGPushButton->setVisible(false);
+ } else {
+ QString fg_color = fg.toString();
+ QString bg_color = bg.toString();
+
+ ui->fGPushButton->setStyleSheet(color_button_ss.arg(one_em).arg(bg_color).arg(fg_color));
+ ui->bGPushButton->setStyleSheet(color_button_ss.arg(one_em).arg(fg_color).arg(bg_color));
+ }
+}
+
+void ColoringRulesDialog::colorRuleSelectionChanged(const QItemSelection&, const QItemSelection&)
+{
+ QModelIndexList selectedList = ui->coloringRulesTreeView->selectionModel()->selectedIndexes();
+
+ //determine the number of unique rows
+ QHash<int, QModelIndex> selectedRows;
+ foreach (const QModelIndex &index, selectedList) {
+ selectedRows.insert(index.row(), index);
+ }
+
+ qsizetype num_selected = selectedRows.count();
+ if (num_selected == 1) {
+ setColorButtons(selectedList[0]);
+ }
+
+ ui->copyToolButton->setEnabled(num_selected == 1);
+ ui->deleteToolButton->setEnabled(num_selected > 0);
+ ui->fGPushButton->setVisible(num_selected == 1);
+ ui->bGPushButton->setVisible(num_selected == 1);
+ ui->displayFilterPushButton->setVisible(num_selected == 1);
+}
+
+void ColoringRulesDialog::changeColor(bool foreground)
+{
+ QModelIndex current = ui->coloringRulesTreeView->currentIndex();
+ if (!current.isValid())
+ return;
+
+ QColorDialog *color_dlg = new QColorDialog();
+ color_dlg->setCurrentColor(colorRuleModel_.data(current, foreground ? Qt::ForegroundRole : Qt::BackgroundRole).toString());
+
+ connect(color_dlg, &QColorDialog::colorSelected, std::bind(&ColoringRulesDialog::colorChanged, this, foreground, std::placeholders::_1));
+ color_dlg->setWindowModality(Qt::ApplicationModal);
+ color_dlg->setAttribute(Qt::WA_DeleteOnClose);
+ color_dlg->show();
+}
+
+void ColoringRulesDialog::colorChanged(bool foreground, const QColor &cc)
+{
+ QModelIndex current = ui->coloringRulesTreeView->currentIndex();
+ if (!current.isValid())
+ return;
+
+ colorRuleModel_.setData(current, cc, foreground ? Qt::ForegroundRole : Qt::BackgroundRole);
+ setColorButtons(current);
+}
+
+void ColoringRulesDialog::on_fGPushButton_clicked()
+{
+ changeColor();
+}
+
+void ColoringRulesDialog::on_bGPushButton_clicked()
+{
+ changeColor(false);
+}
+
+void ColoringRulesDialog::on_displayFilterPushButton_clicked()
+{
+ QModelIndex current = ui->coloringRulesTreeView->currentIndex();
+ if (!current.isValid())
+ return;
+
+ QString filter = colorRuleModel_.data(colorRuleModel_.index(current.row(), ColoringRulesModel::colFilter), Qt::DisplayRole).toString();
+ emit filterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain);
+}
+
+void ColoringRulesDialog::addRule(bool copy_from_current)
+{
+ const QModelIndex &current = ui->coloringRulesTreeView->currentIndex();
+ if (copy_from_current && !current.isValid())
+ return;
+
+ //always add rules at the top of the list
+ if (copy_from_current) {
+ colorRuleModel_.copyRow(colorRuleModel_.index(0, 0).row(), current.row());
+ } else {
+ if (!colorRuleModel_.insertRows(0, 1)) {
+ return;
+ }
+ }
+
+ //set edit on display filter
+ ui->coloringRulesTreeView->edit(colorRuleModel_.index(0, 1));
+}
+
+void ColoringRulesDialog::on_newToolButton_clicked()
+{
+ addRule();
+}
+
+void ColoringRulesDialog::on_deleteToolButton_clicked()
+{
+ QModelIndexList selectedList = ui->coloringRulesTreeView->selectionModel()->selectedIndexes();
+ qsizetype num_selected = selectedList.count() / colorRuleModel_.columnCount();
+ if (num_selected > 0) {
+ //list is not guaranteed to be sorted, so force it
+ std::sort(selectedList.begin(), selectedList.end());
+
+ //walk the list from the back because deleting a value in
+ //the middle will leave the selectedList out of sync and
+ //delete the wrong elements
+ for (int i = static_cast<int>(selectedList.count()) - 1; i >= 0; i--) {
+ QModelIndex deleteIndex = selectedList[i];
+ //selectedList includes all cells, use first column as key to remove row
+ if (deleteIndex.isValid() && (deleteIndex.column() == 0)) {
+ colorRuleModel_.removeRows(deleteIndex.row(), 1);
+ }
+ }
+ }
+}
+
+void ColoringRulesDialog::on_copyToolButton_clicked()
+{
+ addRule(true);
+}
+
+void ColoringRulesDialog::on_clearToolButton_clicked()
+{
+ colorRuleModel_.removeRows(0, colorRuleModel_.rowCount());
+}
+
+void ColoringRulesDialog::on_buttonBox_clicked(QAbstractButton *button)
+{
+ QString err;
+
+ if (button == import_button_) {
+ QString file_name = WiresharkFileDialog::getOpenFileName(this, mainApp->windowTitleString(tr("Import Coloring Rules")),
+ mainApp->openDialogInitialDir().path());
+ if (!file_name.isEmpty()) {
+ if (!colorRuleModel_.importColors(file_name, err)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData());
+ }
+ }
+ } else if (button == export_button_) {
+ int num_items = static_cast<int>(ui->coloringRulesTreeView->selectionModel()->selectedIndexes().count()) / colorRuleModel_.columnCount();
+
+ if (num_items < 1) {
+ num_items = colorRuleModel_.rowCount();
+ }
+
+ if (num_items < 1)
+ return;
+
+ QString caption = mainApp->windowTitleString(tr("Export %1 Coloring Rules").arg(num_items));
+ QString file_name = WiresharkFileDialog::getSaveFileName(this, caption,
+ mainApp->openDialogInitialDir().path());
+ if (!file_name.isEmpty()) {
+ if (!colorRuleModel_.exportColors(file_name, err)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData());
+ }
+ }
+ }
+}
+
+void ColoringRulesDialog::on_buttonBox_accepted()
+{
+ QString err;
+ int ret = QDialog::Accepted;
+ if (!colorRuleModel_.writeColors(err)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err.toUtf8().constData());
+ ret = QDialog::Rejected;
+ }
+ done(ret);
+}
+
+void ColoringRulesDialog::on_buttonBox_helpRequested()
+{
+ mainApp->helpTopicAction(HELP_COLORING_RULES_DIALOG);
+}