diff options
Diffstat (limited to 'ui/qt/models/pref_models.cpp')
-rw-r--r-- | ui/qt/models/pref_models.cpp | 774 |
1 files changed, 774 insertions, 0 deletions
diff --git a/ui/qt/models/pref_models.cpp b/ui/qt/models/pref_models.cpp new file mode 100644 index 00000000..b86230f0 --- /dev/null +++ b/ui/qt/models/pref_models.cpp @@ -0,0 +1,774 @@ +/* pref_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/pref_models.h> +#include <ui/qt/utils/qt_ui_utils.h> +#include <epan/prefs-int.h> + +#ifdef HAVE_LIBPCAP +#ifdef _WIN32 +#include "capture/capture-wpcap.h" +#endif /* _WIN32 */ +#endif /* HAVE_LIBPCAP */ + +#include <QFont> +#include <QColor> +#include <QRegularExpression> +#include <QApplication> + +// 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 ((void *)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), + name_(module->name ? module->name : module->parent->name), + changed_(false) +{ + if (pref_ != NULL) { + name_ += QString(".%1").arg(prefs_get_name(pref_)); + } +} + +PrefsItem::PrefsItem(const 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_); +} + +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); +} + +QString PrefsItem::getModuleTitle() const +{ + if ((module_ == NULL) && (pref_ == NULL)) + return name_; + + Q_ASSERT(module_); + + return QString(module_->title); +} + +void PrefsItem::setChanged(bool changed) +{ + changed_ = changed; +} + +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 static_cast<int>(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->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(QRegularExpression("\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->prependChild(module_item); + + for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = gxx_list_next(pref_l)) { + pref_t *pref = gxx_list_data(pref_t *, pref_l); + + 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->prependChild(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() +{ + 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(typeToString(PrefsModel::Appearance), root_); + root_->prependChild(appearance_item); + + appearance_subitem = new PrefsItem(typeToString(PrefsModel::Layout), appearance_item); + appearance_item->prependChild(appearance_subitem); + appearance_subitem = new PrefsItem(typeToString(PrefsModel::Columns), appearance_item); + appearance_item->prependChild(appearance_subitem); + appearance_subitem = new PrefsItem(typeToString(PrefsModel::FontAndColors), appearance_item); + appearance_item->prependChild(appearance_subitem); + + special_item = new PrefsItem(typeToString(PrefsModel::Capture), root_); + root_->prependChild(special_item); + special_item = new PrefsItem(typeToString(PrefsModel::Expert), root_); + root_->prependChild(special_item); + special_item = new PrefsItem(typeToString(PrefsModel::FilterButtons), root_); + root_->prependChild(special_item); +#ifdef HAVE_LIBGNUTLS + special_item = new PrefsItem(typeToString(PrefsModel::RSAKeys), root_); + root_->prependChild(special_item); +#endif + special_item = new PrefsItem(typeToString(PrefsModel::Advanced), root_); + root_->prependChild(special_item); +} + +QString PrefsModel::typeToString(int type) +{ + QString typeStr; + + switch(type) + { + case Advanced: typeStr = tr("Advanced"); break; + case Appearance: typeStr = tr("Appearance"); break; + case Layout: typeStr = tr("Layout"); break; + case Columns: typeStr = tr("Columns"); break; + case FontAndColors: typeStr = tr("Font and Colors"); break; + case Capture: typeStr = tr("Capture"); break; + case Expert: typeStr = tr("Expert"); break; + case FilterButtons: typeStr = tr("Filter Buttons"); break; + case RSAKeys: typeStr = tr("RSA Keys"); break; + } + + return typeStr; +} + +AdvancedPrefsModel::AdvancedPrefsModel(QObject * parent) +: QSortFilterProxyModel(parent), +filter_(), +show_changed_values_(false), +passwordChar_(QApplication::style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter)) +{ +} + +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(); + + if (PREF_PASSWORD == item->getPrefType()) + { + return QString(sourceModel()->data(sourceModel()->index(modelIndex.row(), PrefsModel::colValue, modelIndex.parent()), role).toString().size(), passwordChar_); + } else { + 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_PASSWORD: + prefs_set_password_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; + } + case PREF_CUSTOM: + prefs_set_custom_value(item->getPref(), value.toString().toStdString().c_str(), pref_stashed); + break; + } + } + + QVector<int> roles; + roles << role; + + // The status field may change as well as the value, so mark them for update + emit dataChanged(index(dataindex.row(), 0, dataindex.parent()), + index(dataindex.row(), columnCount() - 1, dataindex.parent()), + roles); + + return true; +} + +Qt::ItemFlags AdvancedPrefsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + + QModelIndex modelIndex = mapToSource(index); + + PrefsItem* item = static_cast<PrefsItem*>(modelIndex.internalPointer()); + if (item == NULL) + return Qt::ItemFlags(); + + 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, index(row, 0, mIndex)); + } + } + } + } else { + for (row = 0; row < rowCount(); row++) { + setFirstColumnSpanned(tree, index(row, 0)); + } + } +} + +bool AdvancedPrefsModel::filterAcceptItem(PrefsItem& item) const +{ + if (filter_.isEmpty() && !show_changed_values_) + 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 (show_changed_values_ && item.getPref()) { + // UATs and custom preferences are "unknown", do not show when show_changed_only. + if (item.isPrefDefault() || item.getPrefType() == PREF_UAT || item.getPrefType() == PREF_CUSTOM) { + return false; + } else if (filter_.isEmpty()) { + return true; + } + } + + // Do not match module title or description when having show_changed_only. + if (!(filter_.isEmpty() || (show_changed_values_ && !item.getPref())) && + (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(); +} + +void AdvancedPrefsModel::setShowChangedValues(bool show_changed_values) +{ + show_changed_values_ = show_changed_values; + invalidateFilter(); +} + + + +ModulePrefsModel::ModulePrefsModel(QObject* parent) + : QSortFilterProxyModel(parent) + , advancedPrefName_(PrefsModel::typeToString(PrefsModel::Advanced)) +{ +} + +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: + return item->getModuleTitle(); + 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 Qt::ItemFlags(); + + 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::typeToString(PrefsModel::Capture)) == 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 +{ + PrefsItem* left_item = static_cast<PrefsItem*>(source_left.internalPointer()); + PrefsItem* right_item = static_cast<PrefsItem*>(source_right.internalPointer()); + + if ((left_item != NULL) && (right_item != NULL)) { + QString left_name = left_item->getModuleTitle(), + right_name = right_item->getModuleTitle(); + + //Force "Advanced" preferences to be at bottom of model + if (source_left.isValid() && !source_left.parent().isValid() && + source_right.isValid() && !source_right.parent().isValid()) { + if (left_name.compare(advancedPrefName_) == 0) { + return false; + } + if (right_name.compare(advancedPrefName_) == 0) { + return true; + } + } + + if (left_name.compare(right_name, Qt::CaseInsensitive) < 0) + return true; + } + + return false; +} + +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; +} |