wireshark/ui/qt/models/coloring_rules_model.cpp

569 lines
15 KiB
C++

/* coloring_rules_model.cpp
* Data model for coloring rules.
*
* 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_model.h"
#include <errno.h>
#include "ui/ws_ui_util.h" //for color_filter_add_cb
#include <ui/qt/utils/color_utils.h>
#include <ui/qt/utils/qt_ui_utils.h>
#include <ui/qt/utils/variant_pointer.h>
#include <ui/qt/utils/wireshark_mime_data.h>
#include <QMimeData>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
ColoringRuleItem::ColoringRuleItem(bool disabled, QString name, QString filter, QColor foreground, QColor background, ColoringRuleItem* parent)
: ModelHelperTreeItem<ColoringRuleItem>(parent),
disabled_(disabled),
name_(name),
filter_(filter),
foreground_(foreground),
background_(background)
{
}
ColoringRuleItem::~ColoringRuleItem()
{
}
ColoringRuleItem::ColoringRuleItem(color_filter_t *colorf, ColoringRuleItem* parent)
: ModelHelperTreeItem<ColoringRuleItem>(parent),
disabled_(colorf->disabled),
name_(colorf->filter_name),
filter_(colorf->filter_text),
foreground_(ColorUtils::fromColorT(colorf->fg_color)),
background_(ColorUtils::fromColorT(colorf->bg_color))
{
}
ColoringRuleItem::ColoringRuleItem(const ColoringRuleItem& item)
: ModelHelperTreeItem<ColoringRuleItem>(item.parent_),
disabled_(item.disabled_),
name_(item.name_),
filter_(item.filter_),
foreground_(item.foreground_),
background_(item.background_)
{
}
ColoringRuleItem& ColoringRuleItem::operator=(ColoringRuleItem& rhs)
{
disabled_ = rhs.disabled_;
name_ = rhs.name_;
filter_ = rhs.filter_;
foreground_ = rhs.foreground_;
background_ = rhs.background_;
return *this;
}
// Callback for color_filters_clone.
void
color_filter_add_cb(color_filter_t *colorf, gpointer user_data)
{
ColoringRulesModel *model = (ColoringRulesModel*)user_data;
if (model == NULL)
return;
model->addColor(colorf);
}
ColoringRulesModel::ColoringRulesModel(QColor defaultForeground, QColor defaultBackground, QObject *parent) :
QAbstractItemModel(parent),
root_(new ColoringRuleItem(false, "", "", QColor(), QColor(), NULL)),
conversation_colors_(NULL),
defaultForeground_(defaultForeground),
defaultBackground_(defaultBackground)
{
color_filters_clone(this, color_filter_add_cb);
}
ColoringRulesModel::~ColoringRulesModel()
{
delete root_;
color_filter_list_delete(&conversation_colors_);
}
GSList *ColoringRulesModel::createColorFilterList()
{
GSList *cfl = NULL;
for (int row = 0; row < root_->childCount(); row++)
{
ColoringRuleItem* rule = root_->child(row);
if (rule == NULL)
continue;
color_t fg = ColorUtils::toColorT(rule->foreground_);
color_t bg = ColorUtils::toColorT(rule->background_);
color_filter_t *colorf = color_filter_new(rule->name_.toUtf8().constData(),
rule->filter_.toUtf8().constData(),
&bg, &fg, rule->disabled_);
cfl = g_slist_append(cfl, colorf);
}
return cfl;
}
void ColoringRulesModel::addColor(color_filter_t* colorf)
{
if (!colorf) return;
if (strstr(colorf->filter_name, CONVERSATION_COLOR_PREFIX) != NULL) {
conversation_colors_ = g_slist_append(conversation_colors_, colorf);
} else {
int count = root_->childCount();
beginInsertRows(QModelIndex(), count, count);
ColoringRuleItem* item = new ColoringRuleItem(colorf, root_);
root_->appendChild(item);
endInsertRows();
}
}
void ColoringRulesModel::addColor(bool disabled, QString filter, QColor foreground, QColor background)
{
//add rule to top of the list
beginInsertRows(QModelIndex(), 0, 0);
ColoringRuleItem* item = new ColoringRuleItem(disabled, tr("New coloring rule"), filter, foreground, background, root_);
root_->prependChild(item);
endInsertRows();
}
bool ColoringRulesModel::importColors(QString filename, QString& err)
{
bool success = true;
gchar* err_msg = NULL;
if (!color_filters_import(filename.toUtf8().constData(), this, &err_msg, color_filter_add_cb)) {
err = gchar_free_to_qstring(err_msg);
success = false;
}
return success;
}
bool ColoringRulesModel::exportColors(QString filename, QString& err)
{
GSList *cfl = createColorFilterList();
bool success = true;
gchar* err_msg = NULL;
if (!color_filters_export(filename.toUtf8().constData(), cfl, FALSE, &err_msg)) {
err = gchar_free_to_qstring(err_msg);
success = false;
}
color_filter_list_delete(&cfl);
return success;
}
bool ColoringRulesModel::writeColors(QString& err)
{
GSList *cfl = createColorFilterList();
bool success = true;
gchar* err_msg = NULL;
if (!color_filters_apply(conversation_colors_, cfl, &err_msg)) {
err = gchar_free_to_qstring(err_msg);
success = false;
}
if (!color_filters_write(cfl, &err_msg)) {
err = QString(tr("Unable to save coloring rules: %1").arg(g_strerror(errno)));
success = false;
g_free(err_msg);
}
color_filter_list_delete(&cfl);
return success;
}
bool ColoringRulesModel::insertRows(int row, int count, const QModelIndex& parent)
{
// sanity check insertion
if (row < 0)
return false;
beginInsertRows(parent, row, row+(count-1));
for (int i = row; i < row + count; i++)
{
ColoringRuleItem* item = new ColoringRuleItem(true, tr("New coloring rule"), "", defaultForeground_, defaultBackground_, root_);
root_->insertChild(i, item);
/* Automatically enable the new coloring rule */
setData(index(i, colName, parent), Qt::Checked, Qt::CheckStateRole);
}
endInsertRows();
return true;
}
bool ColoringRulesModel::removeRows(int row, int count, const QModelIndex& parent)
{
if (row < 0)
return false;
beginRemoveRows(parent, row, row+(count-1));
for (int i = row; i < row + count; i++)
{
root_->removeChild(row);
}
endRemoveRows();
return true;
}
bool ColoringRulesModel::copyRow(int dst_row, int src_row)
{
if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) {
return false;
}
ColoringRuleItem* src_item = root_->child(src_row);
if (src_item == NULL)
return false;
ColoringRuleItem* dst_item = new ColoringRuleItem(*src_item);
if (dst_item == NULL)
return false;
beginInsertRows(QModelIndex(), dst_row, dst_row);
root_->insertChild(dst_row, dst_item);
endInsertRows();
return true;
}
Qt::ItemFlags ColoringRulesModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
switch (index.column())
{
case colName:
flags |= (Qt::ItemIsUserCheckable|Qt::ItemIsEditable);
break;
case colFilter:
flags |= Qt::ItemIsEditable;
break;
}
if (index.isValid())
flags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
else
flags |= Qt::ItemIsDropEnabled;
return flags;
}
QVariant ColoringRulesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
ColoringRuleItem* rule = root_->child(index.row());
if (rule == NULL)
return QVariant();
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column())
{
case colName:
return rule->name_;
case colFilter:
return rule->filter_;
}
break;
case Qt::CheckStateRole:
switch(index.column())
{
case colName:
return rule->disabled_ ? Qt::Unchecked : Qt::Checked;
}
break;
case Qt::BackgroundRole:
return rule->background_;
case Qt::ForegroundRole:
return rule->foreground_;
}
return QVariant();
}
bool ColoringRulesModel::setData(const QModelIndex &dataIndex, const QVariant &value, int role)
{
if (!dataIndex.isValid())
return false;
if (data(dataIndex, role) == value) {
// Data appears unchanged, do not do additional checks.
return true;
}
ColoringRuleItem* rule = root_->child(dataIndex.row());
if (rule == NULL)
return false;
QModelIndex topLeft = dataIndex,
bottomRight = dataIndex;
switch (role)
{
case Qt::EditRole:
switch (dataIndex.column())
{
case colName:
rule->name_ = value.toString();
break;
case colFilter:
rule->filter_ = value.toString();
break;
default:
return false;
}
break;
case Qt::CheckStateRole:
switch (dataIndex.column())
{
case colName:
rule->disabled_ = (value.toInt() == Qt::Checked) ? false : true;
break;
default:
return false;
}
break;
case Qt::BackgroundRole:
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
if (!value.canConvert<QColor>())
#else
if (!value.canConvert(QVariant::Color))
#endif
return false;
rule->background_ = QColor(value.toString());
break;
case Qt::ForegroundRole:
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
if (!value.canConvert<QColor>())
#else
if (!value.canConvert(QVariant::Color))
#endif
return false;
rule->foreground_ = QColor(value.toString());
break;
case Qt::UserRole:
{
ColoringRuleItem* new_rule = VariantPointer<ColoringRuleItem>::asPtr(value);
*rule = *new_rule;
topLeft = index(dataIndex.row(), colName);
bottomRight = index(dataIndex.row(), colFilter);
break;
}
default:
return false;
}
QVector<int> roles;
roles << role;
emit dataChanged(topLeft, bottomRight, roles);
return true;
}
QVariant ColoringRulesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
return QVariant();
switch ((ColoringRulesColumn)section) {
case colName:
return tr("Name");
case colFilter:
return tr("Filter");
default:
break;
}
return QVariant();
}
Qt::DropActions ColoringRulesModel::supportedDropActions() const
{
return Qt::MoveAction | Qt::CopyAction;
}
QStringList ColoringRulesModel::mimeTypes() const
{
return QStringList() << WiresharkMimeData::ColoringRulesMimeType;
}
QMimeData* ColoringRulesModel::mimeData(const QModelIndexList &indexes) const
{
//if the list is empty, don't return an empty list
if (indexes.count() == 0)
return NULL;
QMimeData *mimeData = new QMimeData();
QJsonArray data;
foreach (const QModelIndex & index, indexes)
{
if (index.column() == 0)
{
ColoringRuleItem * item = root_->child(index.row());
QJsonObject entry;
entry["disabled"] = item->disabled_;
entry["name"] = item->name_;
entry["filter"] = item->filter_;
entry["foreground"] = QVariant::fromValue(item->foreground_).toString();
entry["background"] = QVariant::fromValue(item->background_).toString();
data.append(entry);
}
}
QJsonObject dataSet;
dataSet["coloringrules"] = data;
QByteArray encodedData = QJsonDocument(dataSet).toJson();
mimeData->setData(WiresharkMimeData::ColoringRulesMimeType, encodedData);
return mimeData;
}
bool ColoringRulesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
//clear any previous dragDrop information
dragDropRows_.clear();
if (action == Qt::IgnoreAction)
return true;
if (!data->hasFormat(WiresharkMimeData::ColoringRulesMimeType) || column > 0)
return false;
int beginRow;
if (row != -1)
beginRow = row;
else if (parent.isValid())
beginRow = parent.row();
else
beginRow = rowCount();
QList<QVariant> rules;
QJsonDocument encodedData = QJsonDocument::fromJson(data->data(WiresharkMimeData::ColoringRulesMimeType));
if (! encodedData.isObject() || ! encodedData.object().contains("coloringrules"))
return false;
QJsonArray dataArray = encodedData.object()["coloringrules"].toArray();
for (int datarow = 0; datarow < dataArray.count(); datarow++)
{
QJsonObject entry = dataArray.at(datarow).toObject();
if (! entry.contains("foreground") || ! entry.contains("background") || ! entry.contains("filter"))
continue;
QColor fgColor = entry["foreground"].toVariant().value<QColor>();
QColor bgColor = entry["background"].toVariant().value<QColor>();
ColoringRuleItem * item = new ColoringRuleItem(
entry["disabled"].toVariant().toBool(),
entry["name"].toString(),
entry["filter"].toString(),
fgColor,
bgColor,
root_);
rules.append(VariantPointer<ColoringRuleItem>::asQVariant(item));
}
insertRows(beginRow, static_cast<int>(rules.count()), QModelIndex());
for (int i = 0; i < rules.count(); i++) {
QModelIndex idx = index(beginRow, 0, QModelIndex());
setData(idx, rules[i], Qt::UserRole);
beginRow++;
}
return true;
}
QModelIndex ColoringRulesModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
ColoringRuleItem *parent_item, *child_item;
if (!parent.isValid())
parent_item = root_;
else
parent_item = static_cast<ColoringRuleItem*>(parent.internalPointer());
Q_ASSERT(parent_item);
child_item = parent_item->child(row);
if (child_item) {
return createIndex(row, column, child_item);
}
return QModelIndex();
}
QModelIndex ColoringRulesModel::parent(const QModelIndex& indexItem) const
{
if (!indexItem.isValid())
return QModelIndex();
ColoringRuleItem* item = static_cast<ColoringRuleItem*>(indexItem.internalPointer());
if (item != NULL) {
ColoringRuleItem* parent_item = item->parentItem();
if (parent_item != NULL) {
if (parent_item == root_)
return QModelIndex();
return createIndex(parent_item->row(), 0, parent_item);
}
}
return QModelIndex();
}
int ColoringRulesModel::rowCount(const QModelIndex& parent) const
{
ColoringRuleItem *parent_item;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parent_item = root_;
else
parent_item = static_cast<ColoringRuleItem*>(parent.internalPointer());
if (parent_item == NULL)
return 0;
return parent_item->childCount();
}
int ColoringRulesModel::columnCount(const QModelIndex&) const
{
return colColoringRulesMax;
}