Qt: Fix Column Preferences

The current implementation has some serious bugs. e.g. if an added
column is moved, existing columns disappear. Also, if new columns
are being added, existing columns may disappear. This moves the column
preferences to a Movel/View concept and implements necessary checks
to ensure, that such an issue cannot occur

Change-Id: I73810812180e2fbed4d2fe67316f190aba82c719
Reviewed-on: https://code.wireshark.org/review/34819
Petri-Dish: Roland Knall <rknall@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
This commit is contained in:
Roland Knall 2019-10-19 00:19:05 +02:00
parent df1c73d68f
commit 6820e3515a
6 changed files with 605 additions and 396 deletions

View File

@ -71,6 +71,7 @@ set(WIRESHARK_MODEL_HEADERS
models/cache_proxy_model.h
models/coloring_rules_delegate.h
models/coloring_rules_model.h
models/column_list_model.h
models/decode_as_delegate.h
models/decode_as_model.h
models/dissector_tables_model.h
@ -298,6 +299,7 @@ set(WIRESHARK_MODEL_SRCS
models/cache_proxy_model.cpp
models/coloring_rules_delegate.cpp
models/coloring_rules_model.cpp
models/column_list_model.cpp
models/decode_as_delegate.cpp
models/decode_as_model.cpp
models/dissector_tables_model.cpp

View File

@ -23,6 +23,7 @@
#include <ui_column_preferences_frame.h>
#include <ui/qt/widgets/syntax_line_edit.h>
#include <ui/qt/widgets/field_filter_edit.h>
#include <ui/qt/models/column_list_model.h>
#include "wireshark_application.h"
#include <QComboBox>
@ -30,51 +31,39 @@
#include <QLineEdit>
#include <QKeyEvent>
const int visible_col_ = 0;
const int title_col_ = 1;
const int type_col_ = 2;
const int custom_fields_col_ = 3;
const int custom_occurrence_col_ = 4;
ColumnPreferencesFrame::ColumnPreferencesFrame(QWidget *parent) :
QFrame(parent),
ui(new Ui::ColumnPreferencesFrame),
cur_column_(0),
cur_line_edit_(NULL),
cur_combo_box_(NULL),
saved_combo_idx_(0),
saved_custom_combo_idx_(-1)
ui(new Ui::ColumnPreferencesFrame)
{
ui->setupUi(this);
int one_em = ui->columnTreeWidget->fontMetrics().height();
ui->columnTreeWidget->setColumnWidth(custom_fields_col_, one_em * 10);
ui->columnTreeWidget->setColumnWidth(custom_occurrence_col_, one_em * 5);
model_ = new ColumnListModel();
ui->columnTreeWidget->setMinimumWidth(one_em * 20);
ui->columnTreeWidget->setMinimumHeight(one_em * 12);
int one_em = ui->columnTreeView->fontMetrics().height();
ui->columnTreeView->setColumnWidth(ColumnListModel::COL_FIELDS, one_em * 10);
ui->columnTreeView->setColumnWidth(ColumnListModel::COL_OCCURRENCE, one_em * 5);
ui->columnTreeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->columnTreeWidget->setDragEnabled(true);
ui->columnTreeWidget->viewport()->setAcceptDrops(true);
ui->columnTreeWidget->setDropIndicatorShown(true);
ui->columnTreeWidget->setDragDropMode(QAbstractItemView::InternalMove);
ui->columnTreeView->setMinimumWidth(one_em * 20);
ui->columnTreeView->setMinimumHeight(one_em * 12);
ui->columnTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->columnTreeView->setDragEnabled(true);
ui->columnTreeView->viewport()->setAcceptDrops(true);
ui->columnTreeView->setDropIndicatorShown(true);
ui->columnTreeView->setDragDropMode(QAbstractItemView::InternalMove);
ui->newToolButton->setStockIcon("list-add");
ui->deleteToolButton->setStockIcon("list-remove");
for (GList *cur = g_list_first(prefs.col_list); cur != NULL && cur->data != NULL; cur = cur->next) {
fmt_data *cfmt = (fmt_data *) cur->data;
addColumn(cfmt->visible, cfmt->title, cfmt->fmt, cfmt->custom_fields, cfmt->custom_occurrence);
}
ui->columnTreeView->setModel(model_);
ui->columnTreeView->setItemDelegate(new ColumnTypeDelegate());
connect(ui->columnTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(updateWidgets()));
ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_DISPLAYED);
ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_TITLE);
ui->columnTreeView->resizeColumnToContents(ColumnListModel::COL_TYPE);
if (prefs.num_cols > 0) {
ui->columnTreeWidget->topLevelItem(0)->setSelected(true);
}
updateWidgets();
connect(ui->columnTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &ColumnPreferencesFrame::selectionChanged);
}
ColumnPreferencesFrame::~ColumnPreferencesFrame()
@ -84,332 +73,28 @@ ColumnPreferencesFrame::~ColumnPreferencesFrame()
void ColumnPreferencesFrame::unstash()
{
GList *new_col_list = NULL;
bool changed = false;
QTreeWidgetItemIterator it(ui->columnTreeWidget);
while (*it) {
fmt_data *cfmt = g_new0(fmt_data, 1);
cfmt->title = qstring_strdup((*it)->text(title_col_));
cfmt->fmt = (*it)->data(type_col_, Qt::UserRole).value<int>();
cfmt->visible = (*it)->checkState(visible_col_) == Qt::Checked ? TRUE : FALSE;
cfmt->resolved = TRUE;
if (cfmt->fmt == COL_CUSTOM) {
bool ok;
int occurrence = (*it)->text(custom_occurrence_col_).toInt(&ok);
cfmt->custom_fields = qstring_strdup((*it)->text(custom_fields_col_));
cfmt->custom_occurrence = ok ? occurrence : 0;
}
if (prefs.col_list == NULL) {
changed = true;
} else {
fmt_data *old_cfmt = (fmt_data *) prefs.col_list->data;
if (!old_cfmt ||
g_strcmp0(old_cfmt->title, cfmt->title) != 0 ||
old_cfmt->fmt != cfmt->fmt ||
old_cfmt->visible != cfmt->visible ||
(old_cfmt->fmt == COL_CUSTOM && (
g_strcmp0(old_cfmt->custom_fields, cfmt->custom_fields) != 0 ||
old_cfmt->custom_occurrence != cfmt->custom_occurrence))) {
changed = true;
}
column_prefs_remove_link(prefs.col_list);
}
new_col_list = g_list_append(new_col_list, cfmt);
++it;
}
while (prefs.col_list) {
changed = true;
column_prefs_remove_link(prefs.col_list);
}
prefs.col_list = new_col_list;
if (changed) {
wsApp->emitAppSignal(WiresharkApplication::ColumnsChanged);
}
}
void ColumnPreferencesFrame::keyPressEvent(QKeyEvent *evt)
{
if (cur_line_edit_ && cur_line_edit_->hasFocus()) {
int new_idx = COL_CUSTOM;
switch (evt->key()) {
case Qt::Key_Escape:
cur_line_edit_->setText(saved_col_string_);
new_idx = saved_combo_idx_;
/* Fall Through */
case Qt::Key_Enter:
case Qt::Key_Return:
switch (cur_column_) {
case title_col_:
columnTitleEditingFinished();
break;
case custom_fields_col_:
customFieldsEditingFinished();
columnTypeCurrentIndexChanged(new_idx);
break;
case custom_occurrence_col_:
customOccurrenceEditingFinished();
columnTypeCurrentIndexChanged(new_idx);
break;
default:
break;
}
delete cur_line_edit_;
return;
default:
break;
}
} else if (cur_combo_box_ && cur_combo_box_->hasFocus()) {
switch (evt->key()) {
case Qt::Key_Escape:
cur_combo_box_->setCurrentIndex(saved_combo_idx_);
/* Fall Through */
case Qt::Key_Enter:
case Qt::Key_Return:
// XXX The combo box eats enter and return
columnTypeCurrentIndexChanged(cur_combo_box_->currentIndex());
delete cur_combo_box_;
return;
default:
break;
}
}
QFrame::keyPressEvent(evt);
}
void ColumnPreferencesFrame::addColumn(bool visible, const char *title, int fmt, const char *custom_fields, int custom_occurrence)
{
QTreeWidgetItem *item = new QTreeWidgetItem(ui->columnTreeWidget);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setFlags(item->flags() & ~(Qt::ItemIsDropEnabled));
item->setCheckState(visible_col_, visible ? Qt::Checked : Qt::Unchecked);
item->setText(title_col_, title);
item->setText(type_col_, col_format_desc(fmt));
item->setData(type_col_, Qt::UserRole, QVariant(fmt));
if (fmt == COL_CUSTOM) {
item->setText(custom_fields_col_, custom_fields);
item->setText(custom_occurrence_col_, QString::number(custom_occurrence));
}
updateWidgets();
}
void ColumnPreferencesFrame::updateWidgets()
{
ui->columnTreeWidget->resizeColumnToContents(visible_col_);
ui->columnTreeWidget->resizeColumnToContents(title_col_);
ui->columnTreeWidget->resizeColumnToContents(type_col_);
ui->deleteToolButton->setEnabled(ui->columnTreeWidget->selectedItems().count() > 0 && ui->columnTreeWidget->topLevelItemCount() > 1);
}
void ColumnPreferencesFrame::on_columnTreeWidget_currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *previous)
{
if (previous) {
if (ui->columnTreeWidget->itemWidget(previous, title_col_)) {
ui->columnTreeWidget->removeItemWidget(previous, title_col_);
}
if (ui->columnTreeWidget->itemWidget(previous, type_col_)) {
ui->columnTreeWidget->removeItemWidget(previous, type_col_);
previous->setText(type_col_, col_format_desc(previous->data(type_col_, Qt::UserRole).toInt()));
}
if (ui->columnTreeWidget->itemWidget(previous, custom_fields_col_)) {
ui->columnTreeWidget->removeItemWidget(previous, custom_fields_col_);
}
if (ui->columnTreeWidget->itemWidget(previous, custom_occurrence_col_)) {
ui->columnTreeWidget->removeItemWidget(previous, custom_occurrence_col_);
}
// If the custom column auto-changed the Column type, change it back if
// there isn't any text in the field columns
if ((previous->text(custom_fields_col_) == "") &&
(previous->text(custom_occurrence_col_) == "") &&
(saved_custom_combo_idx_ >= 0))
{
previous->setText(type_col_, col_format_desc(saved_custom_combo_idx_));
previous->setData(type_col_, Qt::UserRole, QVariant(saved_custom_combo_idx_));
saved_custom_combo_idx_ = -1;
}
}
updateWidgets();
}
void ColumnPreferencesFrame::on_columnTreeWidget_itemActivated(QTreeWidgetItem *item, int column)
{
if (!item || cur_line_edit_ || cur_combo_box_) return;
QTreeWidget *ctw = ui->columnTreeWidget;
QWidget *editor = NULL;
cur_column_ = column;
saved_combo_idx_ = item->data(type_col_, Qt::UserRole).toInt();
switch (column) {
case title_col_:
{
cur_line_edit_ = new QLineEdit();
cur_column_ = column;
saved_col_string_ = item->text(title_col_);
connect(cur_line_edit_, SIGNAL(editingFinished()), this, SLOT(columnTitleEditingFinished()));
editor = cur_line_edit_;
break;
}
case type_col_:
{
cur_combo_box_ = new QComboBox();
for (int i = 0; i < NUM_COL_FMTS; i++) {
cur_combo_box_->addItem(col_format_desc(i), QVariant(i));
if (i == saved_combo_idx_) {
cur_combo_box_->setCurrentIndex(i);
}
}
connect(cur_combo_box_, SIGNAL(currentIndexChanged(int)), this, SLOT(columnTypeCurrentIndexChanged(int)));
editor = cur_combo_box_;
break;
}
case custom_fields_col_:
{
FieldFilterEdit *field_filter_edit = new FieldFilterEdit();
saved_col_string_ = item->text(custom_fields_col_);
connect(field_filter_edit, SIGNAL(textChanged(QString)),
field_filter_edit, SLOT(checkCustomColumn(QString)));
connect(field_filter_edit, SIGNAL(editingFinished()), this, SLOT(customFieldsEditingFinished()));
field_filter_edit->setFixedWidth(ctw->columnWidth(custom_fields_col_));
editor = cur_line_edit_ = field_filter_edit;
//Save off the current column type in case it needs to be restored
if ((item->text(custom_fields_col_) == "") && (item->text(custom_occurrence_col_) == "")) {
saved_custom_combo_idx_ = item->data(type_col_, Qt::UserRole).toInt();
}
item->setText(type_col_, col_format_desc(COL_CUSTOM));
item->setData(type_col_, Qt::UserRole, QVariant(COL_CUSTOM));
break;
}
case custom_occurrence_col_:
{
SyntaxLineEdit *syntax_edit = new SyntaxLineEdit();
saved_col_string_ = item->text(custom_occurrence_col_);
connect(syntax_edit, SIGNAL(textChanged(QString)),
syntax_edit, SLOT(checkInteger(QString)));
connect(syntax_edit, SIGNAL(editingFinished()), this, SLOT(customOccurrenceEditingFinished()));
syntax_edit->setFixedWidth(ctw->columnWidth(custom_occurrence_col_));
editor = cur_line_edit_ = syntax_edit;
//Save off the current column type in case it needs to be restored
if ((item->text(custom_fields_col_) == "") && (item->text(custom_occurrence_col_) == "")) {
saved_custom_combo_idx_ = item->data(type_col_, Qt::UserRole).toInt();
}
item->setText(type_col_, col_format_desc(COL_CUSTOM));
item->setData(type_col_, Qt::UserRole, QVariant(COL_CUSTOM));
break;
}
default:
return;
}
if (cur_line_edit_) {
cur_line_edit_->setText(saved_col_string_);
cur_line_edit_->selectAll();
connect(cur_line_edit_, SIGNAL(destroyed()), this, SLOT(lineEditDestroyed()));
}
if (cur_combo_box_) {
connect(cur_combo_box_, SIGNAL(destroyed()), this, SLOT(comboDestroyed()));
}
if (editor) {
QFrame *edit_frame = new QFrame();
QHBoxLayout *hb = new QHBoxLayout();
QSpacerItem *spacer = new QSpacerItem(5, 10);
hb->addWidget(editor, 0);
hb->addSpacerItem(spacer);
hb->setStretch(1, 1);
hb->setContentsMargins(0, 0, 0, 0);
edit_frame->setLineWidth(0);
edit_frame->setFrameStyle(QFrame::NoFrame);
// The documentation suggests setting autoFillbackground. That looks silly
// so we clear the item text instead.
item->setText(cur_column_, "");
edit_frame->setLayout(hb);
ui->columnTreeWidget->setItemWidget(item, cur_column_, edit_frame);
editor->setFocus();
}
}
void ColumnPreferencesFrame::lineEditDestroyed()
{
cur_line_edit_ = NULL;
}
void ColumnPreferencesFrame::comboDestroyed()
{
cur_combo_box_ = NULL;
}
void ColumnPreferencesFrame::columnTitleEditingFinished()
{
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();
if (!cur_line_edit_ || !item) return;
item->setText(title_col_, cur_line_edit_->text());
ui->columnTreeWidget->removeItemWidget(item, title_col_);
}
void ColumnPreferencesFrame::columnTypeCurrentIndexChanged(int index)
{
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();
if (!item || index < 0) return;
item->setData(type_col_, Qt::UserRole, QVariant(index));
item->setText(type_col_, col_format_desc(index));
if (index != COL_CUSTOM) {
item->setText(custom_fields_col_, "");
item->setText(custom_occurrence_col_, "");
}
}
void ColumnPreferencesFrame::customFieldsEditingFinished()
{
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();
if (!cur_line_edit_ || !item) return;
item->setText(custom_fields_col_, cur_line_edit_->text());
ui->columnTreeWidget->removeItemWidget(item, custom_fields_col_);
}
void ColumnPreferencesFrame::customOccurrenceEditingFinished()
{
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();
if (!cur_line_edit_ || !item) return;
item->setText(custom_occurrence_col_, cur_line_edit_->text());
ui->columnTreeWidget->removeItemWidget(item, custom_occurrence_col_);
model_->saveColumns();
wsApp->emitAppSignal(WiresharkApplication::ColumnsChanged);
}
void ColumnPreferencesFrame::on_newToolButton_clicked()
{
addColumn(true, "New Column", COL_NUMBER, NULL, 0); //TODO : Fix Translate
model_->addEntry();
}
void ColumnPreferencesFrame::on_deleteToolButton_clicked()
{
if (ui->columnTreeWidget->topLevelItemCount() < 2) return;
QTreeWidgetItem *item = ui->columnTreeWidget->currentItem();
if (item) {
ui->columnTreeWidget->invisibleRootItem()->removeChild(item);
if ( ui->columnTreeView->selectionModel()->selectedIndexes().count() > 0 )
{
QModelIndex selIndex = ui->columnTreeView->selectionModel()->selectedIndexes().at(0);
model_->deleteEntry(selIndex.row());
}
}
updateWidgets();
void ColumnPreferencesFrame::selectionChanged(const QItemSelection &/*selected*/,
const QItemSelection &/*deselected*/)
{
ui->deleteToolButton->setEnabled( ui->columnTreeView->selectionModel()->selectedIndexes().count() > 0 );
}
/*

View File

@ -15,6 +15,8 @@
class QComboBox;
class QLineEdit;
class QTreeWidgetItem;
class ColumnListModel;
class QItemSelection;
namespace Ui {
class ColumnPreferencesFrame;
@ -30,31 +32,12 @@ public:
void unstash();
protected:
void keyPressEvent(QKeyEvent *evt);
private:
Ui::ColumnPreferencesFrame *ui;
int cur_column_;
QLineEdit *cur_line_edit_;
QString saved_col_string_;
QComboBox *cur_combo_box_;
int saved_combo_idx_;
int saved_custom_combo_idx_;
void addColumn(bool visible, const char *title, int fmt, const char *custom_fields, int custom_occurrence);
ColumnListModel * model_;
private slots:
void updateWidgets(void);
void on_columnTreeWidget_currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *previous);
void on_columnTreeWidget_itemActivated(QTreeWidgetItem *item, int column);
void lineEditDestroyed();
void comboDestroyed();
void columnTitleEditingFinished();
void columnTypeCurrentIndexChanged(int index);
void customFieldsEditingFinished();
void customOccurrenceEditingFinished();
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void on_newToolButton_clicked();
void on_deleteToolButton_clicked();
};

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>550</width>
<height>350</height>
<height>456</height>
</rect>
</property>
<property name="sizePolicy">
@ -24,32 +24,22 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="columnTreeWidget">
<column>
<property name="text">
<string>Displayed</string>
</property>
</column>
<column>
<property name="text">
<string>Title</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Fields</string>
</property>
</column>
<column>
<property name="text">
<string>Field Occurrence</string>
</property>
</column>
<widget class="QTreeView" name="columnTreeView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
</widget>
</item>
<item>

View File

@ -0,0 +1,458 @@
/* column_list_models.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <ui/qt/models/column_list_model.h>
#include <ui/qt/utils/qt_ui_utils.h>
#include <ui/qt/widgets/field_filter_edit.h>
#include <ui/qt/widgets/syntax_line_edit.h>
#include <glib.h>
#include <epan/column-info.h>
#include <epan/column.h>
#include <epan/prefs.h>
#include <epan/proto.h>
#include <ui/preference_utils.h>
#include <QLineEdit>
#include <QStringList>
#include <QComboBox>
struct ListElement
{
QString title;
QString customFields;
int nr;
int type;
int originalType;
int occurrence;
bool displayed;
bool changed;
};
static QList<ListElement> store_;
ColumnTypeDelegate::ColumnTypeDelegate(QObject * parent) :
QStyledItemDelegate(parent)
{}
QWidget *ColumnTypeDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if ( index.column() == ColumnListModel::COL_TYPE )
{
QComboBox *editor = new QComboBox(parent);
for (int i = 0; i < NUM_COL_FMTS; i++)
{
editor->addItem(col_format_desc(i), QVariant(i));
if ( i == index.data().toInt() )
editor->setCurrentIndex(i);
}
editor->setFrame(false);
return editor;
}
else if ( index.column() == ColumnListModel::COL_FIELDS )
{
FieldFilterEdit * editor = new FieldFilterEdit(parent);
editor->setText(index.data().toString());
return editor;
}
else if ( index.column() == ColumnListModel::COL_OCCURRENCE )
{
SyntaxLineEdit * editor = new SyntaxLineEdit(parent);
connect(editor, &SyntaxLineEdit::textChanged, editor, &SyntaxLineEdit::checkInteger);
editor->setText(index.data().toString());
return editor;
}
return QStyledItemDelegate::createEditor(parent, option, index);
}
void ColumnTypeDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QVariant data = index.model()->data(index);
if ( index.column() == ColumnListModel::COL_TYPE )
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
comboBox->setCurrentText(data.toString());
}
else if ( index.column() == ColumnListModel::COL_FIELDS )
{
if ( qobject_cast<FieldFilterEdit *>(editor) )
qobject_cast<FieldFilterEdit *>(editor)->setText(data.toString());
}
else if ( index.column() == ColumnListModel::COL_OCCURRENCE )
{
if ( qobject_cast<SyntaxLineEdit *>(editor) )
qobject_cast<SyntaxLineEdit *>(editor)->setText(data.toString());
}
else
{
if ( qobject_cast<QLineEdit *>(editor) )
qobject_cast<QLineEdit *>(editor)->setText(data.toString());
}
}
void ColumnTypeDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
if ( index.column() == ColumnListModel::COL_TYPE )
{
QComboBox *comboBox = static_cast<QComboBox*>(editor);
bool ok = false;
int value = comboBox->currentData().toInt(&ok);
if ( ok )
model->setData(index, value, Qt::EditRole);
}
else if ( index.column() == ColumnListModel::COL_FIELDS )
{
FieldFilterEdit * ffe = qobject_cast<FieldFilterEdit *>(editor);
if ( ffe && ffe->checkFilter() )
{
QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE);
model->setData(typeIndex, COL_CUSTOM, Qt::EditRole);
model->setData(index, ffe->text(), Qt::EditRole);
}
else
ffe->setText(index.data().toString());
if ( index.data().toString().length() == 0 )
{
QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE);
model->setData(typeIndex, index.data(ColumnListModel::OriginalType).toInt(), Qt::EditRole);
}
}
else if ( index.column() == ColumnListModel::COL_OCCURRENCE )
{
SyntaxLineEdit * sle = qobject_cast<SyntaxLineEdit *>(editor);
bool ok = false;
if ( sle )
{
sle->checkInteger(index.data().toString());
if ( sle->syntaxState() == SyntaxLineEdit::Valid )
ok = true;
}
if ( ok )
{
QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE);
model->setData(typeIndex, COL_CUSTOM, Qt::EditRole);
model->setData(index, sle->text(), Qt::EditRole);
}
else
sle->setText(index.data().toString());
if ( index.data().toString().length() == 0 )
{
QModelIndex typeIndex = index.sibling(index.row(), ColumnListModel::COL_TYPE);
model->setData(typeIndex, index.data(ColumnListModel::OriginalType).toInt(), Qt::EditRole);
}
}
else
QStyledItemDelegate::setModelData(editor, model, index);
}
void ColumnTypeDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option,
const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
ColumnListModel::ColumnListModel(QObject * parent):
QAbstractTableModel(parent)
{
populate();
}
QVariant ColumnListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ( section > ColumnListModel::COL_OCCURRENCE || orientation != Qt::Horizontal ||
role != Qt::DisplayRole )
return QVariant();
return headerTitle(section);
}
int ColumnListModel::rowCount(const QModelIndex &/*parent*/) const
{
return store_.count();
}
int ColumnListModel::columnCount(const QModelIndex &/*parent*/) const
{
return ColumnListModel::COL_OCCURRENCE + 1;
}
QString ColumnListModel::headerTitle(int section) const
{
switch ( section )
{
case ColumnListModel::COL_DISPLAYED:
return tr("Displayed");
case ColumnListModel::COL_TITLE:
return tr("Title");
case ColumnListModel::COL_TYPE:
return tr("Type");
case ColumnListModel::COL_FIELDS:
return tr("Fields");
case ColumnListModel::COL_OCCURRENCE:
return tr("Field Occurence");
}
return QString();
}
void ColumnListModel::populate()
{
store_.clear();
int nr = 0;
for (GList *cur = g_list_first(prefs.col_list); cur != NULL && cur->data != NULL; cur = cur->next) {
fmt_data *cfmt = (fmt_data *) cur->data;
ListElement ne;
ne.nr = nr;
ne.displayed = cfmt->visible;
ne.title = cfmt->title;
ne.type = ne.originalType = cfmt->fmt;
ne.customFields = cfmt->custom_fields;
ne.occurrence = cfmt->custom_occurrence;
nr++;
store_ << ne;
}
}
QVariant ColumnListModel::data(const QModelIndex &index, int role) const
{
if ( ! index.isValid() || index.column() >= store_.count() )
return QVariant();
ListElement ne = store_.at(index.row());
if ( role == Qt::DisplayRole )
{
switch (index.column())
{
case COL_DISPLAYED:
return QVariant();
case ColumnListModel::COL_TITLE:
return ne.title;
case ColumnListModel::COL_TYPE:
return col_format_desc(ne.type);
case ColumnListModel::COL_FIELDS:
return ne.customFields;
case ColumnListModel::COL_OCCURRENCE:
return ne.customFields.length() > 0 ? qVariantFromValue(ne.occurrence) : QVariant();
}
}
else if ( role == Qt::CheckStateRole )
{
if ( index.column() == COL_DISPLAYED )
{
return ne.displayed ? Qt::Checked : Qt::Unchecked;
}
}
else if ( role == OriginalType )
{
return qVariantFromValue(ne.originalType);
}
return QVariant();
}
Qt::ItemFlags ColumnListModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index);
if ( index.isValid() && index.row() < store_.count() )
{
ListElement ne = store_.at(index.row());
Qt::ItemFlags flags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
if ( index.column() == COL_DISPLAYED )
flags |= Qt::ItemIsUserCheckable;
flags |= Qt::ItemIsEditable;
return flags;
}
else
return Qt::ItemIsDropEnabled | defaultFlags;
}
QMimeData *ColumnListModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData;
int row = -1;
if ( indexes.count() > 0 )
row = indexes.at(0).row();
mimeData->setText(QString::number(row));
return mimeData;
}
bool ColumnListModel::canDropMimeData(const QMimeData *data,
Qt::DropAction /* action */, int /* row */, int /* column */, const QModelIndex &parent) const
{
if (parent.isValid() || data->text().isEmpty() )
return false;
return true;
}
bool ColumnListModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
int moveTo;
if (!canDropMimeData(data, action, row, column, parent))
return false;
if (action == Qt::IgnoreAction || parent.isValid() )
return true;
if (row != -1)
moveTo = row;
else
moveTo = rowCount(QModelIndex());
bool ok = false;
int moveFrom = data->text().toInt(&ok);
if ( ! ok )
return false;
if ( moveFrom < moveTo )
moveTo = moveTo - 1;
if ( moveTo >= store_.count() )
moveTo = store_.count() - 1;
emit beginResetModel();
store_.move(moveFrom, moveTo);
emit endResetModel();
return true;
}
Qt::DropActions ColumnListModel::supportedDropActions() const
{
return Qt::MoveAction;
}
bool ColumnListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if ( !index.isValid() || ! value.isValid() )
return false;
bool change = false;
if ( role == Qt::CheckStateRole && index.column() == ColumnListModel::COL_DISPLAYED )
{
store_[index.row()].displayed = value.toInt() == Qt::Checked ? true : false;
change = true;
}
else if ( index.column() == ColumnListModel::COL_TYPE )
{
bool ok = false;
int val = value.toInt(&ok);
if ( ok )
store_[index.row()].type = val;
}
else if ( index.column() == ColumnListModel::COL_TITLE )
{
store_[index.row()].title = value.toString();
}
else if ( index.column() == ColumnListModel::COL_FIELDS )
{
store_[index.row()].customFields = value.toString();
}
else if ( index.column() == ColumnListModel::COL_OCCURRENCE )
{
bool ok = false;
int val = value.toInt(&ok);
if ( ok )
store_[index.row()].occurrence = val;
}
if ( change )
emit dataChanged(index, index);
return change;
}
void ColumnListModel::saveColumns()
{
GList *new_col_list = Q_NULLPTR;
for ( int row = 0; row < store_.count(); row++ )
{
fmt_data * cfmt = new fmt_data();
ListElement elem = store_.at(row);
cfmt->title = qstring_strdup(elem.title);
cfmt->visible = elem.displayed;
cfmt->fmt = elem.type;
cfmt->resolved = TRUE;
if ( cfmt->fmt == COL_CUSTOM )
{
cfmt->custom_fields = qstring_strdup(elem.customFields);
cfmt->custom_occurrence = elem.occurrence;
}
new_col_list = g_list_append(new_col_list, cfmt);
}
while (prefs.col_list)
column_prefs_remove_link(prefs.col_list);
prefs.col_list = new_col_list;
}
void ColumnListModel::addEntry()
{
emit beginInsertRows(QModelIndex(), rowCount(), rowCount());
ListElement elem;
elem.nr = rowCount();
elem.title = tr("New Column");
elem.displayed = true;
elem.type = elem.originalType = COL_NUMBER;
elem.occurrence = 0;
elem.customFields = QString();
store_ << elem;
emit endInsertRows();
}
void ColumnListModel::deleteEntry(int row)
{
emit beginRemoveRows(QModelIndex(), row, row);
store_.removeAt(row);
emit endRemoveRows();
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -0,0 +1,91 @@
/* column_list_model.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef COLUMN_LIST_MODELS_H
#define COLUMN_LIST_MODELS_H
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <QStyledItemDelegate>
#include <QMimeData>
class ColumnTypeDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ColumnTypeDelegate(QObject * parent = Q_NULLPTR);
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
};
class ColumnListModel : public QAbstractTableModel
{
Q_OBJECT
public:
ColumnListModel(QObject * parent = Q_NULLPTR);
enum {
COL_DISPLAYED,
COL_TITLE,
COL_TYPE,
COL_FIELDS,
COL_OCCURRENCE
} Columns;
enum {
OriginalType = Qt::UserRole
} UserTypes;
void saveColumns();
void addEntry();
void deleteEntry(int row);
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
virtual Qt::DropActions supportedDropActions() const;
virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const;
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
private:
QString headerTitle(int section) const;
void populate();
};
#endif // COLUMN_LIST_MODELS_H
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/