diff options
author | Michael Mann <mmann78@netscape.net> | 2017-12-29 11:23:07 -0500 |
---|---|---|
committer | Roland Knall <rknall@gmail.com> | 2018-01-04 21:20:59 +0000 |
commit | d239da264c5d0ca2ecfd609ae3eccced939a4c46 (patch) | |
tree | aa5b490fa2d9c1b4b6899a411bbcca03ccc0659d /ui/qt/models | |
parent | a79b7986cddf40a8ade57638916951323fff581e (diff) |
Convert preference dialog to use more models.
Convert Advanced view and Modules view to use a single base model,
loading the preferences once and then filter and display what they
need with QSortFilterProxyModel derived classes.
Convert the PreferencePane "types" to just strings. This allows
a more straightforward relationship between the "special" modules
that need custom widgets for preference manipulation and it also
removes dependency on preferences_dialog.h for many files.
Change-Id: I091deb3061564aa4d1564e9ca1c792715961b083
Reviewed-on: https://code.wireshark.org/review/25134
Reviewed-by: Michael Mann <mmann78@netscape.net>
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
Diffstat (limited to 'ui/qt/models')
-rw-r--r-- | ui/qt/models/pref_delegate.cpp | 270 | ||||
-rw-r--r-- | ui/qt/models/pref_delegate.h | 54 | ||||
-rw-r--r-- | ui/qt/models/pref_models.cpp | 768 | ||||
-rw-r--r-- | ui/qt/models/pref_models.h | 173 |
4 files changed, 1265 insertions, 0 deletions
diff --git a/ui/qt/models/pref_delegate.cpp b/ui/qt/models/pref_delegate.cpp new file mode 100644 index 0000000000..8a4b83a68f --- /dev/null +++ b/ui/qt/models/pref_delegate.cpp @@ -0,0 +1,270 @@ +/* pref_delegate.cpp + * Delegates for editing prefereneces. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <ui/qt/models/pref_delegate.h> +#include <epan/prefs-int.h> + +#include <QComboBox> +#include <QFileDialog> +#include <QLineEdit> +#include <QColorDialog> + +#include "uat_dialog.h" +#include "wireshark_application.h" + +#include <ui/qt/widgets/editor_file_dialog.h> + +RangeSyntaxLineEdit::RangeSyntaxLineEdit(QWidget *parent) + : SyntaxLineEdit(parent), + maxRange_(0xFFFFFFFF) +{ + connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkRange(QString))); +} + +void RangeSyntaxLineEdit::checkRange(QString range) +{ + if (range.isEmpty()) { + setSyntaxState(SyntaxLineEdit::Empty); + return; + } + + range_t *newrange; + convert_ret_t ret = range_convert_str(NULL, &newrange, range.toUtf8().constData(), maxRange_); + + if (ret == CVT_NO_ERROR) { + setSyntaxState(SyntaxLineEdit::Valid); + wmem_free(NULL, newrange); + } else { + setSyntaxState(SyntaxLineEdit::Invalid); + } +} + + + + +AdvancedPrefDelegate::AdvancedPrefDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +PrefsItem* AdvancedPrefDelegate::indexToPref(const QModelIndex &index) const +{ + const QVariant v = index.model()->data(index, Qt::UserRole); + return VariantPointer<PrefsItem>::asPtr(v); +} + +QWidget *AdvancedPrefDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + PrefsItem* pref; + QString filename; + + switch(index.column()) + { + case AdvancedPrefsModel::colName: + case AdvancedPrefsModel::colStatus: + case AdvancedPrefsModel::colType: + //If user clicks on any of these columns, reset preference back to default + //There is no need to launch an editor + ((QAbstractItemModel*)index.model())->setData(index, QVariant(), Qt::EditRole); + break; + case AdvancedPrefsModel::colValue: + pref = indexToPref(index); + switch(pref->getPrefType()) + { + case PREF_DECODE_AS_UINT: + case PREF_UINT: + { + QLineEdit* editor = new QLineEdit(parent); +#if 0 + //XXX - Do we want some help handling formatting the number? + editor->setInputMask("0000000009;"); +#endif + return editor; + } + case PREF_BOOL: + //Setting any non-NULL value will invert boolean value + ((QAbstractItemModel*)index.model())->setData(index, QString("BOOL"), Qt::EditRole); + break; + case PREF_ENUM: + { + QComboBox* editor = new QComboBox(parent); + return editor; + } + case PREF_STRING: + { + //Separated from UINT in case formatting needs to be applied to UINT + QLineEdit* editor = new QLineEdit(parent); + return editor; + } + case PREF_DECODE_AS_RANGE: + case PREF_RANGE: + { + RangeSyntaxLineEdit *editor = new RangeSyntaxLineEdit(parent); + return editor; + } + case PREF_UAT: + { + if (pref->getPrefGUIType() == GUI_ALL || pref->getPrefGUIType() == GUI_QT) { + UatDialog uat_dlg(parent, prefs_get_uat_value(pref->getPref())); + uat_dlg.exec(); + } + } + break; + case PREF_SAVE_FILENAME: + filename = QFileDialog::getSaveFileName(parent, wsApp->windowTitleString(prefs_get_title(pref->getPref())), + index.model()->data(index, Qt::DisplayRole).toString()); + if (!filename.isEmpty()) { + ((QAbstractItemModel*)index.model())->setData(index, QDir::toNativeSeparators(filename), Qt::EditRole); + } + break; + case PREF_OPEN_FILENAME: + filename = QFileDialog::getOpenFileName(parent, wsApp->windowTitleString(prefs_get_title(pref->getPref())), + index.model()->data(index, Qt::DisplayRole).toString()); + if (!filename.isEmpty()) { + ((QAbstractItemModel*)index.model())->setData(index, QDir::toNativeSeparators(filename), Qt::EditRole); + } + break; + case PREF_DIRNAME: + filename = QFileDialog::getExistingDirectory(parent, wsApp->windowTitleString(prefs_get_title(pref->getPref())), + index.model()->data(index, Qt::DisplayRole).toString()); + if (!filename.isEmpty()) { + ((QAbstractItemModel*)index.model())->setData(index, QDir::toNativeSeparators(filename), Qt::EditRole); + } + break; + case PREF_COLOR: + { + QColorDialog color_dlg; + color_t color = *prefs_get_color_value(pref->getPref(), pref_stashed); + + color_dlg.setCurrentColor(QColor( + color.red >> 8, + color.green >> 8, + color.blue >> 8 + )); + if (color_dlg.exec() == QDialog::Accepted) { + ((QAbstractItemModel*)index.model())->setData(index, color_dlg.currentColor().name(), Qt::EditRole); + } + break; + } + + } + break; + } + + return 0; +} + +void AdvancedPrefDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + PrefsItem* pref = indexToPref(index); + + switch(pref->getPrefType()) + { + case PREF_DECODE_AS_UINT: + case PREF_UINT: + { + QLineEdit* line = static_cast<QLineEdit*>(editor); + line->setText(index.model()->data(index, Qt::DisplayRole).toString()); + } + break; + case PREF_ENUM: + { + QComboBox* combo = static_cast<QComboBox*>(editor); + const enum_val_t *ev; + PrefsItem* pref = VariantPointer<PrefsItem>::asPtr(index.model()->data(index, Qt::UserRole)); + for (ev = prefs_get_enumvals(pref->getPref()); ev && ev->description; ev++) { + combo->addItem(ev->description, QVariant(ev->value)); + if (prefs_get_enum_value(pref->getPref(), pref_stashed) == ev->value) + combo->setCurrentIndex(combo->count() - 1); + } + } + break; + case PREF_STRING: + { + QLineEdit* line = static_cast<QLineEdit*>(editor); + line->setText(index.model()->data(index, Qt::DisplayRole).toString()); + } + break; + case PREF_DECODE_AS_RANGE: + case PREF_RANGE: + { + RangeSyntaxLineEdit* syntax = static_cast<RangeSyntaxLineEdit*>(editor); + syntax->setText(index.model()->data(index, Qt::DisplayRole).toString()); + } + break; + case PREF_UAT: + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + case PREF_COLOR: + //Handled by the dialogs created + break; + default: + //Ensure any new preference types are handled + Q_ASSERT(FALSE); + break; + } +} + +void AdvancedPrefDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + PrefsItem* pref = indexToPref(index); + switch(pref->getPrefType()) + { + case PREF_DECODE_AS_UINT: + case PREF_UINT: + case PREF_STRING: + { + QLineEdit* line = static_cast<QLineEdit*>(editor); + model->setData(index, line->text(), Qt::EditRole); + break; + } + case PREF_ENUM: + { + QComboBox* combo = static_cast<QComboBox*>(editor); + model->setData(index, combo->itemData(combo->currentIndex()), Qt::EditRole); + } + break; + case PREF_DECODE_AS_RANGE: + case PREF_RANGE: + { + RangeSyntaxLineEdit* syntax = static_cast<RangeSyntaxLineEdit*>(editor); + model->setData(index, syntax->text(), Qt::EditRole); + break; + } + case PREF_UAT: + //do nothing because UAT values aren't shown in table + break; + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + case PREF_COLOR: + //do nothing, dialog signals will update table + pref = NULL; + break; + default: + //Ensure any new preference types are handled + Q_ASSERT(FALSE); + break; + } +} + +/* * 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: + */ diff --git a/ui/qt/models/pref_delegate.h b/ui/qt/models/pref_delegate.h new file mode 100644 index 0000000000..1f693e6ade --- /dev/null +++ b/ui/qt/models/pref_delegate.h @@ -0,0 +1,54 @@ +/* pref_delegate.h + * Delegates for editing prefereneces. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef PREF_DELEGATE_H +#define PREF_DELEGATE_H + +#include <config.h> + +#include <ui/qt/models/pref_models.h> +#include <ui/qt/widgets/syntax_line_edit.h> + +#include <QStyledItemDelegate> +#include <QModelIndex> + +class AdvancedPrefDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + AdvancedPrefDelegate(QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + +private: + PrefsItem* indexToPref(const QModelIndex &index) const; +}; + +//Utility class for range preferences +class RangeSyntaxLineEdit : public SyntaxLineEdit +{ + Q_OBJECT +public: + explicit RangeSyntaxLineEdit(QWidget *parent = 0); + void setMaxRange(unsigned int max) {maxRange_ = max;} + +public slots: + void checkRange(QString range); + +private: + unsigned int maxRange_; +}; + +#endif // PREF_DELEGATE_H diff --git a/ui/qt/models/pref_models.cpp b/ui/qt/models/pref_models.cpp new file mode 100644 index 0000000000..5c37035ab4 --- /dev/null +++ b/ui/qt/models/pref_models.cpp @@ -0,0 +1,768 @@ +/* pref_models.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <ui/qt/models/pref_models.h> +#include <ui/qt/utils/qt_ui_utils.h> +#include <epan/prefs-int.h> + +#ifdef HAVE_LIBPCAP +#ifdef _WIN32 +#include "caputils/capture-wpcap.h" +#endif /* _WIN32 */ +#endif /* HAVE_LIBPCAP */ + +#include <QFont> +#include <QColor> + +// XXX Should we move this to ui/preference_utils? +static GHashTable * pref_ptr_to_pref_ = NULL; +pref_t *prefFromPrefPtr(void *pref_ptr) +{ + return (pref_t *)g_hash_table_lookup(pref_ptr_to_pref_, (gpointer) pref_ptr); +} + +static void prefInsertPrefPtr(void * pref_ptr, pref_t * pref) +{ + if ( ! pref_ptr_to_pref_ ) + pref_ptr_to_pref_ = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); + + gpointer key = (gpointer) pref_ptr; + gpointer val = (gpointer) pref; + + /* Already existing entries will be ignored */ + if ( (pref = (pref_t *)g_hash_table_lookup(pref_ptr_to_pref_, key) ) == NULL ) + g_hash_table_insert(pref_ptr_to_pref_, key, val); +} + +PrefsItem::PrefsItem(module_t *module, pref_t *pref, PrefsItem* parent) + : ModelHelperTreeItem<PrefsItem>(parent), + pref_(pref), + module_(module), + changed_(false) +{ + name_ = QString(module->name ? module->name : module->parent->name); + if (pref_ != NULL) { + name_ += QString(".%1").arg(prefs_get_name(pref_)); + } +} + +PrefsItem::PrefsItem(QString name, PrefsItem* parent) + : ModelHelperTreeItem<PrefsItem>(parent), + pref_(NULL), + module_(NULL), + name_(name), + changed_(false) +{ + +} + +PrefsItem::~PrefsItem() +{ +} + +int PrefsItem::getPrefType() const +{ + if (pref_ == NULL) + return 0; + + return prefs_get_type(pref_); +} + +int PrefsItem::getPrefGUIType() const +{ + if (pref_ == NULL) + return GUI_ALL; + + return prefs_get_gui_type(pref_); +} + +bool PrefsItem::isPrefDefault() const +{ + if (pref_ == NULL) + return true; + + if (changed_ == false) + return prefs_pref_is_default(pref_) ? true : false; + + return false; +} + +QString PrefsItem::getPrefTypeName() const +{ + if (pref_ == NULL) + return ""; + + return QString(prefs_pref_type_name(pref_)); +} + +QString PrefsItem::getModuleName() const +{ + if (module_ == NULL) + return name_; + + return QString(module_->name); +} + + +void PrefsItem::setChanged(bool changed) +{ + changed_ = changed; +} + + +const char* PrefsModel::ADVANCED_PREFERENCE_TREE_NAME = "Advanced"; +const char* PrefsModel::APPEARANCE_PREFERENCE_TREE_NAME = "Appearance"; +const char* PrefsModel::LAYOUT_PREFERENCE_TREE_NAME = "Layout"; +const char* PrefsModel::COLUMNS_PREFERENCE_TREE_NAME = "Columns"; +const char* PrefsModel::FONT_AND_COLORS_PREFERENCE_TREE_NAME = "Font and Colors"; +const char* PrefsModel::CAPTURE_PREFERENCE_TREE_NAME = "Capture"; +const char* PrefsModel::EXPERT_PREFERENCE_TREE_NAME = "Expert"; +const char* PrefsModel::FILTER_BUTTONS_PREFERENCE_TREE_NAME = "Filter Buttons"; + + +PrefsModel::PrefsModel(QObject *parent) : + QAbstractItemModel(parent), + root_(new PrefsItem(QString("ROOT"), NULL)) +{ + populate(); +} + +PrefsModel::~PrefsModel() +{ + delete root_; +} + +int PrefsModel::rowCount(const QModelIndex &parent) const +{ + PrefsItem *parent_item; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast<PrefsItem*>(parent.internalPointer()); + + if (parent_item == NULL) + return 0; + + return parent_item->childCount(); +} + +int PrefsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + + +QModelIndex PrefsModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) + return QModelIndex(); + + PrefsItem* item = static_cast<PrefsItem*>(index.internalPointer()); + if (item != NULL) { + PrefsItem* parent_item = item->parentItem(); + if (parent_item != NULL) { + if (parent_item == root_) + return QModelIndex(); + + return createIndex(parent_item->row(), 0, parent_item); + } + } + + return QModelIndex(); +} + +QModelIndex PrefsModel::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + PrefsItem *parent_item, *child_item; + + if (!parent.isValid()) + parent_item = root_; + else + parent_item = static_cast<PrefsItem*>(parent.internalPointer()); + + Q_ASSERT(parent_item); + + child_item = parent_item->child(row); + if (child_item) { + return createIndex(row, column, child_item); + } + + return QModelIndex(); +} + +QVariant PrefsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::UserRole)) + return QVariant(); + + PrefsItem* item = static_cast<PrefsItem*>(index.internalPointer()); + if (item == NULL) + return QVariant(); + + if (role == Qt::UserRole) + return VariantPointer<PrefsItem>::asQVariant(item); + + switch ((enum PrefsModelColumn)index.column()) { + case colName: + return item->getName(); + + case colStatus: + if ((item->getPrefType() == PREF_UAT && (item->getPrefGUIType() == GUI_ALL || item->getPrefGUIType() == GUI_QT)) || item->getPrefType() == PREF_CUSTOM) + return QObject::tr("Unknown"); + + if (item->isPrefDefault()) + return QObject::tr("Default"); + + return QObject::tr("Changed"); + case colType: + return item->getPrefTypeName(); + case colValue: + if (item->getPref() == NULL) + return QVariant(); + + return QString(gchar_free_to_qstring(prefs_pref_to_str(item->getPref(), pref_stashed)).remove(QRegExp("\n\t"))); + default: + break; + } + + return QVariant(); +} + +static guint +fill_prefs(module_t *module, gpointer root_ptr) +{ + PrefsItem* root_item = static_cast<PrefsItem*>(root_ptr); + + if ((module == NULL) || (root_item == NULL)) + return 1; + + if (module->numprefs < 1 && !prefs_module_has_submodules(module)) + return 0; + + PrefsItem* module_item = new PrefsItem(module, NULL, root_item); + root_item->appendChild(module_item); + + for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) { + pref_t *pref = (pref_t *) pref_l->data; + + if (prefs_get_type(pref) == PREF_OBSOLETE || prefs_get_type(pref) == PREF_STATIC_TEXT) + continue; + + const char *type_name = prefs_pref_type_name(pref); + if (!type_name) + continue; + + pref_stash(pref, NULL); + + PrefsItem* item = new PrefsItem(module, pref, module_item); + module_item->appendChild(item); + + // .uat is a void * so it wins the "useful key value" prize. + if (prefs_get_uat_value(pref)) { + prefInsertPrefPtr( prefs_get_uat_value(pref), pref); + } + } + + if(prefs_module_has_submodules(module)) + return prefs_modules_foreach_submodules(module, fill_prefs, module_item); + + return 0; +} + +void PrefsModel::populate() +{ + // Printing prefs don't apply here. + module_t *print_module = prefs_find_module("print"); + if (print_module) + print_module->use_gui = FALSE; + + //Since "expert" is really a pseudo protocol, it shouldn't be + //categorized with other "real" protocols when it comes to + //preferences. Since it's just a UAT, don't bury it in + //with the other protocols + module_t *expert_module = prefs_find_module("_ws.expert"); + if (expert_module) + expert_module->use_gui = FALSE; + + prefs_modules_foreach_submodules(NULL, fill_prefs, (gpointer)root_); + + //Add the "specially handled" preferences + PrefsItem *appearance_item, *appearance_subitem, *special_item; + + appearance_item = new PrefsItem(APPEARANCE_PREFERENCE_TREE_NAME, root_); + root_->appendChild(appearance_item); + + appearance_subitem = new PrefsItem(LAYOUT_PREFERENCE_TREE_NAME, appearance_item); + appearance_item->appendChild(appearance_subitem); + appearance_subitem = new PrefsItem(COLUMNS_PREFERENCE_TREE_NAME, appearance_item); + appearance_item->appendChild(appearance_subitem); + appearance_subitem = new PrefsItem(FONT_AND_COLORS_PREFERENCE_TREE_NAME, appearance_item); + appearance_item->appendChild(appearance_subitem); + + special_item = new PrefsItem(CAPTURE_PREFERENCE_TREE_NAME, root_); + root_->appendChild(special_item); + special_item = new PrefsItem(EXPERT_PREFERENCE_TREE_NAME, root_); + root_->appendChild(special_item); + special_item = new PrefsItem(FILTER_BUTTONS_PREFERENCE_TREE_NAME, root_); + root_->appendChild(special_item); + special_item = new PrefsItem(ADVANCED_PREFERENCE_TREE_NAME, root_); + root_->appendChild(special_item); +} + + + + + +AdvancedPrefsModel::AdvancedPrefsModel(QObject * parent) +: QSortFilterProxyModel(parent), +filter_() +{ +} + +QVariant AdvancedPrefsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + + switch (section) { + case colName: + return tr("Name"); + case colStatus: + return tr("Status"); + case colType: + return tr("Type"); + case colValue: + return tr("Value"); + default: + break; + } + } + return QVariant(); +} + +QVariant AdvancedPrefsModel::data(const QModelIndex &dataindex, int role) const +{ + if (!dataindex.isValid()) + return QVariant(); + + QModelIndex modelIndex = mapToSource(dataindex); + + PrefsItem* item = static_cast<PrefsItem*>(modelIndex.internalPointer()); + if (item == NULL) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + switch ((AdvancedPrefsModelColumn)dataindex.column()) + { + case colName: + if (item->getPref() == NULL) + return item->getModule()->title; + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colName, modelIndex.parent()), role); + case colStatus: + if (item->getPref() == NULL) + return QVariant(); + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colStatus, modelIndex.parent()), role); + case colType: + if (item->getPref() == NULL) + return QVariant(); + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colType, modelIndex.parent()), role); + case colValue: + if (item->getPref() == NULL) + return QVariant(); + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colValue, modelIndex.parent()), role); + default: + break; + } + break; + case Qt::ToolTipRole: + switch ((AdvancedPrefsModelColumn)dataindex.column()) + { + case colName: + if (item->getPref() == NULL) + return QString("<span>%1</span>").arg(item->getModule()->description); + + return QString("<span>%1</span>").arg(prefs_get_description(item->getPref())); + case colStatus: + if (item->getPref() == NULL) + return QVariant(); + + return QObject::tr("Has this preference been changed?"); + case colType: + if (item->getPref() == NULL) { + return QVariant(); + } else { + QString type_desc = gchar_free_to_qstring(prefs_pref_type_description(item->getPref())); + return QString("<span>%1</span>").arg(type_desc); + } + break; + case colValue: + if (item->getPref() == NULL) { + return QVariant(); + } else { + QString default_value = gchar_free_to_qstring(prefs_pref_to_str(item->getPref(), pref_stashed)); + return QString("<span>%1</span>").arg( + default_value.isEmpty() ? default_value : QObject::tr("Default value is empty")); + } + default: + break; + } + break; + case Qt::FontRole: + if (item->getPref() == NULL) + return QVariant(); + + if (!item->isPrefDefault() && + /* UATs and custom preferences are "unknown", that shouldn't mean that they are always bolded */ + item->getPrefType() != PREF_UAT && item->getPrefType() != PREF_CUSTOM) { + QFont font; + font.setBold(true); + return font; + } + break; + case Qt::UserRole: + return sourceModel()->data(modelIndex, role); + default: + break; + } + + return QVariant(); +} + +bool AdvancedPrefsModel::setData(const QModelIndex &dataindex, const QVariant &value, int role) +{ + if ((!dataindex.isValid()) || (role != Qt::EditRole)) + return false; + + QModelIndex modelIndex = mapToSource(dataindex); + + PrefsItem* item = static_cast<PrefsItem*>(modelIndex.internalPointer()); + if (item == NULL) + return false; + + if (value.isNull()) { + //reset preference to default + reset_stashed_pref(item->getPref()); + item->setChanged(false); + } else { + item->setChanged(true); + switch (item->getPrefType()) + { + case PREF_DECODE_AS_UINT: + case PREF_UINT: + { + bool ok; + guint new_val = value.toString().toUInt(&ok, prefs_get_uint_base(item->getPref())); + + if (ok) + prefs_set_uint_value(item->getPref(), new_val, pref_stashed); + } + break; + case PREF_BOOL: + prefs_invert_bool_value(item->getPref(), pref_stashed); + break; + case PREF_ENUM: + prefs_set_enum_value(item->getPref(), value.toInt(), pref_stashed); + break; + case PREF_STRING: + prefs_set_string_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + case PREF_DECODE_AS_RANGE: + case PREF_RANGE: + prefs_set_stashed_range_value(item->getPref(), value.toString().toUtf8().constData()); + break; + case PREF_SAVE_FILENAME: + case PREF_OPEN_FILENAME: + case PREF_DIRNAME: + prefs_set_string_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + case PREF_COLOR: + { + QColor qc(value.toString()); + color_t color; + + color.red = qc.red() << 8 | qc.red(); + color.green = qc.green() << 8 | qc.green(); + color.blue = qc.blue() << 8 | qc.blue(); + + prefs_set_color_value(item->getPref(), color, pref_stashed); + break; + } + } + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QVector<int> roles; + roles << role; +#endif + + // The status field may change as well as the value, so mark them for update + emit dataChanged(index(dataindex.row(), AdvancedPrefsModel::colStatus), + index(dataindex.row(), AdvancedPrefsModel::colValue) +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + , roles +#endif + ); + + return true; +} + +Qt::ItemFlags AdvancedPrefsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + QModelIndex modelIndex = mapToSource(index); + + PrefsItem* item = static_cast<PrefsItem*>(modelIndex.internalPointer()); + if (item == NULL) + return 0; + + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + if (item->getPref() == NULL) { + /* Base modules aren't changable */ + flags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + } else { + flags |= Qt::ItemIsEditable; + } + + return flags; +} + + +int AdvancedPrefsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +void AdvancedPrefsModel::setFirstColumnSpanned(QTreeView* tree, const QModelIndex& mIndex) +{ + int childCount, row; + PrefsItem* item; + if (mIndex.isValid()) { + item = VariantPointer<PrefsItem>::asPtr(data(mIndex, Qt::UserRole)); + if (item != NULL) { + childCount = item->childCount(); + if (childCount > 0) { + tree->setFirstColumnSpanned(mIndex.row(), mIndex.parent(), true); + for (row = 0; row < childCount; row++) { + setFirstColumnSpanned(tree, mIndex.child(row, 0)); + } + } + } + } else { + for (row = 0; row < rowCount(); row++) { + setFirstColumnSpanned(tree, index(row, 0)); + } + } +} + +bool AdvancedPrefsModel::filterAcceptItem(PrefsItem& item) const +{ + if (filter_.isEmpty()) + return true; + + QString name, tooltip; + if (item.getPref() == NULL) { + name = item.getModule()->title; + tooltip = item.getModule()->description; + } else { + name = QString(item.getModule()->name ? item.getModule()->name : item.getModule()->parent->name); + name += QString(".%1").arg(prefs_get_name(item.getPref())); + tooltip = prefs_get_description(item.getPref()); + } + + if (name.contains(filter_, Qt::CaseInsensitive) || tooltip.contains(filter_, Qt::CaseInsensitive)) + return true; + + PrefsItem *child_item; + for (int child_row = 0; child_row < item.childCount(); child_row++) + { + child_item = item.child(child_row); + if ((child_item != NULL) && (filterAcceptItem(*child_item))) + return true; + } + + return false; +} + +bool AdvancedPrefsModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, PrefsModel::colName, sourceParent); + PrefsItem* item = static_cast<PrefsItem*>(nameIdx.internalPointer()); + if (item == NULL) + return true; + + //filter out the "special" preferences + if ((item->getModule() == NULL) && (item->getPref() == NULL)) + return false; + + if (filterAcceptItem(*item)) + return true; + + return false; +} + +void AdvancedPrefsModel::setFilter(const QString& filter) +{ + filter_ = filter; + invalidateFilter(); +} + + + + + +ModulePrefsModel::ModulePrefsModel(QObject* parent) + : QSortFilterProxyModel(parent) + , advancedPrefName_(PrefsModel::ADVANCED_PREFERENCE_TREE_NAME) +{ +} + +QVariant ModulePrefsModel::data(const QModelIndex &dataindex, int role) const +{ + if (!dataindex.isValid()) + return QVariant(); + + QModelIndex modelIndex = mapToSource(dataindex); + + PrefsItem* item = static_cast<PrefsItem*>(modelIndex.internalPointer()); + if (item == NULL) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + switch ((ModulePrefsModelColumn)dataindex.column()) + { + case colName: + if (item->getPref() == NULL) { + if (item->getModule() == NULL) { + return item->getName(); + } + + return item->getModule()->title; + } + + return sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colName, modelIndex.parent()), role); + default: + break; + } + break; + case Qt::UserRole: + return sourceModel()->data(modelIndex, role); + case ModuleName: + return item->getModuleName(); + default: + break; + } + + return QVariant(); +} +Qt::ItemFlags ModulePrefsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + bool disable_capture = true; +#ifdef HAVE_LIBPCAP +#ifdef _WIN32 + /* Is WPcap loaded? */ + if (has_wpcap) { +#endif /* _WIN32 */ + disable_capture = false; +#ifdef _WIN32 + } +#endif /* _WIN32 */ +#endif /* HAVE_LIBPCAP */ + + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + if (disable_capture) { + QModelIndex modelIndex = mapToSource(index); + + PrefsItem* item = static_cast<PrefsItem*>(modelIndex.internalPointer()); + if (item == NULL) + return flags; + + if (item->getName().compare(PrefsModel::CAPTURE_PREFERENCE_TREE_NAME) == 0) { + flags &= (~Qt::ItemIsEnabled); + } + } + + return flags; +} + +int ModulePrefsModel::columnCount(const QModelIndex&) const +{ + return colLast; +} + +bool ModulePrefsModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + //Force "Advanced" preferences to be at bottom of model + if (source_left.isValid() && !source_left.parent().isValid() && + source_right.isValid() && !source_right.parent().isValid()) { + PrefsItem* left_item = static_cast<PrefsItem*>(source_left.internalPointer()); + PrefsItem* right_item = static_cast<PrefsItem*>(source_right.internalPointer()); + if ((left_item != NULL) && (left_item->getName().compare(advancedPrefName_) == 0)) { + return false; + } + if ((right_item != NULL) && (right_item->getName().compare(advancedPrefName_) == 0)) { + return true; + } + } + + + return QSortFilterProxyModel::lessThan(source_left, source_right); +} + +bool ModulePrefsModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QModelIndex nameIdx = sourceModel()->index(sourceRow, PrefsModel::colName, sourceParent); + PrefsItem* item = static_cast<PrefsItem*>(nameIdx.internalPointer()); + if (item == NULL) + return true; + + if (item->getPref() != NULL) + return false; + + if (item->getModule() != NULL) { + if (!item->getModule()->use_gui) { + return false; + } + } + + return true; +} + + + + +/* + * 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: + */ diff --git a/ui/qt/models/pref_models.h b/ui/qt/models/pref_models.h new file mode 100644 index 0000000000..4914e61133 --- /dev/null +++ b/ui/qt/models/pref_models.h @@ -0,0 +1,173 @@ +/* pref_models.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef PREF_MODELS_H +#define PREF_MODELS_H + +#include <config.h> + +#include <ui/qt/models/tree_model_helpers.h> + +#include <epan/prefs.h> + +#include <QSortFilterProxyModel> +#include <QTreeView> + +class PrefsItem : public ModelHelperTreeItem<PrefsItem> +{ +public: + PrefsItem(module_t *module, pref_t *pref, PrefsItem* parent); + PrefsItem(QString name, PrefsItem* parent); + virtual ~PrefsItem(); + + QString getName() const {return name_;} + pref_t* getPref() const {return pref_;} + int getPrefType() const; + int getPrefGUIType() const; + bool isPrefDefault() const; + QString getPrefTypeName() const; + module_t* getModule() const {return module_;} + QString getModuleName() const; + void setChanged(bool changed = true); + +private: + pref_t *pref_; + module_t *module_; + QString name_; + //set to true if changed during module manipulation + //Used to determine proper "default" for comparison + bool changed_; +}; + + +class PrefsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit PrefsModel(QObject * parent = Q_NULLPTR); + virtual ~PrefsModel(); + + //Names of special preferences handled by the GUI + //Names used as keys to determine correct pan displayed + static const char* ADVANCED_PREFERENCE_TREE_NAME; + static const char* APPEARANCE_PREFERENCE_TREE_NAME; + static const char* LAYOUT_PREFERENCE_TREE_NAME; + static const char* COLUMNS_PREFERENCE_TREE_NAME; + static const char* FONT_AND_COLORS_PREFERENCE_TREE_NAME; + static const char* CAPTURE_PREFERENCE_TREE_NAME; + static const char* EXPERT_PREFERENCE_TREE_NAME; + static const char* FILTER_BUTTONS_PREFERENCE_TREE_NAME; + + enum PrefsModelColumn { + colName = 0, + colStatus, + colType, + colValue, + colLast + }; + + QModelIndex index(int row, int column, + const QModelIndex & = QModelIndex()) const; + QModelIndex parent(const QModelIndex &) const; + QVariant data(const QModelIndex &index, int role) const; + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + +private: + void populate(); + + PrefsItem* root_; +}; + +class AdvancedPrefsModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + + explicit AdvancedPrefsModel(QObject * parent = Q_NULLPTR); + + enum AdvancedPrefsModelColumn { + colName = 0, + colStatus, + colType, + colValue, + colLast + }; + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + + void setFilter(const QString& filter); + + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + //Keep the internals of model hidden from tree + void setFirstColumnSpanned(QTreeView* tree, const QModelIndex &index = QModelIndex()); + +protected: + bool filterAcceptItem(PrefsItem& item) const; + +private: + + QString filter_; +}; + +class ModulePrefsModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + + explicit ModulePrefsModel(QObject * parent = Q_NULLPTR); + + enum ModulePrefsModelColumn { + colName = 0, + colLast + }; + + enum ModulePrefsRoles { + ModuleName = Qt::UserRole + 1 + }; + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + +private: + //cache of the translated "Advanced" preference name + QString advancedPrefName_; +}; + +extern pref_t *prefFromPrefPtr(void *pref_ptr); + +#endif // PREF_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: + */ |