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:
parent
df1c73d68f
commit
6820e3515a
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
Loading…
Reference in New Issue