Qt: Move Profile Dialog to Model/View

Move the profile dialog to a model/view based concept. Display and
ordering works now, but nothing much else.

Add possibility to filter for profiles, as well as group filters.

Change-Id: I4740ad5aa10feeb31192f8131fb1204f5dca7232
Reviewed-on: https://code.wireshark.org/review/25141
Petri-Dish: Roland Knall <rknall@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Roland Knall 2019-07-04 21:55:21 +02:00 committed by Anders Broman
parent 8bb5320cb2
commit 4e7ac431a5
8 changed files with 1024 additions and 256 deletions

View File

@ -35,6 +35,7 @@ set(WIRESHARK_WIDGET_HEADERS
widgets/overlay_scroll_bar.h
widgets/pref_module_view.h
widgets/packet_list_header.h
widgets/profile_tree_view.h
widgets/qcustomplot.h
widgets/range_syntax_lineedit.h
widgets/splash_overlay.h
@ -89,6 +90,7 @@ set(WIRESHARK_MODEL_HEADERS
models/percent_bar_delegate.h
models/pref_delegate.h
models/pref_models.h
models/profile_model.h
models/proto_tree_model.h
models/related_packet_delegate.h
models/sparkline_delegate.h
@ -261,6 +263,7 @@ set(WIRESHARK_WIDGET_SRCS
widgets/overlay_scroll_bar.cpp
widgets/packet_list_header.cpp
widgets/pref_module_view.cpp
widgets/profile_tree_view.cpp
widgets/qcustomplot.cpp
widgets/range_syntax_lineedit.cpp
widgets/splash_overlay.cpp
@ -312,7 +315,8 @@ set(WIRESHARK_MODEL_SRCS
models/percent_bar_delegate.cpp
models/pref_delegate.cpp
models/pref_models.cpp
models/proto_tree_model.cpp
models/profile_model.cpp
models/proto_tree_model.cpp
models/related_packet_delegate.cpp
models/sparkline_delegate.cpp
models/supported_protocols_model.cpp

View File

@ -0,0 +1,557 @@
/* profile_model.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "glib.h"
#include "ui/profile.h"
#include "wsutil/filesystem.h"
#include "epan/prefs.h"
#include <ui/qt/models/profile_model.h>
#include <ui/qt/utils/color_utils.h>
#include <ui/qt/utils/qt_ui_utils.h>
#include <QDir>
#include <QFont>
#define gxx_list_next(list) ((list) ? ((reinterpret_cast<GList *>(list))->next) : Q_NULLPTR)
Q_LOGGING_CATEGORY(profileLogger, "wireshark.profiles")
ProfileSortModel::ProfileSortModel(QObject * parent):
QSortFilterProxyModel (parent),
ft_(ProfileSortModel::AllProfiles),
ftext_(QString())
{}
bool ProfileSortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
QModelIndex left = source_left;
if ( source_left.column() != ProfileModel::COL_NAME )
left = source_left.sibling(source_left.row(), ProfileModel::COL_NAME);
QModelIndex right = source_right;
if ( source_right.column() != ProfileModel::COL_NAME )
right = source_right.sibling(source_right.row(), ProfileModel::COL_NAME);
bool igL = left.data(ProfileModel::DATA_IS_GLOBAL).toBool();
bool igR = right.data(ProfileModel::DATA_IS_GLOBAL).toBool();
if (left.data(ProfileModel::DATA_STATUS) == PROF_STAT_DEFAULT)
igL = true;
if (right.data(ProfileModel::DATA_STATUS) == PROF_STAT_DEFAULT)
igR = true;
if ( igL && ! igR )
return true;
else if ( ! igL && igR )
return false;
else if ( igL && igR )
{
if (left.data(ProfileModel::DATA_STATUS) == PROF_STAT_DEFAULT)
return true;
}
if ( left.data().toString().compare(right.data().toString()) <= 0 )
return true;
return false;
}
void ProfileSortModel::setFilterType(FilterType ft)
{
ft_ = ft;
invalidateFilter();
}
void ProfileSortModel::setFilterString(QString txt)
{
ftext_ = ! txt.isEmpty() ? txt : "";
invalidateFilter();
}
bool ProfileSortModel::filterAcceptsRow(int source_row, const QModelIndex &) const
{
bool accept = true;
QModelIndex idx = sourceModel()->index(source_row, ProfileModel::COL_NAME);
if ( ft_ != ProfileSortModel::AllProfiles )
{
bool gl = idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() || idx.data(ProfileModel::DATA_IS_DEFAULT).toBool();
if ( ft_ == ProfileSortModel::UserProfiles && gl )
accept = false;
else if ( ft_ == ProfileSortModel::GlobalProfiles && ! gl )
accept = false;
}
if ( ftext_.length() > 0 )
{
QString name = idx.data().toString();
if ( ! name.contains(ftext_, Qt::CaseInsensitive) )
accept = false;
}
return accept;
}
ProfileModel::ProfileModel(QObject * parent) :
QAbstractTableModel(parent)
{
/* Store preset profile name */
set_profile_ = get_profile_name();
reset_default_ = false;
loadProfiles();
}
void ProfileModel::loadProfiles()
{
bool refresh = profiles_.count() > 0;
if ( refresh )
profiles_.clear();
else
init_profile_list();
emit beginResetModel();
GList *fl_entry = edited_profile_list();
while (fl_entry && fl_entry->data)
{
profiles_ << reinterpret_cast<profile_def *>(fl_entry->data);
fl_entry = gxx_list_next(fl_entry);
}
emit endResetModel();
}
GList * ProfileModel::entry(profile_def *ref) const
{
GList *fl_entry = edited_profile_list();
while (fl_entry && fl_entry->data) {
profile_def *profile = reinterpret_cast<profile_def *>(fl_entry->data);
if (strcmp(ref->reference, profile->reference) == 0 && ref->is_global == profile->is_global)
return fl_entry;
fl_entry = gxx_list_next(fl_entry);
}
return Q_NULLPTR;
}
GList *ProfileModel::at(int row) const
{
if ( row < 0 || row >= profiles_.count() )
return Q_NULLPTR;
profile_def * prof = profiles_.at(row);
return entry(prof);
}
int ProfileModel::rowCount(const QModelIndex &) const
{
return profiles_.count();
}
int ProfileModel::columnCount(const QModelIndex &) const
{
return static_cast<int>(_LAST_ENTRY);
}
QVariant ProfileModel::data(const QModelIndex &index, int role) const
{
if ( ! index.isValid() || profiles_.count() <= index.row() )
return QVariant();
profile_def * prof = profiles_.at(index.row());
if ( ! prof )
return QVariant();
switch ( role )
{
case Qt::DisplayRole:
switch (index.column())
{
case COL_NAME:
return QString(prof->name);
case COL_TYPE:
if ( prof->is_global || prof->status == PROF_STAT_DEFAULT )
return tr("Global");
else
return tr("User");
case COL_PATH:
switch (prof->status)
{
case PROF_STAT_DEFAULT:
if (!reset_default_)
return get_persconffile_path("", FALSE);
else
return tr("Resetting to default");
case PROF_STAT_EXISTS:
{
QString profile_path = prof->is_global ? get_global_profiles_dir() : get_profiles_dir();
profile_path.append(QDir::separator()).append(prof->name);
return profile_path;
}
case PROF_STAT_NEW:
return tr("Created from default settings");
case PROF_STAT_COPY:
if (prof->reference)
return QString("%1 %2").arg(tr("Copied from: ")).arg(prof->reference);
break;
}
break;
default:
break;
}
break;
case Qt::FontRole:
{
QFont font;
if ( prof->is_global || prof->status == PROF_STAT_DEFAULT )
font.setItalic(true);
if ( set_profile_.compare(prof->name) == 0 )
{
profile_def * act = profiles_.at(activeProfile().row());
if ( act->is_global == prof->is_global )
font.setBold(true);
}
if ( prof->status == PROF_STAT_DEFAULT && reset_default_ )
font.setStrikeOut(true);
return font;
}
case Qt::BackgroundRole:
{
QBrush bgBrush;
if ( ! profile_name_is_valid(prof->name) )
bgBrush.setColor(ColorUtils::fromColorT(&prefs.gui_text_invalid));
if ( prof->status == PROF_STAT_DEFAULT && reset_default_ )
bgBrush.setColor(ColorUtils::fromColorT(&prefs.gui_text_deprecated));
return bgBrush;
}
case Qt::ToolTipRole:
switch (prof->status)
{
case PROF_STAT_DEFAULT:
if (reset_default_)
return tr("Will be reset to default values");
break;
case PROF_STAT_COPY:
if (prof->reference) {
QString reference = prof->reference;
GList *fl_entry = entry(prof);
if (fl_entry)
{
profile_def *profile = reinterpret_cast<profile_def *>(fl_entry->data);
if (strcmp(prof->reference, profile->reference) == 0) {
if (profile->status == PROF_STAT_CHANGED) {
// Reference profile was renamed, use the new name
reference = profile->name;
break;
}
}
}
QString profile_info = tr("Created from %1").arg(reference);
if (prof->from_global) {
profile_info.append(QString(" %1").arg(tr("(system provided)")));
} else if (!reference.isEmpty()) {
profile_info.append(QString(" %1").arg(tr("(deleted)")));
}
return profile_info;
}
break;
case PROF_STAT_NEW:
return tr("Created from default settings");
case PROF_STAT_CHANGED:
if ( prof->reference )
return tr("Renamed from %1").arg(prof->reference);
break;
default:
break;
}
if (gchar * err_msg = profile_name_is_valid(prof->name))
{
QString msg = gchar_free_to_qstring(err_msg);
return msg;
}
if (prof->is_global)
return tr("This is a system provided profile.");
if ( prof->status == PROF_STAT_DEFAULT && reset_default_ )
return tr("The profile will be reset to default values.");
break;
case ProfileModel::DATA_STATUS:
return qVariantFromValue(prof->status);
case ProfileModel::DATA_IS_DEFAULT:
return qVariantFromValue(prof->status == PROF_STAT_DEFAULT);
case ProfileModel::DATA_IS_GLOBAL:
return qVariantFromValue(prof->is_global);
case ProfileModel::DATA_IS_SELECTED:
{
QModelIndex selected = activeProfile();
profile_def * selprof = profiles_.at(selected.row());
if ( selprof )
{
if ( selprof->is_global != prof->is_global )
return qVariantFromValue(false);
if ( strcmp(selprof->name, prof->name) == 0 )
return qVariantFromValue(true);
}
return qVariantFromValue(false);
}
case ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION:
if ( prof->status == PROF_STAT_NEW || prof->status == PROF_STAT_COPY || ( prof->status == PROF_STAT_DEFAULT && reset_default_ ) )
return qVariantFromValue(false);
else
return qVariantFromValue(true);
default:
break;
}
#if 0
if (pd_ui_->profileTreeView->topLevelItemCount() > 0) {
profile_def *profile;
for (int i = 0; i < pd_ui_->profileTreeView->topLevelItemCount(); i++) {
item = pd_ui_->profileTreeView->topLevelItem(i);
profile = (profile_def *) VariantPointer<GList>::asPtr(item->data(0, Qt::UserRole))->data;
if (current_profile && !current_profile->is_global && profile != current_profile && strcmp(profile->name, current_profile->name) == 0) {
item->setToolTip(0, tr("A profile already exists with this name."));
item->setBackground(0, ColorUtils::fromColorT(&prefs.gui_text_invalid));
if (current_profile->status != PROF_STAT_DEFAULT &&
current_profile->status != PROF_STAT_EXISTS)
{
pd_ui_->infoLabel->setText(tr("A profile already exists with this name"));
}
enable_ok = false;
}
}
}
#endif
return QVariant();
}
QVariant ProfileModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
{
switch (section)
{
case COL_NAME:
return tr("Profile");
case COL_TYPE:
return tr("Type");
case COL_PATH:
return tr("Path");
default:
break;
}
}
return QVariant();
}
Qt::ItemFlags ProfileModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags fl = QAbstractTableModel::flags(index);
if ( ! index.isValid() || profiles_.count() <= index.row() )
return fl;
profile_def * prof = profiles_.at(index.row());
if ( ! prof )
return fl;
if ( index.column() == ProfileModel::COL_NAME && ! prof->is_global && prof->status != PROF_STAT_DEFAULT )
fl |= Qt::ItemIsEditable;
return fl;
}
int ProfileModel::findByName(QString name)
{
int row = findByNameAndVisibility(name, false);
if ( row < 0 )
row = findByNameAndVisibility(name, true);
return row;
}
int ProfileModel::findByNameAndVisibility(QString name, bool isGlobal)
{
int row = -1;
for ( int cnt = 0; cnt < profiles_.count() && row < 0; cnt++ )
{
profile_def * prof = profiles_.at(cnt);
if ( prof && prof->is_global == isGlobal && name.compare(prof->name) == 0 )
row = cnt;
}
return row;
}
QModelIndex ProfileModel::addNewProfile(QString name)
{
add_to_profile_list(name.toUtf8().data(), "", PROF_STAT_NEW, FALSE, FALSE);
loadProfiles();
return index(findByName(name), COL_NAME);
}
QModelIndex ProfileModel::duplicateEntry(QModelIndex idx)
{
if ( ! idx.isValid() || profiles_.count() <= idx.row() )
return QModelIndex();
profile_def * prof = profiles_.at(idx.row());
if ( ! prof )
return QModelIndex();
QString parent = prof->name;
if (!prof->is_global)
parent = get_profile_parent (prof->name);
QString new_name;
if (prof->is_global && ! profile_exists (parent.toUtf8().constData(), FALSE))
new_name = QString(prof->name);
else
new_name = QString("%1 (%2)").arg(parent).arg(tr("copy"));
if ( findByNameAndVisibility(new_name) >= 0 )
return QModelIndex();
if ( new_name.compare(QString(new_name.toUtf8().constData())) != 0 && !prof->is_global )
return QModelIndex();
add_to_profile_list(new_name.toUtf8().constData(), parent.toUtf8().constData(), PROF_STAT_COPY, FALSE, prof->from_global);
loadProfiles();
int row = findByNameAndVisibility(new_name, false);
if ( row < 0 || row == idx.row() )
return QModelIndex();
return index(row, COL_NAME);
}
void ProfileModel::deleteEntry(QModelIndex idx)
{
if ( ! idx.isValid() )
return;
profile_def * prof = profiles_.at(idx.row());
if ( ! prof )
return;
if ( prof->is_global )
return;
if ( prof->status == PROF_STAT_DEFAULT )
{
emit layoutAboutToBeChanged();
reset_default_ = ! reset_default_;
emit dataChanged(index(0, 0), index(rowCount(), columnCount()));
emit layoutChanged();
}
else
{
GList * fl_entry = entry(prof);
emit beginRemoveRows(QModelIndex(), idx.row(), idx.row());
remove_from_profile_list(fl_entry);
loadProfiles();
emit endRemoveRows();
}
}
bool ProfileModel::resetDefault() const
{
return reset_default_;
}
void ProfileModel::doResetModel()
{
reset_default_ = false;
loadProfiles();
}
QModelIndex ProfileModel::activeProfile() const
{
ProfileModel * temp = const_cast<ProfileModel *>(this);
QString sel_profile = get_profile_name();
int row = temp->findByName(sel_profile);
if ( row >= 0 )
return index(row, ProfileModel::COL_NAME);
return QModelIndex();
}
bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int role)
{
if ( role != Qt::EditRole || ! idx.isValid() )
return false;
if ( ! value.isValid() || value.toString().isEmpty() )
return false;
profile_def * prof = profiles_.at(idx.row());
if ( ! prof || prof->status == PROF_STAT_DEFAULT )
return false;
QString current(prof->name);
if ( current.compare(value.toString()) != 0 )
{
g_free(prof->name);
prof->name = qstring_strdup(value.toString());
if (strcmp(prof->name, prof->reference) == 0) {
prof->status = PROF_STAT_EXISTS;
} else if (prof->status == PROF_STAT_EXISTS) {
prof->status = PROF_STAT_CHANGED;
}
}
loadProfiles();
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:
*/

View File

@ -0,0 +1,118 @@
/* profile_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 PROFILE_MODEL_H
#define PROFILE_MODEL_H
#include "glib.h"
#include <ui/profile.h>
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(profileLogger);
class ProfileSortModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
ProfileSortModel(QObject *parent = Q_NULLPTR);
enum FilterType {
AllProfiles = 0,
GlobalProfiles,
UserProfiles
};
void setFilterType(FilterType ft);
void setFilterString(QString txt = QString());
protected:
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
private:
FilterType ft_;
QString ftext_;
};
class ProfileModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit ProfileModel(QObject * parent = Q_NULLPTR);
enum {
COL_NAME,
COL_TYPE,
COL_PATH,
_LAST_ENTRY
} columns_;
enum {
DATA_STATUS = Qt::UserRole,
DATA_IS_DEFAULT,
DATA_IS_GLOBAL,
DATA_IS_SELECTED,
DATA_PATH_IS_NOT_DESCRIPTION
} data_values_;
// QAbstractItemModel interface
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex & parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex & idx, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
void deleteEntry(QModelIndex idx);
int findByName(QString name);
QModelIndex addNewProfile(QString name);
QModelIndex duplicateEntry(QModelIndex idx);
void doResetModel();
bool resetDefault() const;
QModelIndex activeProfile() const;
GList * at(int row) const;
private:
QList<profile_def *> profiles_;
QString set_profile_;
bool reset_default_;
void loadProfiles();
GList * entry(profile_def *) const;
int findByNameAndVisibility(QString name, bool isGlobal = false);
// QAbstractItemModel interface
public:
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
};
#endif
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -19,6 +19,7 @@
#include "ui/recent.h"
#include <ui/qt/utils/variant_pointer.h>
#include <ui/qt/models/profile_model.h>
#include "profile_dialog.h"
#include <ui_profile_dialog.h>
@ -32,19 +33,20 @@
#include <QPushButton>
#include <QTreeWidgetItem>
#include <QUrl>
#include <QComboBox>
#include <QLineEdit>
ProfileDialog::ProfileDialog(QWidget *parent) :
GeometryStateDialog(parent),
pd_ui_(new Ui::ProfileDialog),
ok_button_(NULL)
ok_button_(Q_NULLPTR),
model_(Q_NULLPTR),
sort_model_(Q_NULLPTR)
{
GList *fl_entry;
profile_def *profile;
const gchar *profile_name = get_profile_name();
pd_ui_->setupUi(this);
loadGeometry();
setWindowTitle(wsApp->windowTitleString(tr("Configuration Profiles")));
ok_button_ = pd_ui_->buttonBox->button(QDialogButtonBox::Ok);
// XXX - Use NSImageNameAddTemplate and NSImageNameRemoveTemplate to set stock
@ -60,33 +62,29 @@ ProfileDialog::ProfileDialog(QWidget *parent) :
pd_ui_->infoLabel->setAttribute(Qt::WA_MacSmallSize, true);
#endif
init_profile_list();
fl_entry = edited_profile_list();
pd_ui_->profileTreeWidget->blockSignals(true);
while (fl_entry && fl_entry->data) {
profile = (profile_def *) fl_entry->data;
QTreeWidgetItem *item = new QTreeWidgetItem(pd_ui_->profileTreeWidget);
item->setText(0, profile->name);
item->setData(0, Qt::UserRole, VariantPointer<GList>::asQVariant(fl_entry));
model_ = new ProfileModel(this);
sort_model_ = new ProfileSortModel(this);
sort_model_->setSourceModel(model_);
pd_ui_->profileTreeView->setModel(sort_model_);
if (profile->is_global || profile->status == PROF_STAT_DEFAULT) {
QFont ti_font = item->font(0);
ti_font.setItalic(true);
item->setFont(0, ti_font);
} else {
item->setFlags(item->flags() | Qt::ItemIsEditable);
}
connect(pd_ui_->profileTreeView, &ProfileTreeView::currentItemChanged,
this, &ProfileDialog::currentItemChanged);
if (!profile->is_global && strcmp(profile_name, profile->name) == 0) {
pd_ui_->profileTreeWidget->setCurrentItem(item);
}
fl_entry = g_list_next(fl_entry);
}
pd_ui_->profileTreeWidget->blockSignals(false);
connect(pd_ui_->profileTreeWidget->itemDelegate(), SIGNAL(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)),
connect(pd_ui_->profileTreeView->itemDelegate(), SIGNAL(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)),
this, SLOT(editingFinished()));
/* Select the row for the currently selected profile or the first row if non is selected*/
selectProfile();
QStringList items;
items << tr("All Profiles") << tr("Global profiles") << tr("User-defined profiles");
pd_ui_->cmbProfileTypes->addItems(items);
connect (pd_ui_->cmbProfileTypes, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(filterChanged(const QString &)));
connect (pd_ui_->lineProfileFilter, SIGNAL(textChanged(const QString &)),
this, SLOT(filterChanged(const QString &)));
updateWidgets();
}
@ -96,10 +94,21 @@ ProfileDialog::~ProfileDialog()
empty_profile_list (TRUE);
}
void ProfileDialog::selectProfile(QString profile)
{
if ( profile.isEmpty() )
profile = QString(get_profile_name());
int row = model_->findByName(profile);
QModelIndex idx = sort_model_->mapFromSource(model_->index(row, ProfileModel::COL_NAME));
if ( idx.isValid() )
pd_ui_->profileTreeView->selectRow(idx.row());
}
int ProfileDialog::execAction(ProfileDialog::ProfileAction profile_action)
{
int ret = QDialog::Accepted;
QTreeWidgetItem *item;
QModelIndex item;
switch (profile_action) {
case ShowProfiles:
@ -110,259 +119,134 @@ int ProfileDialog::execAction(ProfileDialog::ProfileAction profile_action)
ret = exec();
break;
case EditCurrentProfile:
item = pd_ui_->profileTreeWidget->currentItem();
if (item) {
pd_ui_->profileTreeWidget->editItem(item, 0);
item = pd_ui_->profileTreeView->currentIndex();
if (item.isValid()) {
pd_ui_->profileTreeView->edit(item);
}
ret = exec();
break;
case DeleteCurrentProfile:
if (delete_current_profile()) {
wsApp->setConfigurationProfile (NULL);
wsApp->setConfigurationProfile (Q_NULLPTR);
}
break;
default:
g_assert_not_reached();
break;
}
return ret;
}
void ProfileDialog::updateWidgets()
{
QTreeWidgetItem *item = pd_ui_->profileTreeWidget->currentItem();
bool enable_new = false;
bool enable_del = false;
bool enable_copy = false;
bool enable_ok = true;
profile_def *current_profile = NULL;
if (item) {
current_profile = (profile_def *) VariantPointer<GList>::asPtr(item->data(0, Qt::UserRole))->data;
enable_new = true;
enable_copy = true;
if (!current_profile->is_global && !(item->font(0).strikeOut())) {
QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
if ( index.column() != ProfileModel::COL_NAME )
index = index.sibling(index.row(), ProfileModel::COL_NAME);
if (index.isValid()) {
if ( !index.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! model_->resetDefault())
enable_del = true;
}
}
if (current_profile) {
QString profile_path;
QString profile_info;
switch (current_profile->status) {
case PROF_STAT_DEFAULT:
if (item->font(0).strikeOut()) {
profile_info = tr("Will be reset to default values");
} else {
profile_path = get_persconffile_path("", FALSE);
}
break;
case PROF_STAT_EXISTS:
if (model_ && model_->rowCount() > 0)
{
for ( int row = 0; row < model_->rowCount(); row++ )
{
QModelIndex idx = model_->index(row, ProfileModel::COL_NAME);
QString name = idx.data().toString();
if (gchar * err_msg = profile_name_is_valid(name.toLatin1().data()))
{
char* profile_dir = current_profile->is_global ? get_global_profiles_dir() : get_profiles_dir();
profile_path = profile_dir;
g_free(profile_dir);
profile_path.append(QDir::separator()).append(current_profile->name);
}
break;
case PROF_STAT_COPY:
if (current_profile->reference) {
bool reference_exists = false;
gchar *reference = current_profile->reference;
GList *fl_entry = edited_profile_list();
while (fl_entry && fl_entry->data) {
profile_def *profile = (profile_def *) fl_entry->data;
if (strcmp(current_profile->reference, profile->reference) == 0) {
if (profile->status != PROF_STAT_COPY) {
// Reference profile exists (and is not current profile)
reference_exists = true;
}
if (profile->status == PROF_STAT_CHANGED) {
// Reference profile was renamed, use the new name
reference = profile->name;
break;
}
}
fl_entry = g_list_next(fl_entry);
}
profile_info = tr("Created from %1").arg(reference);
if (current_profile->from_global) {
profile_info.append(QString(" %1").arg(tr("(system provided)")));
} else if (!reference_exists) {
profile_info.append(QString(" %1").arg(tr("(deleted)")));
}
break;
}
/* Fall Through */
case PROF_STAT_NEW:
profile_info = tr("Created from default settings");
break;
case PROF_STAT_CHANGED:
profile_info = tr("Renamed from %1").arg(current_profile->reference);
break;
}
if (!profile_path.isEmpty()) {
pd_ui_->infoLabel->setUrl(QUrl::fromLocalFile(profile_path).toString());
pd_ui_->infoLabel->setText(profile_path);
pd_ui_->infoLabel->setToolTip(tr("Go to %1").arg(profile_path));
} else {
pd_ui_->infoLabel->clear();
pd_ui_->infoLabel->setText(profile_info);
}
} else {
pd_ui_->infoLabel->clear();
}
if (pd_ui_->profileTreeWidget->topLevelItemCount() > 0) {
profile_def *profile;
for (int i = 0; i < pd_ui_->profileTreeWidget->topLevelItemCount(); i++) {
item = pd_ui_->profileTreeWidget->topLevelItem(i);
profile = (profile_def *) VariantPointer<GList>::asPtr(item->data(0, Qt::UserRole))->data;
if (gchar *err_msg = profile_name_is_valid(profile->name)) {
item->setToolTip(0, err_msg);
item->setBackground(0, ColorUtils::fromColorT(&prefs.gui_text_invalid));
if (profile == current_profile) {
pd_ui_->infoLabel->setText(err_msg);
}
g_free(err_msg);
pd_ui_->infoLabel->setText(gchar_free_to_qstring(err_msg));
enable_ok = false;
continue;
}
if (profile->is_global) {
item->setToolTip(0, tr("This is a system provided profile."));
continue;
}
if (current_profile && !current_profile->is_global && profile != current_profile && strcmp(profile->name, current_profile->name) == 0) {
item->setToolTip(0, tr("A profile already exists with this name."));
item->setBackground(0, ColorUtils::fromColorT(&prefs.gui_text_invalid));
if (current_profile->status != PROF_STAT_DEFAULT &&
current_profile->status != PROF_STAT_EXISTS)
if ( idx != index && idx.data().toString().compare(index.data().toString()) == 0 )
{
if (idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() == index.data(ProfileModel::DATA_IS_GLOBAL).toBool())
{
pd_ui_->infoLabel->setText(tr("A profile already exists with this name"));
int status = index.data(ProfileModel::DATA_STATUS).toInt();
if (status != PROF_STAT_DEFAULT && status != PROF_STAT_EXISTS)
pd_ui_->infoLabel->setText(tr("A profile already exists with this name"));
enable_ok = false;
}
enable_ok = false;
} else if (item->font(0).strikeOut()) {
item->setToolTip(0, tr("The profile will be reset to default values."));
item->setBackground(0, ColorUtils::fromColorT(&prefs.gui_text_deprecated));
} else {
item->setBackground(0, QBrush());
}
}
}
pd_ui_->profileTreeWidget->resizeColumnToContents(0);
pd_ui_->newToolButton->setEnabled(enable_new);
pd_ui_->profileTreeView->resizeColumnToContents(0);
pd_ui_->deleteToolButton->setEnabled(enable_del);
pd_ui_->copyToolButton->setEnabled(enable_copy);
ok_button_->setEnabled(enable_ok);
}
void ProfileDialog::on_profileTreeWidget_currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)
void ProfileDialog::currentItemChanged()
{
if (pd_ui_->profileTreeWidget->updatesEnabled()) updateWidgets();
updateWidgets();
}
void ProfileDialog::on_newToolButton_clicked()
{
QTreeWidgetItem *item = new QTreeWidgetItem();
profile_def *profile;
const gchar *name = "New profile";
GList *fl_entry = add_to_profile_list(name, "", PROF_STAT_NEW, FALSE, FALSE);
if ( model_->findByName(tr("New profile")) >= 0 )
return;
profile = (profile_def *) fl_entry->data;
item->setText(0, profile->name);
item->setData(0, Qt::UserRole, VariantPointer<GList>::asQVariant(fl_entry));
item->setFlags(item->flags() | Qt::ItemIsEditable);
pd_ui_->profileTreeWidget->addTopLevelItem(item);
pd_ui_->profileTreeWidget->setCurrentItem(item);
pd_ui_->profileTreeWidget->editItem(item, 0);
pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::UserProfiles);
sort_model_->setFilterString();
QModelIndex ridx = sort_model_->mapFromSource(model_->addNewProfile(tr("New profile")));
if (ridx.isValid())
{
pd_ui_->profileTreeView->setCurrentIndex(ridx);
pd_ui_->profileTreeView->edit(ridx);
}
}
void ProfileDialog::on_deleteToolButton_clicked()
{
QTreeWidgetItem *item = pd_ui_->profileTreeWidget->currentItem();
QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
if (item) {
GList *fl_entry = VariantPointer<GList>::asPtr(item->data(0, Qt::UserRole));
profile_def *profile = (profile_def *) fl_entry->data;
if (profile->is_global || item->font(0).strikeOut()) {
return;
}
if (profile->status == PROF_STAT_DEFAULT) {
QFont ti_font = item->font(0);
ti_font.setStrikeOut(true);
item->setFont(0, ti_font);
updateWidgets();
} else {
delete item;
// Select the default
pd_ui_->profileTreeWidget->setCurrentItem(pd_ui_->profileTreeWidget->topLevelItem(0));
remove_from_profile_list(fl_entry);
}
}
model_->deleteEntry(index);
}
void ProfileDialog::on_copyToolButton_clicked()
{
QTreeWidgetItem *cur_item = pd_ui_->profileTreeWidget->currentItem();
if (!cur_item) return;
pd_ui_->cmbProfileTypes->setCurrentIndex(ProfileSortModel::AllProfiles);
sort_model_->setFilterString();
profile_def *cur_profile = (profile_def *) VariantPointer<GList>::asPtr(cur_item->data(0, Qt::UserRole))->data;
if (!cur_profile) return;
QModelIndex current = pd_ui_->profileTreeView->currentIndex();
if ( current.column() != ProfileModel::COL_NAME )
current = current.sibling(current.row(), ProfileModel::COL_NAME);
QTreeWidgetItem *new_item = new QTreeWidgetItem();
GList *fl_entry;
const gchar *parent;
gchar *new_name;
profile_def *new_profile;
if (cur_profile->is_global) {
parent = cur_profile->name;
} else {
parent = get_profile_parent (cur_profile->name);
QModelIndex source = sort_model_->mapToSource(current);
QModelIndex ridx = model_->duplicateEntry(source);
if (ridx.isValid())
{
pd_ui_->profileTreeView->setCurrentIndex(sort_model_->mapFromSource(ridx));
pd_ui_->profileTreeView->edit(sort_model_->mapFromSource(ridx));
}
if (cur_profile->is_global && !profile_exists (parent, FALSE)) {
new_name = g_strdup (cur_profile->name);
} else {
new_name = g_strdup_printf ("%s (copy)", cur_profile->name);
}
/* Add a new entry to the profile list. */
fl_entry = add_to_profile_list(new_name, parent, PROF_STAT_COPY, FALSE, cur_profile->from_global);
new_profile = (profile_def *) fl_entry->data;
new_item->setText(0, new_profile->name);
new_item->setData(0, Qt::UserRole, VariantPointer<GList>::asQVariant(fl_entry));
new_item->setFlags(new_item->flags() | Qt::ItemIsEditable);
pd_ui_->profileTreeWidget->addTopLevelItem(new_item);
pd_ui_->profileTreeWidget->setCurrentItem(new_item);
pd_ui_->profileTreeWidget->editItem(new_item, 0);
g_free(new_name);
}
void ProfileDialog::on_buttonBox_accepted()
{
gchar *err_msg;
QTreeWidgetItem *default_item = pd_ui_->profileTreeWidget->topLevelItem(0);
QTreeWidgetItem *item = pd_ui_->profileTreeWidget->currentItem();
gchar *profile_name = NULL;
QModelIndex default_item = sort_model_->mapFromSource(model_->index(0, ProfileModel::COL_NAME));
QModelIndex index = sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
if (index.column() != ProfileModel::COL_NAME)
index = index.sibling(index.row(), ProfileModel::COL_NAME);
bool write_recent = true;
bool item_data_removed = false;
if (default_item && default_item->font(0).strikeOut()) {
if (default_item.data(ProfileModel::DATA_STATUS).toInt() == PROF_STAT_DEFAULT && model_->resetDefault())
{
// Reset Default profile.
GList *fl_entry = VariantPointer<GList>::asPtr(default_item->data(0, Qt::UserRole));
GList *fl_entry = model_->at(0);
remove_from_profile_list(fl_entry);
// Don't write recent file if leaving the Default profile after this has been reset.
write_recent = !is_default_profile();
// Don't fetch profile data if removed.
item_data_removed = (item == default_item);
item_data_removed = (index.row() == 0);
}
if (write_recent) {
@ -374,17 +258,23 @@ void ProfileDialog::on_buttonBox_accepted()
write_profile_recent();
}
if ((err_msg = apply_profile_changes()) != NULL) {
gchar * err_msg = Q_NULLPTR;
if ((err_msg = apply_profile_changes()) != Q_NULLPTR) {
QMessageBox::critical(this, tr("Profile Error"),
err_msg,
QMessageBox::Ok);
g_free(err_msg);
model_->doResetModel();
return;
}
if (item && !item_data_removed) {
profile_def *profile = (profile_def *) VariantPointer<GList>::asPtr(item->data(0, Qt::UserRole))->data;
profile_name = profile->name;
model_->doResetModel();
const char * profile_name = Q_NULLPTR;
if (index.isValid() && !item_data_removed) {
QString profileName = model_->data(index).toString();
profile_name = profileName.toLatin1().data();
}
if (profile_exists (profile_name, FALSE) || profile_exists (profile_name, TRUE)) {
@ -393,7 +283,7 @@ void ProfileDialog::on_buttonBox_accepted()
} else if (!profile_exists (get_profile_name(), FALSE)) {
// The new profile does not exist, and the previous profile has
// been deleted. Change to the default profile.
wsApp->setConfigurationProfile (NULL, FALSE);
wsApp->setConfigurationProfile (Q_NULLPTR, FALSE);
}
}
@ -404,23 +294,26 @@ void ProfileDialog::on_buttonBox_helpRequested()
void ProfileDialog::editingFinished()
{
QTreeWidgetItem *item = pd_ui_->profileTreeWidget->currentItem();
if (item) {
profile_def *profile = (profile_def *) VariantPointer<GList>::asPtr(item->data(0, Qt::UserRole))->data;
if (item->text(0).compare(profile->name) != 0) {
g_free(profile->name);
profile->name = qstring_strdup(item->text(0));
if (strcmp(profile->name, profile->reference) == 0) {
profile->status = PROF_STAT_EXISTS;
} else if (profile->status == PROF_STAT_EXISTS) {
profile->status = PROF_STAT_CHANGED;
}
}
}
updateWidgets();
}
void ProfileDialog::filterChanged(const QString &text)
{
if (qobject_cast<QComboBox *>(sender()))
{
QComboBox * cmb = qobject_cast<QComboBox *>(sender());
sort_model_->setFilterType(static_cast<ProfileSortModel::FilterType>(cmb->currentIndex()));
}
else if (qobject_cast<QLineEdit *>(sender()))
sort_model_->setFilterString(text);
pd_ui_->profileTreeView->resizeColumnToContents(ProfileModel::COL_NAME);
QModelIndex active = sort_model_->mapFromSource(model_->activeProfile());
if (active.isValid())
pd_ui_->profileTreeView->setCurrentIndex(active);
}
/*
* Editor modelines
*

View File

@ -10,10 +10,12 @@
#ifndef PROFILE_DIALOG_H
#define PROFILE_DIALOG_H
#include "geometry_state_dialog.h"
#include <ui/qt/geometry_state_dialog.h>
#include <ui/qt/models/profile_model.h>
#include <ui/qt/widgets/profile_tree_view.h>
class QPushButton;
class QTreeWidgetItem;
#include <QPushButton>
#include <QTreeWidgetItem>
namespace Ui {
class ProfileDialog;
@ -26,24 +28,39 @@ class ProfileDialog : public GeometryStateDialog
public:
enum ProfileAction { ShowProfiles, NewProfile, EditCurrentProfile, DeleteCurrentProfile };
explicit ProfileDialog(QWidget *parent = 0);
explicit ProfileDialog(QWidget *parent = Q_NULLPTR);
~ProfileDialog();
int execAction(ProfileAction profile_action);
/**
* @brief Select the profile with the given name.
*
* If the profile name is empty, the currently selected profile will be choosen instead.
* If the choosen profile is invalid, the first row will be choosen.
*
* @param profile the name of the profile to be selected
*/
void selectProfile(QString profile = QString());
private:
void updateWidgets();
Ui::ProfileDialog *pd_ui_;
QPushButton *ok_button_;
ProfileModel *model_;
ProfileSortModel *sort_model_;
void updateWidgets();
private slots:
void on_profileTreeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void currentItemChanged();
void on_newToolButton_clicked();
void on_deleteToolButton_clicked();
void on_copyToolButton_clicked();
void on_buttonBox_accepted();
void on_buttonBox_helpRequested();
void editingFinished();
void filterChanged(const QString &);
};
#endif // PROFILE_DIALOG_H

View File

@ -7,12 +7,26 @@
<x>0</x>
<y>0</y>
<width>470</width>
<height>300</height>
<height>386</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="profileTreeWidget">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineProfileFilter">
<property name="placeholderText">
<string>Search for profile ...</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbProfileTypes"/>
</item>
</layout>
</item>
<item>
<widget class="ProfileTreeView" name="profileTreeView">
<property name="rootIsDecorated">
<bool>false</bool>
</property>
@ -22,24 +36,22 @@
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
<property name="columnCount">
<number>1</number>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<attribute name="headerVisible">
<bool>true</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0">
<item>
<widget class="StockIconToolButton" name="newToolButton">
<property name="toolTip">
@ -124,6 +136,11 @@
<extends>QToolButton</extends>
<header>widgets/stock_icon_tool_button.h</header>
</customwidget>
<customwidget>
<class>ProfileTreeView</class>
<extends>QTreeView</extends>
<header>widgets/profile_tree_view.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>

View File

@ -0,0 +1,106 @@
/* profile_tree_view.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/url_link_delegate.h>
#include <ui/qt/models/profile_model.h>
#include <ui/qt/widgets/profile_tree_view.h>
#include <QDesktopServices>
#include <QDir>
#include <QItemDelegate>
#include <QLineEdit>
#include <QUrl>
ProfileUrlLinkDelegate::ProfileUrlLinkDelegate(QObject *parent) : UrlLinkDelegate (parent) {}
void ProfileUrlLinkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if ( index.column() != ProfileModel::COL_PATH )
QStyledItemDelegate::paint(painter, option, index);
/* Only paint links for valid paths */
if ( index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() )
UrlLinkDelegate::paint(painter, option, index);
else
QStyledItemDelegate::paint(painter, option, index);
}
ProfileTreeEditDelegate::ProfileTreeEditDelegate(QWidget *parent) : QItemDelegate(parent) {}
void ProfileTreeEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
if (qobject_cast<QLineEdit *>(editor))
{
QLineEdit * ql = qobject_cast<QLineEdit *>(editor);
ql->setText(index.data().toString());
}
}
ProfileTreeView::ProfileTreeView(QWidget *parent) :
QTreeView (parent)
{
setItemDelegateForColumn(ProfileModel::COL_NAME, new ProfileTreeEditDelegate());
setItemDelegateForColumn(ProfileModel::COL_PATH, new ProfileUrlLinkDelegate());
resizeColumnToContents(ProfileModel::COL_NAME);
connect(this, &QAbstractItemView::clicked, this, &ProfileTreeView::clicked);
}
void ProfileTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
if ( selected.count() == 0 && deselected.count() > 0 )
{
QItemSelection newSelection;
newSelection << deselected.at(0);
selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect);
}
else if ( selected.count() > 1 )
{
/* If more then one item is selected, only accept the new item, deselect everything else */
QSet<QItemSelectionRange> intersection = selected.toSet().intersect(deselected.toSet());
QItemSelection newSelection;
newSelection << intersection.toList().at(0);
selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect);
}
else
QTreeView::selectionChanged(selected, deselected);
}
void ProfileTreeView::currentChanged(const QModelIndex &, const QModelIndex &)
{
emit currentItemChanged();
}
void ProfileTreeView::clicked(const QModelIndex &index)
{
if ( !index.isValid() || index.column() != ProfileModel::COL_PATH )
return;
/* Only paint links for valid paths */
if ( index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() )
{
QString path = QDir::toNativeSeparators(index.data().toString());
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
}
void ProfileTreeView::selectRow(int row)
{
if ( row < 0 )
return;
setCurrentIndex(model()->index(row, 0));
selectionModel()->select(
QItemSelection(model()->index(row, 0), model()->index(row, model()->columnCount() -1)),
QItemSelectionModel::ClearAndSelect);
}

View File

@ -0,0 +1,56 @@
/* profile_tree_view.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PROFILE_TREEVIEW_H
#define PROFILE_TREEVIEW_H
#include <ui/qt/models/url_link_delegate.h>
#include <QTreeView>
#include <QItemDelegate>
class ProfileUrlLinkDelegate : public UrlLinkDelegate
{
Q_OBJECT
public:
explicit ProfileUrlLinkDelegate(QObject *parent = Q_NULLPTR);
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
class ProfileTreeEditDelegate : public QItemDelegate
{
Q_OBJECT
public:
ProfileTreeEditDelegate(QWidget *parent = Q_NULLPTR);
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
};
class ProfileTreeView : public QTreeView
{
Q_OBJECT
public:
ProfileTreeView(QWidget *parent = nullptr);
void selectRow(int row);
Q_SIGNALS:
void currentItemChanged();
// QAbstractItemView interface
protected slots:
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous);
virtual void clicked(const QModelIndex &index);
};
#endif