aboutsummaryrefslogtreecommitdiffstats
path: root/ui/qt/models
diff options
context:
space:
mode:
Diffstat (limited to 'ui/qt/models')
-rw-r--r--ui/qt/models/cache_proxy_model.cpp112
-rw-r--r--ui/qt/models/cache_proxy_model.h59
-rw-r--r--ui/qt/models/interface_sort_filter_model.cpp396
-rw-r--r--ui/qt/models/interface_sort_filter_model.h106
-rw-r--r--ui/qt/models/interface_tree_cache_model.cpp620
-rw-r--r--ui/qt/models/interface_tree_cache_model.h92
-rw-r--r--ui/qt/models/interface_tree_model.cpp558
-rw-r--r--ui/qt/models/interface_tree_model.h125
-rw-r--r--ui/qt/models/numeric_value_chooser_delegate.cpp117
-rw-r--r--ui/qt/models/numeric_value_chooser_delegate.h69
-rw-r--r--ui/qt/models/packet_list_model.cpp753
-rw-r--r--ui/qt/models/packet_list_model.h138
-rw-r--r--ui/qt/models/packet_list_record.cpp333
-rw-r--r--ui/qt/models/packet_list_record.h106
-rw-r--r--ui/qt/models/path_chooser_delegate.cpp152
-rw-r--r--ui/qt/models/path_chooser_delegate.h58
-rw-r--r--ui/qt/models/percent_bar_delegate.cpp116
-rw-r--r--ui/qt/models/percent_bar_delegate.h79
-rw-r--r--ui/qt/models/related_packet_delegate.cpp273
-rw-r--r--ui/qt/models/related_packet_delegate.h78
-rw-r--r--ui/qt/models/sparkline_delegate.cpp120
-rw-r--r--ui/qt/models/sparkline_delegate.h60
-rw-r--r--ui/qt/models/timeline_delegate.cpp102
-rw-r--r--ui/qt/models/timeline_delegate.h84
-rw-r--r--ui/qt/models/uat_delegate.cpp217
-rw-r--r--ui/qt/models/uat_delegate.h57
-rw-r--r--ui/qt/models/uat_model.cpp417
-rw-r--r--ui/qt/models/uat_model.h70
-rw-r--r--ui/qt/models/voip_calls_info_model.cpp246
-rw-r--r--ui/qt/models/voip_calls_info_model.h96
30 files changed, 5809 insertions, 0 deletions
diff --git a/ui/qt/models/cache_proxy_model.cpp b/ui/qt/models/cache_proxy_model.cpp
new file mode 100644
index 0000000000..f98f59528b
--- /dev/null
+++ b/ui/qt/models/cache_proxy_model.cpp
@@ -0,0 +1,112 @@
+/* cache_proxy_model.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/cache_proxy_model.h>
+
+CacheProxyModel::CacheProxyModel(QObject *parent) : QIdentityProxyModel(parent)
+{
+}
+
+QVariant CacheProxyModel::data(const QModelIndex &index, int role) const
+{
+ QModelIndex dataIndex = cache.index(index.row(), index.column());
+ if (!dataIndex.isValid()) {
+ // index is possibly outside columnCount or rowCount
+ return QVariant();
+ }
+
+ if (hasModel()) {
+ QVariant value = QIdentityProxyModel::data(index, role);
+ cache.setData(dataIndex, value, role);
+ return value;
+ } else {
+ return cache.data(dataIndex, role);
+ }
+}
+
+Qt::ItemFlags CacheProxyModel::flags(const QModelIndex &index) const
+{
+ if (hasModel()) {
+ return QIdentityProxyModel::flags(index);
+ } else {
+ // Override default to prevent editing.
+ return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
+ }
+}
+
+QVariant CacheProxyModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (hasModel()) {
+ QVariant value = QIdentityProxyModel::headerData(section, orientation, role);
+ cache.setHeaderData(section, orientation, value, role);
+ return value;
+ } else {
+ return cache.headerData(section, orientation, role);
+ }
+}
+
+int CacheProxyModel::rowCount(const QModelIndex &parent) const
+{
+ if (hasModel()) {
+ int count = QIdentityProxyModel::rowCount(parent);
+ cache.setRowCount(count);
+ return count;
+ } else {
+ return cache.rowCount(parent);
+ }
+}
+
+int CacheProxyModel::columnCount(const QModelIndex &parent) const
+{
+ if (hasModel()) {
+ int count = QIdentityProxyModel::columnCount(parent);
+ cache.setColumnCount(count);
+ return count;
+ } else {
+ return cache.columnCount(parent);
+ }
+}
+
+/**
+ * Sets the source model from which data must be pulled. If newSourceModel is
+ * NULL, then the cache will be used.
+ */
+void CacheProxyModel::setSourceModel(QAbstractItemModel *newSourceModel)
+{
+ if (newSourceModel) {
+ cache.clear();
+ QIdentityProxyModel::setSourceModel(newSourceModel);
+ connect(newSourceModel, SIGNAL(modelReset()),
+ this, SLOT(resetCacheModel()));
+ } else {
+ if (sourceModel()) {
+ // Prevent further updates to source model from invalidating cache.
+ disconnect(sourceModel(), SIGNAL(modelReset()),
+ this, SLOT(resetCacheModel()));
+ }
+ QIdentityProxyModel::setSourceModel(&cache);
+ }
+}
+
+void CacheProxyModel::resetCacheModel() {
+ cache.clear();
+}
diff --git a/ui/qt/models/cache_proxy_model.h b/ui/qt/models/cache_proxy_model.h
new file mode 100644
index 0000000000..9163a4695c
--- /dev/null
+++ b/ui/qt/models/cache_proxy_model.h
@@ -0,0 +1,59 @@
+/* cache_proxy_model.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CACHE_PROXY_MODEL_H
+#define CACHE_PROXY_MODEL_H
+
+#include <config.h>
+
+#include <QIdentityProxyModel>
+#include <QStandardItemModel>
+
+/**
+ * Caches any data read access to the source model, returning an older copy if
+ * the source model is invalidated.
+ *
+ * Only flat data is supported at the moment, tree models (with parents) are
+ * unsupported.
+ */
+class CacheProxyModel : public QIdentityProxyModel
+{
+ Q_OBJECT
+
+public:
+ CacheProxyModel(QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ void setSourceModel(QAbstractItemModel *newSourceModel);
+
+private:
+ mutable QStandardItemModel cache;
+
+ bool hasModel() const { return sourceModel() != &cache; }
+
+private slots:
+ void resetCacheModel();
+};
+#endif
diff --git a/ui/qt/models/interface_sort_filter_model.cpp b/ui/qt/models/interface_sort_filter_model.cpp
new file mode 100644
index 0000000000..ddaebb1c9c
--- /dev/null
+++ b/ui/qt/models/interface_sort_filter_model.cpp
@@ -0,0 +1,396 @@
+/* interface_sort_filter_model.cpp
+ * Proxy model for the display of interface data for the interface tree
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/interface_tree_model.h>
+#include <ui/qt/models/interface_tree_cache_model.h>
+#include <ui/qt/models/interface_sort_filter_model.h>
+
+#include <glib.h>
+
+#include <epan/prefs.h>
+#include <ui/preference_utils.h>
+#include <ui/qt/utils/qt_ui_utils.h>
+
+#include "wireshark_application.h"
+
+#include <QAbstractItemModel>
+
+InterfaceSortFilterModel::InterfaceSortFilterModel(QObject *parent) :
+ QSortFilterProxyModel(parent)
+{
+ resetAllFilter();
+}
+
+void InterfaceSortFilterModel::resetAllFilter()
+{
+ _filterHidden = true;
+ _filterTypes = true;
+ _invertTypeFilter = false;
+ _storeOnChange = false;
+#ifdef HAVE_PCAP_REMOTE
+ _remoteDisplay = true;
+#endif
+
+ /* Adding all columns, to have a default setting */
+ for ( int col = 0; col < IFTREE_COL_MAX; col++ )
+ _columns.append((InterfaceTreeColumns)col);
+
+ invalidateFilter();
+ invalidate();
+}
+
+void InterfaceSortFilterModel::setStoreOnChange(bool storeOnChange)
+{
+ _storeOnChange = storeOnChange;
+ if ( storeOnChange )
+ {
+ connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(resetPreferenceData()));
+ resetPreferenceData();
+ }
+}
+
+void InterfaceSortFilterModel::setFilterHidden(bool filter)
+{
+ _filterHidden = filter;
+
+ invalidate();
+}
+
+#ifdef HAVE_PCAP_REMOTE
+void InterfaceSortFilterModel::setRemoteDisplay(bool remoteDisplay)
+{
+ _remoteDisplay = remoteDisplay;
+
+ invalidate();
+}
+
+bool InterfaceSortFilterModel::remoteDisplay()
+{
+ return _remoteDisplay;
+}
+
+void InterfaceSortFilterModel::toggleRemoteDisplay()
+{
+ _remoteDisplay = ! _remoteDisplay;
+
+ if ( _storeOnChange )
+ {
+ prefs.gui_interfaces_remote_display = ! _remoteDisplay;
+
+ prefs_main_write();
+ }
+
+ invalidateFilter();
+ invalidate();
+}
+
+bool InterfaceSortFilterModel::remoteInterfacesExist()
+{
+ bool exist = false;
+
+ if ( sourceModel()->rowCount() == 0 )
+ return exist;
+
+ for (int idx = 0; idx < sourceModel()->rowCount() && ! exist; idx++)
+ exist = ((InterfaceTreeModel *)sourceModel())->isRemote(idx);
+
+ return exist;
+}
+#endif
+
+void InterfaceSortFilterModel::setFilterByType(bool filter, bool invert)
+{
+ _filterTypes = filter;
+ _invertTypeFilter = invert;
+
+ invalidate();
+}
+
+void InterfaceSortFilterModel::resetPreferenceData()
+{
+ displayHiddenTypes.clear();
+ QString stored_prefs(prefs.gui_interfaces_hide_types);
+ if ( stored_prefs.length() > 0 )
+ {
+ QStringList ifTypesStored = stored_prefs.split(',');
+ QStringList::const_iterator it = ifTypesStored.constBegin();
+ while(it != ifTypesStored.constEnd())
+ {
+ int i_val = (*it).toInt();
+ if ( ! displayHiddenTypes.contains(i_val) )
+ displayHiddenTypes.append(i_val);
+ ++it;
+ }
+ }
+
+#if 0
+ // Disabled until bug 13354 is fixed
+ _filterHidden = ! prefs.gui_interfaces_show_hidden;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ _remoteDisplay = prefs.gui_interfaces_remote_display;
+#endif
+
+ invalidate();
+}
+
+bool InterfaceSortFilterModel::filterHidden() const
+{
+ return _filterHidden;
+}
+
+void InterfaceSortFilterModel::toggleFilterHidden()
+{
+ _filterHidden = ! _filterHidden;
+
+ if ( _storeOnChange )
+ {
+ prefs.gui_interfaces_show_hidden = ! _filterHidden;
+
+ prefs_main_write();
+ }
+
+ invalidateFilter();
+ invalidate();
+}
+
+bool InterfaceSortFilterModel::filterByType() const
+{
+ return _filterTypes;
+}
+
+int InterfaceSortFilterModel::interfacesHidden()
+{
+#ifdef HAVE_LIBPCAP
+ if ( ! global_capture_opts.all_ifaces )
+ return 0;
+#endif
+
+ return sourceModel()->rowCount() - rowCount();
+}
+
+QList<int> InterfaceSortFilterModel::typesDisplayed()
+{
+ QList<int> shownTypes;
+
+ if ( sourceModel()->rowCount() == 0 )
+ return shownTypes;
+
+ for (int idx = 0; idx < sourceModel()->rowCount(); idx++)
+ {
+ int type = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_TYPE).toInt();
+ bool hidden = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_HIDDEN).toBool();
+
+ if ( ! hidden )
+ {
+ if ( ! shownTypes.contains(type) )
+ shownTypes.append(type);
+ }
+ }
+
+ return shownTypes;
+}
+
+void InterfaceSortFilterModel::setInterfaceTypeVisible(int ifType, bool visible)
+{
+ if ( visible && displayHiddenTypes.contains(ifType) )
+ displayHiddenTypes.removeAll(ifType);
+ else if ( ! visible && ! displayHiddenTypes.contains(ifType) )
+ displayHiddenTypes.append(ifType);
+ else
+ /* Nothing should have changed */
+ return;
+
+ if ( _storeOnChange )
+ {
+ QString new_pref;
+ QList<int>::const_iterator it = displayHiddenTypes.constBegin();
+ while( it != displayHiddenTypes.constEnd() )
+ {
+ new_pref.append(QString("%1,").arg(*it));
+ ++it;
+ }
+ if (new_pref.length() > 0)
+ new_pref = new_pref.left(new_pref.length() - 1);
+
+ prefs.gui_interfaces_hide_types = qstring_strdup(new_pref);
+
+ prefs_main_write();
+ }
+
+ invalidateFilter();
+ invalidate();
+}
+
+void InterfaceSortFilterModel::toggleTypeVisibility(int ifType)
+{
+ bool checked = isInterfaceTypeShown(ifType);
+
+ setInterfaceTypeVisible(ifType, checked ? false : true);
+}
+
+bool InterfaceSortFilterModel::isInterfaceTypeShown(int ifType) const
+{
+ bool result = false;
+
+ if ( displayHiddenTypes.size() == 0 )
+ result = true;
+ else if ( ! displayHiddenTypes.contains(ifType) )
+ result = true;
+
+ return ( ( _invertTypeFilter && ! result ) || ( ! _invertTypeFilter && result ) );
+}
+
+bool InterfaceSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const
+{
+ QModelIndex realIndex = sourceModel()->index(sourceRow, 0, sourceParent);
+
+ if ( ! realIndex.isValid() )
+ return false;
+
+#ifdef HAVE_LIBPCAP
+ int idx = realIndex.row();
+
+ /* No data loaded, we do not display anything */
+ if ( sourceModel()->rowCount() == 0 )
+ return false;
+
+ int type = -1;
+ bool hidden = false;
+
+ if (dynamic_cast<InterfaceTreeCacheModel*>(sourceModel()) != 0)
+ {
+ type = ((InterfaceTreeCacheModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_TYPE).toInt();
+ hidden = ((InterfaceTreeCacheModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::UserRole).toBool();
+ }
+ else if (dynamic_cast<InterfaceTreeModel*>(sourceModel()) != 0)
+ {
+ type = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_TYPE).toInt();
+ hidden = ((InterfaceTreeModel *)sourceModel())->getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::UserRole).toBool();
+ }
+ else
+ return false;
+
+ if ( hidden && _filterHidden )
+ return false;
+
+ if ( _filterTypes && ! isInterfaceTypeShown(type) )
+ {
+#ifdef HAVE_PCAP_REMOTE
+ /* Remote interfaces have the if type IF_WIRED, therefore would be filtered, if not explicitly checked here */
+ if ( type != IF_WIRED || ! ((InterfaceTreeModel *)sourceModel())->isRemote(idx) )
+#endif
+ return false;
+ }
+
+#ifdef HAVE_PCAP_REMOTE
+ if ( ((InterfaceTreeModel *)sourceModel())->isRemote(idx) )
+ {
+ if ( ! _remoteDisplay )
+ return false;
+ }
+#endif
+
+#endif
+
+ return true;
+}
+
+bool InterfaceSortFilterModel::filterAcceptsColumn(int sourceColumn, const QModelIndex & sourceParent) const
+{
+ QModelIndex realIndex = sourceModel()->index(0, sourceColumn, sourceParent);
+
+ if ( ! realIndex.isValid() )
+ return false;
+
+ if ( ! _columns.contains((InterfaceTreeColumns)sourceColumn) )
+ return false;
+
+ return true;
+}
+
+void InterfaceSortFilterModel::setColumns(QList<InterfaceTreeColumns> columns)
+{
+ _columns.clear();
+ _columns.append(columns);
+}
+
+int InterfaceSortFilterModel::mapSourceToColumn(InterfaceTreeColumns mdlIndex)
+{
+ if ( ! _columns.contains(mdlIndex) )
+ return -1;
+
+ return _columns.indexOf(mdlIndex, 0);
+}
+
+QModelIndex InterfaceSortFilterModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ if ( ! proxyIndex.isValid() )
+ return QModelIndex();
+
+ if ( ! sourceModel() )
+ return QModelIndex();
+
+ QModelIndex baseIndex = QSortFilterProxyModel::mapToSource(proxyIndex);
+ QModelIndex newIndex = sourceModel()->index(baseIndex.row(), _columns.at(proxyIndex.column()));
+
+ return newIndex;
+}
+
+QModelIndex InterfaceSortFilterModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ if ( ! sourceIndex.isValid() )
+ return QModelIndex();
+ else if ( ! _columns.contains( (InterfaceTreeColumns) sourceIndex.column() ) )
+ return QModelIndex();
+
+ QModelIndex newIndex = QSortFilterProxyModel::mapFromSource(sourceIndex);
+
+ return index(newIndex.row(), _columns.indexOf((InterfaceTreeColumns) sourceIndex.column()));
+}
+
+QString InterfaceSortFilterModel::interfaceError()
+{
+ QString result;
+
+ InterfaceTreeModel * sourceModel = dynamic_cast<InterfaceTreeModel *>(this->sourceModel());
+ if ( sourceModel != NULL )
+ result = sourceModel->interfaceError();
+
+ if ( result.size() == 0 && rowCount() == 0 )
+ result = QString(tr("No interfaces to be displayed. %1 interfaces filtered.")).arg(interfacesHidden());
+
+ return result;
+}
+
+/*
+ * 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/interface_sort_filter_model.h b/ui/qt/models/interface_sort_filter_model.h
new file mode 100644
index 0000000000..5a6f35b97c
--- /dev/null
+++ b/ui/qt/models/interface_sort_filter_model.h
@@ -0,0 +1,106 @@
+/* interface_sort_filter_model.h
+ * Proxy model for the display of interface data for the interface tree
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_SORT_FILTER_MODEL_H
+#define INTERFACE_SORT_FILTER_MODEL_H
+
+#include <config.h>
+
+#include <ui/qt/models/interface_tree_model.h>
+
+#include <glib.h>
+
+#include <QSortFilterProxyModel>
+
+class InterfaceSortFilterModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ InterfaceSortFilterModel(QObject *parent);
+
+ void setStoreOnChange(bool storeOnChange);
+ void resetAllFilter();
+
+ void setFilterHidden(bool filter);
+ bool filterHidden() const;
+ int interfacesHidden();
+ void toggleFilterHidden();
+
+#ifdef HAVE_PCAP_REMOTE
+ void setRemoteDisplay(bool remoteDisplay);
+ bool remoteDisplay();
+ void toggleRemoteDisplay();
+ bool remoteInterfacesExist();
+#endif
+
+ void setInterfaceTypeVisible(int ifType, bool visible);
+ bool isInterfaceTypeShown(int ifType) const;
+ void setFilterByType(bool filter, bool invert = false);
+ bool filterByType() const;
+ void toggleTypeVisibility(int ifType);
+
+ QList<int> typesDisplayed();
+
+ void setColumns(QList<InterfaceTreeColumns> columns);
+ int mapSourceToColumn(InterfaceTreeColumns mdlIndex);
+
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
+
+ QString interfaceError();
+
+protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const;
+ bool filterAcceptsColumn(int source_column, const QModelIndex & source_parent) const;
+
+private:
+ bool _filterHidden;
+ bool _filterTypes;
+ bool _invertTypeFilter;
+ bool _storeOnChange;
+
+#ifdef HAVE_PCAP_REMOTE
+ bool _remoteDisplay;
+#endif
+
+ QList<int> displayHiddenTypes;
+
+ QList<InterfaceTreeColumns> _columns;
+
+private slots:
+ void resetPreferenceData();
+};
+
+#endif // INTERFACE_SORT_FILTER_MODEL_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:
+ */
diff --git a/ui/qt/models/interface_tree_cache_model.cpp b/ui/qt/models/interface_tree_cache_model.cpp
new file mode 100644
index 0000000000..0937dbcde6
--- /dev/null
+++ b/ui/qt/models/interface_tree_cache_model.cpp
@@ -0,0 +1,620 @@
+/* interface_tree_cache_model.cpp
+ * Model caching interface changes before sending them to global storage
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include <ui/qt/models/interface_tree_cache_model.h>
+
+#include "glib.h"
+
+#include "epan/prefs.h"
+
+#include <ui/qt/utils/qt_ui_utils.h>
+#include "ui/capture_globals.h"
+#include "wsutil/utf8_entities.h"
+
+#include "wiretap/wtap.h"
+
+#include "wireshark_application.h"
+
+#include <QIdentityProxyModel>
+
+InterfaceTreeCacheModel::InterfaceTreeCacheModel(QObject *parent) :
+ QIdentityProxyModel(parent)
+{
+ /* ATTENTION: This cache model is not intended to be used with anything
+ * else then InterfaceTreeModel, and will break with anything else
+ * leading to unintended results. */
+ sourceModel = new InterfaceTreeModel(parent);
+
+ QIdentityProxyModel::setSourceModel(sourceModel);
+ storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>();
+
+ checkableColumns << IFTREE_COL_HIDDEN << IFTREE_COL_PROMISCUOUSMODE;
+#ifdef HAVE_PCAP_CREATE
+ checkableColumns << IFTREE_COL_MONITOR_MODE;
+#endif
+
+ editableColumns << IFTREE_COL_INTERFACE_COMMENT << IFTREE_COL_SNAPLEN << IFTREE_COL_PIPE_PATH;
+
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ editableColumns << IFTREE_COL_BUFFERLEN;
+#endif
+}
+
+InterfaceTreeCacheModel::~InterfaceTreeCacheModel()
+{
+#ifdef HAVE_LIBPCAP
+ /* This list should only exist, if the dialog is closed, without calling save first */
+ newDevices.clear();
+#endif
+
+ delete storage;
+ delete sourceModel;
+}
+
+QVariant InterfaceTreeCacheModel::getColumnContent(int idx, int col, int role)
+{
+ return InterfaceTreeCacheModel::data(index(idx, col), role);
+}
+
+#ifdef HAVE_LIBPCAP
+void InterfaceTreeCacheModel::reset(int row)
+{
+ if ( row < 0 )
+ {
+ delete storage;
+ storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>();
+ }
+ else
+ {
+ if ( storage->count() > row )
+ storage->remove(storage->keys().at(row));
+ }
+}
+
+void InterfaceTreeCacheModel::saveNewDevices()
+{
+ QList<interface_t>::const_iterator it = newDevices.constBegin();
+ /* idx is used for iterating only over the indices of the new devices. As all new
+ * devices are stored with an index higher then sourceModel->rowCount(), we start
+ * only with those storage indices.
+ * it is just the iterator over the new devices. A new device must not necessarily
+ * have storage, which will lead to that device not being stored in global_capture_opts */
+ for (int idx = sourceModel->rowCount(); it != newDevices.constEnd(); ++it, idx++)
+ {
+ interface_t *device = const_cast<interface_t *>(&(*it));
+ bool useDevice = false;
+
+ QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0);
+ /* When devices are being added, they are added using generic values. So only devices
+ * whose data have been changed should be used from here on out. */
+ if ( dataField != 0 )
+ {
+ if ( device->if_info.type != IF_PIPE )
+ {
+ continue;
+ }
+
+ if ( device->if_info.type == IF_PIPE )
+ {
+ QVariant saveValue = dataField->value(IFTREE_COL_PIPE_PATH);
+ if ( saveValue.isValid() )
+ {
+ g_free(device->if_info.name);
+ device->if_info.name = qstring_strdup(saveValue.toString());
+
+ g_free(device->name);
+ device->name = qstring_strdup(saveValue.toString());
+
+ g_free(device->display_name);
+ device->display_name = qstring_strdup(saveValue.toString());
+ useDevice = true;
+ }
+ }
+
+ if ( useDevice )
+ g_array_append_val(global_capture_opts.all_ifaces, *device);
+
+ }
+
+ /* All entries of this new devices have been considered */
+ storage->remove(idx);
+ delete dataField;
+ }
+
+ newDevices.clear();
+}
+
+void InterfaceTreeCacheModel::save()
+{
+ if ( storage->count() == 0 )
+ return;
+
+ QMap<char**, QStringList> prefStorage;
+
+ /* No devices are hidden until checking "Show" state */
+ prefStorage[&prefs.capture_devices_hide] = QStringList();
+
+ /* Storing new devices first including their changed values */
+ saveNewDevices();
+
+
+ for(unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++)
+ {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+
+ if (! device.name )
+ continue;
+
+ /* Try to load a saved value row for this index */
+ QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0);
+
+ /* Handle the storing of values for this device here */
+ if ( dataField )
+ {
+ QMap<InterfaceTreeColumns, QVariant>::const_iterator it = dataField->constBegin();
+ while ( it != dataField->constEnd() )
+ {
+ InterfaceTreeColumns col = it.key();
+ QVariant saveValue = it.value();
+
+ /* Setting the field values for each individual saved value cannot be generic, as the
+ * struct cannot be accessed in a generic way. Therefore below, each individually changed
+ * value has to be handled separately. Comments are stored only in the preference file
+ * and applied to the data name during loading. Therefore comments are not handled here */
+
+ if ( col == IFTREE_COL_HIDDEN )
+ {
+ device.hidden = saveValue.toBool();
+ }
+#ifdef HAVE_EXTCAP
+ else if ( device.if_info.type == IF_EXTCAP )
+ {
+ /* extcap interfaces do not have the following columns.
+ * ATTENTION: all generic columns must be added, BEFORE this
+ * if-clause, or they will be ignored for extcap interfaces */
+ }
+#endif
+ else if ( col == IFTREE_COL_PROMISCUOUSMODE )
+ {
+ device.pmode = saveValue.toBool();
+ }
+#ifdef HAVE_PCAP_CREATE
+ else if ( col == IFTREE_COL_MONITOR_MODE )
+ {
+ device.monitor_mode_enabled = saveValue.toBool();
+ }
+#endif
+ else if ( col == IFTREE_COL_SNAPLEN )
+ {
+ int iVal = saveValue.toInt();
+ if ( iVal != WTAP_MAX_PACKET_SIZE_STANDARD )
+ {
+ device.has_snaplen = true;
+ device.snaplen = iVal;
+ }
+ else
+ {
+ device.has_snaplen = false;
+ device.snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
+ }
+ }
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ else if ( col == IFTREE_COL_BUFFERLEN )
+ {
+ device.buffer = saveValue.toInt();
+ }
+#endif
+
+ global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, idx);
+ g_array_insert_val(global_capture_opts.all_ifaces, idx, device);
+
+ ++it;
+ }
+ }
+
+ QVariant content = getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::CheckStateRole);
+ if ( content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Unchecked )
+ prefStorage[&prefs.capture_devices_hide] << QString(device.name);
+
+ content = getColumnContent(idx, IFTREE_COL_INTERFACE_COMMENT);
+ if ( content.isValid() && content.toString().size() > 0 )
+ prefStorage[&prefs.capture_devices_descr] << QString("%1(%2)").arg(device.name).arg(content.toString());
+
+ bool allowExtendedColumns = true;
+#ifdef HAVE_EXTCAP
+ if ( device.if_info.type == IF_EXTCAP )
+ allowExtendedColumns = false;
+#endif
+ if ( allowExtendedColumns )
+ {
+ content = getColumnContent(idx, IFTREE_COL_PROMISCUOUSMODE, Qt::CheckStateRole);
+ if ( content.isValid() )
+ {
+ bool value = static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked;
+ prefStorage[&prefs.capture_devices_pmode] << QString("%1(%2)").arg(device.name).arg(value ? 1 : 0);
+ }
+
+#ifdef HAVE_PCAP_CREATE
+ content = getColumnContent(idx, IFTREE_COL_MONITOR_MODE, Qt::CheckStateRole);
+ if ( content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked )
+ prefStorage[&prefs.capture_devices_monitor_mode] << QString(device.name);
+#endif
+
+ content = getColumnContent(idx, IFTREE_COL_SNAPLEN);
+ if ( content.isValid() )
+ {
+ int value = content.toInt();
+ prefStorage[&prefs.capture_devices_snaplen] <<
+ QString("%1:%2(%3)").arg(device.name).
+ arg(device.has_snaplen ? 1 : 0).
+ arg(device.has_snaplen ? value : WTAP_MAX_PACKET_SIZE_STANDARD);
+ }
+
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ content = getColumnContent(idx, IFTREE_COL_BUFFERLEN);
+ if ( content.isValid() )
+ {
+ int value = content.toInt();
+ if ( value != -1 )
+ {
+ prefStorage[&prefs.capture_devices_buffersize] <<
+ QString("%1(%2)").arg(device.name).
+ arg(value);
+ }
+ }
+#endif
+ }
+ }
+
+ QMap<char**, QStringList>::const_iterator it = prefStorage.constBegin();
+ while ( it != prefStorage.constEnd() )
+ {
+ char ** key = it.key();
+
+ g_free(*key);
+ *key = qstring_strdup(it.value().join(","));
+
+ ++it;
+ }
+
+ wsApp->emitAppSignal(WiresharkApplication::LocalInterfacesChanged);
+}
+#endif
+
+int InterfaceTreeCacheModel::rowCount(const QModelIndex & parent) const
+{
+ int totalCount = sourceModel->rowCount(parent);
+#ifdef HAVE_LIBPCAP
+ totalCount += newDevices.size();
+#endif
+ return totalCount;
+}
+
+bool InterfaceTreeCacheModel::changeIsAllowed(InterfaceTreeColumns col) const
+{
+ if ( editableColumns.contains(col) || checkableColumns.contains(col) )
+ return true;
+ return false;
+}
+
+#ifdef HAVE_LIBPCAP
+const interface_t * InterfaceTreeCacheModel::lookup(const QModelIndex &index) const
+{
+ const interface_t * result = 0;
+
+ if ( ! index.isValid() )
+ return result;
+ if ( ! global_capture_opts.all_ifaces && newDevices.size() == 0 )
+ return result;
+
+ int idx = index.row();
+
+ if ( (unsigned int) idx >= global_capture_opts.all_ifaces->len )
+ {
+ idx = idx - global_capture_opts.all_ifaces->len;
+ if ( idx < newDevices.size() )
+ result = &newDevices[idx];
+ }
+ else
+ {
+ result = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+ }
+
+ return result;
+}
+#endif
+
+/* This checks if the column can be edited for the given index. This differs from
+ * isAvailableField in such a way, that it is only used in flags and not any
+ * other method.*/
+bool InterfaceTreeCacheModel::isAllowedToBeEdited(const QModelIndex &index) const
+{
+ Q_UNUSED(index)
+
+#ifdef HAVE_LIBPCAP
+ const interface_t * device = lookup(index);
+ if ( device == 0 )
+ return false;
+
+#ifdef HAVE_EXTCAP
+ InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
+ if ( device->if_info.type == IF_EXTCAP )
+ {
+ /* extcap interfaces do not have those settings */
+ if ( col == IFTREE_COL_PROMISCUOUSMODE || col == IFTREE_COL_SNAPLEN )
+ return false;
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ if ( col == IFTREE_COL_BUFFERLEN )
+ return false;
+#endif
+ }
+#endif
+
+#endif
+ return true;
+}
+
+// Whether this field is available for modification and display.
+bool InterfaceTreeCacheModel::isAvailableField(const QModelIndex &index) const
+{
+ Q_UNUSED(index)
+
+#ifdef HAVE_LIBPCAP
+ const interface_t * device = lookup(index);
+
+ if ( device == 0 )
+ return false;
+
+ InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
+ if ( col == IFTREE_COL_HIDDEN )
+ {
+ // Do not allow default capture interface to be hidden.
+ if ( ! g_strcmp0(prefs.capture_device, device->display_name) )
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+Qt::ItemFlags InterfaceTreeCacheModel::flags(const QModelIndex &index) const
+{
+ if ( ! index.isValid() )
+ return 0;
+
+ Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+
+ InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
+
+ if ( changeIsAllowed(col) && isAvailableField(index) && isAllowedToBeEdited(index) )
+ {
+ if ( checkableColumns.contains(col) )
+ {
+ flags |= Qt::ItemIsUserCheckable;
+ }
+ else
+ {
+ flags |= Qt::ItemIsEditable;
+ }
+ }
+
+ return flags;
+}
+
+bool InterfaceTreeCacheModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if ( ! index.isValid() )
+ return false;
+
+ if ( ! isAvailableField(index) )
+ return false;
+
+ int row = index.row();
+ InterfaceTreeColumns col = (InterfaceTreeColumns)index.column();
+
+ if ( role == Qt::CheckStateRole || role == Qt::EditRole )
+ {
+ if ( changeIsAllowed( col ) )
+ {
+ QVariant saveValue = value;
+
+ QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
+ /* obtain the list of already stored changes for this row. If none exist
+ * create a new storage row for this entry */
+ if ( ( dataField = storage->value(row, 0) ) == 0 )
+ {
+ dataField = new QMap<InterfaceTreeColumns, QVariant>();
+ storage->insert(row, dataField);
+ }
+
+ dataField->insert(col, saveValue);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QVariant InterfaceTreeCacheModel::data(const QModelIndex &index, int role) const
+{
+ if ( ! index.isValid() )
+ return QVariant();
+
+ int row = index.row();
+
+ InterfaceTreeColumns col = (InterfaceTreeColumns)index.column();
+
+ if ( isAvailableField(index) && isAllowedToBeEdited(index) )
+ {
+ if ( ( ( role == Qt::DisplayRole || role == Qt::EditRole ) && editableColumns.contains(col) ) ||
+ ( role == Qt::CheckStateRole && checkableColumns.contains(col) ) )
+ {
+ QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
+ if ( ( dataField = storage->value(row, 0) ) != 0 )
+ {
+ if ( dataField->contains(col) )
+ {
+ return dataField->value(col, QVariant());
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( role == Qt::CheckStateRole )
+ return QVariant();
+ else if ( role == Qt::DisplayRole )
+ return QString(UTF8_EM_DASH);
+ }
+
+ if ( row < sourceModel->rowCount() )
+ {
+ return sourceModel->data(index, role);
+ }
+#ifdef HAVE_LIBPCAP
+ else
+ {
+ /* Handle all fields, which will have to be displayed for new devices. Only pipes
+ * are supported at the moment, so the information to be displayed is pretty limited.
+ * After saving, the devices are stored in global_capture_opts and no longer
+ * classify as new devices. */
+ const interface_t * device = lookup(index);
+
+ if ( device != 0 )
+ {
+ if ( role == Qt::DisplayRole || role == Qt::EditRole )
+ {
+ if ( col == IFTREE_COL_PIPE_PATH ||
+ col == IFTREE_COL_NAME ||
+ col == IFTREE_COL_INTERFACE_NAME )
+ {
+
+ QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
+ if ( ( dataField = storage->value(row, 0) ) != 0 &&
+ dataField->contains(IFTREE_COL_PIPE_PATH) )
+ {
+ return dataField->value(IFTREE_COL_PIPE_PATH, QVariant());
+ }
+ else
+ return QString(device->name);
+ }
+ else if ( col == IFTREE_COL_TYPE )
+ {
+ return QVariant::fromValue((int)device->if_info.type);
+ }
+ }
+ else if ( role == Qt::CheckStateRole )
+ {
+ if ( col == IFTREE_COL_HIDDEN )
+ {
+ // Do not allow default capture interface to be hidden.
+ if ( ! g_strcmp0(prefs.capture_device, device->display_name) )
+ return QVariant();
+
+ /* Hidden is a de-selection, therefore inverted logic here */
+ return device->hidden ? Qt::Unchecked : Qt::Checked;
+ }
+ }
+ }
+ }
+#endif
+
+ return QVariant();
+}
+
+#ifdef HAVE_LIBPCAP
+QModelIndex InterfaceTreeCacheModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if ( row >= sourceModel->rowCount() && ( row - sourceModel->rowCount() ) < newDevices.count() )
+ {
+ return createIndex(row, column, (void *)0);
+ }
+
+ return sourceModel->index(row, column, parent);
+}
+
+void InterfaceTreeCacheModel::addDevice(const interface_t * newDevice)
+{
+ emit beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ newDevices << *newDevice;
+ emit endInsertRows();
+}
+
+void InterfaceTreeCacheModel::deleteDevice(const QModelIndex &index)
+{
+ if ( ! index.isValid() )
+ return;
+
+ emit beginRemoveRows(QModelIndex(), index.row(), index.row());
+
+ int row = index.row();
+
+ /* device is in newDevices */
+ if ( row >= sourceModel->rowCount() )
+ {
+ int newDeviceIdx = row - sourceModel->rowCount();
+
+ newDevices.removeAt(newDeviceIdx);
+ if ( storage->contains(index.row()) )
+ storage->remove(index.row());
+
+ /* The storage array has to be resorted, if the index, that was removed
+ * had been in the middle of the array. Can't start at index.row(), as
+ * it may not be contained in storage
+ * We must iterate using a list, not an iterator, otherwise the change
+ * will fold on itself. */
+ QList<int> storageKeys = storage->keys();
+ for ( int i = 0; i < storageKeys.size(); ++i )
+ {
+ int key = storageKeys.at(i);
+ if ( key > index.row() )
+ {
+ storage->insert(key - 1, storage->value(key));
+ storage->remove(key);
+ }
+ }
+
+ emit endRemoveRows();
+ }
+ else
+ {
+ g_array_remove_index(global_capture_opts.all_ifaces, row);
+ emit endRemoveRows();
+ wsApp->emitAppSignal(WiresharkApplication::LocalInterfacesChanged);
+ }
+}
+#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:
+ */
diff --git a/ui/qt/models/interface_tree_cache_model.h b/ui/qt/models/interface_tree_cache_model.h
new file mode 100644
index 0000000000..3898857463
--- /dev/null
+++ b/ui/qt/models/interface_tree_cache_model.h
@@ -0,0 +1,92 @@
+/* interface_tree_cache_model.h
+ * Model caching interface changes before sending them to global storage
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TREE_CACHE_MODEL_H_
+#define INTERFACE_TREE_CACHE_MODEL_H_
+
+#include <ui/qt/models/interface_tree_model.h>
+
+#include <QMap>
+#include <QAbstractItemModel>
+#include <QIdentityProxyModel>
+
+class InterfaceTreeCacheModel : public QIdentityProxyModel
+{
+ Q_OBJECT
+
+public:
+ explicit InterfaceTreeCacheModel(QObject *parent);
+ ~InterfaceTreeCacheModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+
+ QVariant getColumnContent(int idx, int col, int role = Qt::DisplayRole);
+
+#ifdef HAVE_LIBPCAP
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+
+ void reset(int row);
+ void save();
+
+ void addDevice(const interface_t * newDevice);
+ void deleteDevice(const QModelIndex &index);
+#endif
+
+private:
+ InterfaceTreeModel * sourceModel;
+
+#ifdef HAVE_LIBPCAP
+ QList<interface_t> newDevices;
+
+ void saveNewDevices();
+#endif
+ QMap<int, QMap<InterfaceTreeColumns, QVariant> *> * storage;
+ QList<InterfaceTreeColumns> editableColumns;
+ QList<InterfaceTreeColumns> checkableColumns;
+
+#ifdef HAVE_LIBPCAP
+ const interface_t * lookup(const QModelIndex &index) const;
+#endif
+
+ bool changeIsAllowed(InterfaceTreeColumns col) const;
+ bool isAvailableField(const QModelIndex &index) const;
+ bool isAllowedToBeEdited(const QModelIndex &index) const;
+
+};
+#endif /* INTERFACE_TREE_CACHE_MODEL_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:
+ */
diff --git a/ui/qt/models/interface_tree_model.cpp b/ui/qt/models/interface_tree_model.cpp
new file mode 100644
index 0000000000..7acda1340e
--- /dev/null
+++ b/ui/qt/models/interface_tree_model.cpp
@@ -0,0 +1,558 @@
+/* interface_tree_model.cpp
+ * Model for the interface data for display in the interface frame
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <ui/qt/models/interface_tree_model.h>
+
+#ifdef HAVE_LIBPCAP
+#include "ui/capture.h"
+#include "caputils/capture-pcap-util.h"
+#include "capture_opts.h"
+#include "ui/capture_ui_utils.h"
+#include "ui/capture_globals.h"
+#endif
+
+#include "wsutil/filesystem.h"
+
+#include <ui/qt/utils/qt_ui_utils.h>
+#include <ui/qt/utils/stock_icon.h>
+#include "wireshark_application.h"
+
+/* Needed for the meta type declaration of QList<int>* */
+#include <ui/qt/models/sparkline_delegate.h>
+
+#ifdef HAVE_EXTCAP
+#include "extcap.h"
+#endif
+
+const QString InterfaceTreeModel::DefaultNumericValue = QObject::tr("default");
+
+/**
+ * This is the data model for interface trees. It implies, that the index within
+ * global_capture_opts.all_ifaces is identical to the row. This is always the case, even
+ * when interfaces are hidden by the proxy model. But for this to work, every access
+ * to the index from within the view, has to be filtered through the proxy model.
+ */
+InterfaceTreeModel::InterfaceTreeModel(QObject *parent) :
+ QAbstractTableModel(parent)
+#ifdef HAVE_LIBPCAP
+ ,stat_cache_(NULL)
+#endif
+{
+ connect(wsApp, SIGNAL(appInitialized()), this, SLOT(interfaceListChanged()));
+ connect(wsApp, SIGNAL(localInterfaceListChanged()), this, SLOT(interfaceListChanged()));
+}
+
+InterfaceTreeModel::~InterfaceTreeModel(void)
+{
+#ifdef HAVE_LIBPCAP
+ if (stat_cache_) {
+ capture_stat_stop(stat_cache_);
+ stat_cache_ = NULL;
+ }
+#endif // HAVE_LIBPCAP
+}
+
+QString InterfaceTreeModel::interfaceError()
+{
+ QString errorText;
+ if ( rowCount() == 0 )
+ {
+ errorText = tr("No Interfaces found.");
+ }
+#ifdef HAVE_LIBPCAP
+ else if ( global_capture_opts.ifaces_err != 0 )
+ {
+ errorText = tr(global_capture_opts.ifaces_err_info);
+ }
+#endif
+
+ return errorText;
+}
+
+int InterfaceTreeModel::rowCount(const QModelIndex & ) const
+{
+#ifdef HAVE_LIBPCAP
+ return (global_capture_opts.all_ifaces ? global_capture_opts.all_ifaces->len : 0);
+#else
+ /* Currently no interfaces available for libpcap-less builds */
+ return 0;
+#endif
+}
+
+int InterfaceTreeModel::columnCount(const QModelIndex & ) const
+{
+ /* IFTREE_COL_MAX is not being displayed, it is the definition for the maximum numbers of columns */
+ return ((int) IFTREE_COL_MAX);
+}
+
+QVariant InterfaceTreeModel::data(const QModelIndex &index, int role) const
+{
+#ifdef HAVE_LIBPCAP
+ bool interfacesLoaded = true;
+ if ( ! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len == 0 )
+ interfacesLoaded = false;
+
+ if ( !index.isValid() )
+ return QVariant();
+
+ int row = index.row();
+ InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
+
+ if ( interfacesLoaded )
+ {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, row);
+
+ /* Data for display in cell */
+ if ( role == Qt::DisplayRole )
+ {
+ /* Only the name is being displayed */
+ if ( col == IFTREE_COL_NAME )
+ {
+ return QString(device.display_name);
+ }
+ else if ( col == IFTREE_COL_INTERFACE_NAME )
+ {
+ return QString(device.name);
+ }
+ else if ( col == IFTREE_COL_PIPE_PATH )
+ {
+ return QString(device.if_info.name);
+ }
+ else if ( col == IFTREE_COL_CAPTURE_FILTER )
+ {
+ if ( device.cfilter && strlen(device.cfilter) > 0 )
+ return html_escape(QString(device.cfilter));
+ }
+#ifdef HAVE_EXTCAP
+ else if ( col == IFTREE_COL_EXTCAP_PATH )
+ {
+ return QString(device.if_info.extcap);
+ }
+#endif
+ else if ( col == IFTREE_COL_SNAPLEN )
+ {
+ return device.has_snaplen ? QString::number(device.snaplen) : DefaultNumericValue;
+ }
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ else if ( col == IFTREE_COL_BUFFERLEN )
+ {
+ return QString::number(device.buffer);
+ }
+#endif
+ else if ( col == IFTREE_COL_TYPE )
+ {
+ return QVariant::fromValue((int)device.if_info.type);
+ }
+ else if ( col == IFTREE_COL_INTERFACE_COMMENT )
+ {
+ QString comment = gchar_free_to_qstring(capture_dev_user_descr_find(device.name));
+ if ( comment.length() > 0 )
+ return comment;
+ else
+ return QString(device.if_info.vendor_description);
+ }
+ else if ( col == IFTREE_COL_DLT )
+ {
+ QString linkname = QObject::tr("DLT %1").arg(device.active_dlt);
+ for (GList *list = device.links; list != NULL; list = g_list_next(list)) {
+ link_row *linkr = (link_row*)(list->data);
+ if (linkr->dlt != -1 && linkr->dlt == device.active_dlt) {
+ linkname = linkr->name;
+ break;
+ }
+ }
+
+ return linkname;
+ }
+ else
+ {
+ /* Return empty string for every other DisplayRole */
+ return QVariant();
+ }
+ }
+ else if ( role == Qt::CheckStateRole )
+ {
+ if ( col == IFTREE_COL_HIDDEN )
+ {
+ /* Hidden is a de-selection, therefore inverted logic here */
+ return device.hidden ? Qt::Unchecked : Qt::Checked;
+ }
+ else if ( col == IFTREE_COL_PROMISCUOUSMODE )
+ {
+ return device.pmode ? Qt::Checked : Qt::Unchecked;
+ }
+#ifdef HAVE_PCAP_CREATE
+ else if ( col == IFTREE_COL_MONITOR_MODE )
+ {
+ return device.monitor_mode_enabled ? Qt::Checked : Qt::Unchecked;
+ }
+#endif
+ }
+ /* Used by SparkLineDelegate for loading the data for the statistics line */
+ else if ( role == Qt::UserRole )
+ {
+ if ( col == IFTREE_COL_STATS )
+ {
+ if ( points.contains(device.name) )
+ return qVariantFromValue(points[device.name]);
+ }
+ else if ( col == IFTREE_COL_HIDDEN )
+ {
+ return QVariant::fromValue((bool)device.hidden);
+ }
+ }
+#ifdef HAVE_EXTCAP
+ /* Displays the configuration icon for extcap interfaces */
+ else if ( role == Qt::DecorationRole )
+ {
+ if ( col == IFTREE_COL_EXTCAP )
+ {
+ if ( device.if_info.type == IF_EXTCAP )
+ return QIcon(StockIcon("x-capture-options"));
+ }
+ }
+ else if ( role == Qt::TextAlignmentRole )
+ {
+ if ( col == IFTREE_COL_EXTCAP )
+ {
+ return Qt::AlignRight;
+ }
+ }
+#endif
+ /* Displays the tooltip for each row */
+ else if ( role == Qt::ToolTipRole )
+ {
+ return toolTipForInterface(row);
+ }
+ }
+#else
+ Q_UNUSED(index)
+ Q_UNUSED(role)
+#endif
+
+ return QVariant();
+}
+
+QVariant InterfaceTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if ( orientation == Qt::Horizontal )
+ {
+ if ( role == Qt::DisplayRole )
+ {
+ if ( section == IFTREE_COL_HIDDEN )
+ {
+ return tr("Show");
+ }
+ else if ( section == IFTREE_COL_INTERFACE_NAME )
+ {
+ return tr("Friendly Name");
+ }
+ else if ( section == IFTREE_COL_NAME )
+ {
+ return tr("Interface Name");
+ }
+ else if ( section == IFTREE_COL_PIPE_PATH )
+ {
+ return tr("Local Pipe Path");
+ }
+ else if ( section == IFTREE_COL_INTERFACE_COMMENT )
+ {
+ return tr("Comment");
+ }
+ else if ( section == IFTREE_COL_DLT )
+ {
+ return tr("Link-Layer Header");
+ }
+ else if ( section == IFTREE_COL_PROMISCUOUSMODE )
+ {
+ return tr("Promiscuous");
+ }
+ else if ( section == IFTREE_COL_SNAPLEN )
+ {
+ return tr("Snaplen (B)");
+ }
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ else if ( section == IFTREE_COL_BUFFERLEN )
+ {
+ return tr("Buffer (MB)");
+ }
+#endif
+#ifdef HAVE_PCAP_CREATE
+ else if ( section == IFTREE_COL_MONITOR_MODE )
+ {
+ return tr("Monitor Mode");
+ }
+#endif
+ else if ( section == IFTREE_COL_CAPTURE_FILTER )
+ {
+ return tr("Capture Filter");
+ }
+ }
+ }
+
+ return QVariant();
+}
+
+QVariant InterfaceTreeModel::getColumnContent(int idx, int col, int role)
+{
+ return InterfaceTreeModel::data(index(idx, col), role);
+}
+
+#ifdef HAVE_PCAP_REMOTE
+bool InterfaceTreeModel::isRemote(int idx)
+{
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+ if ( device.remote_opts.src_type == CAPTURE_IFREMOTE )
+ return true;
+ return false;
+}
+#endif
+
+/**
+ * The interface list has changed. global_capture_opts.all_ifaces may have been reloaded
+ * or changed with current data. beginResetModel() and endResetModel() will signalize the
+ * proxy model and the view, that the data has changed and the view has to reload
+ */
+void InterfaceTreeModel::interfaceListChanged()
+{
+ emit beginResetModel();
+
+ points.clear();
+
+ emit endResetModel();
+}
+
+/*
+ * Displays the tooltip code for the given device index.
+ */
+QVariant InterfaceTreeModel::toolTipForInterface(int idx) const
+{
+#ifdef HAVE_LIBPCAP
+ if ( ! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) idx)
+ return QVariant();
+
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+
+ QString tt_str = "<p>";
+ if ( device.no_addresses > 0 )
+ {
+ tt_str += QString("%1: %2")
+ .arg(device.no_addresses > 1 ? tr("Addresses") : tr("Address"))
+ .arg(html_escape(device.addresses))
+ .replace('\n', ", ");
+ }
+#ifdef HAVE_EXTCAP
+ else if ( device.if_info.type == IF_EXTCAP )
+ {
+ tt_str = QString(tr("Extcap interface: %1")).arg(get_basename(device.if_info.extcap));
+ }
+#endif
+ else
+ {
+ tt_str = tr("No addresses");
+ }
+ tt_str += "<br/>";
+
+ QString cfilter = device.cfilter;
+ if ( cfilter.isEmpty() )
+ {
+ tt_str += tr("No capture filter");
+ }
+ else
+ {
+ tt_str += QString("%1: %2")
+ .arg(tr("Capture filter"))
+ .arg(html_escape(cfilter));
+ }
+ tt_str += "</p>";
+
+ return tt_str;
+#else
+ Q_UNUSED(idx)
+
+ return QVariant();
+#endif
+}
+
+#ifdef HAVE_LIBPCAP
+void InterfaceTreeModel::stopStatistic()
+{
+ if ( stat_cache_ )
+ {
+ capture_stat_stop(stat_cache_);
+ stat_cache_ = NULL;
+ }
+}
+#endif
+
+void InterfaceTreeModel::updateStatistic(unsigned int idx)
+{
+#ifdef HAVE_LIBPCAP
+ guint diff;
+ if ( ! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) idx )
+ return;
+
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+
+ if ( device.if_info.type == IF_PIPE )
+ return;
+
+ if ( !stat_cache_ )
+ {
+ // Start gathering statistics using dumpcap
+ // We crash (on macOS at least) if we try to do this from ::showEvent.
+ stat_cache_ = capture_stat_start(&global_capture_opts);
+ }
+ if ( !stat_cache_ )
+ return;
+
+ struct pcap_stat stats;
+
+ diff = 0;
+ if ( capture_stats(stat_cache_, device.name, &stats) )
+ {
+ if ( (int)(stats.ps_recv - device.last_packets) >= 0 )
+ {
+ diff = stats.ps_recv - device.last_packets;
+ device.packet_diff = diff;
+ }
+ device.last_packets = stats.ps_recv;
+
+ global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, idx);
+ g_array_insert_val(global_capture_opts.all_ifaces, idx, device);
+ }
+
+ points[device.name].append(diff);
+ emit dataChanged(index(idx, IFTREE_COL_STATS), index(idx, IFTREE_COL_STATS));
+#else
+ Q_UNUSED(idx)
+#endif
+}
+
+void InterfaceTreeModel::getPoints(int idx, PointList *pts)
+{
+#ifdef HAVE_LIBPCAP
+ if ( ! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) idx )
+ return;
+
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+ if ( points.contains(device.name) )
+ pts->append(points[device.name]);
+#else
+ Q_UNUSED(idx)
+ Q_UNUSED(pts)
+#endif
+}
+
+QItemSelection InterfaceTreeModel::selectedDevices()
+{
+ QItemSelection mySelection;
+#ifdef HAVE_LIBPCAP
+ for( int idx = 0; idx < rowCount(); idx++ )
+ {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+
+ if ( device.selected )
+ {
+ QModelIndex selectIndex = index(idx, 0);
+ mySelection.merge(
+ QItemSelection( selectIndex, index(selectIndex.row(), columnCount() - 1) ),
+ QItemSelectionModel::SelectCurrent
+ );
+ }
+ }
+#endif
+ return mySelection;
+}
+
+bool InterfaceTreeModel::updateSelectedDevices(QItemSelection sourceSelection)
+{
+ bool selectionHasChanged = false;
+#ifdef HAVE_LIBPCAP
+ QList<int> selectedIndices;
+
+ QItemSelection::const_iterator it = sourceSelection.constBegin();
+ while(it != sourceSelection.constEnd())
+ {
+ QModelIndexList indeces = ((QItemSelectionRange) (*it)).indexes();
+
+ QModelIndexList::const_iterator cit = indeces.constBegin();
+ while(cit != indeces.constEnd())
+ {
+ QModelIndex index = (QModelIndex) (*cit);
+ if ( ! selectedIndices.contains(index.row()) )
+ {
+ selectedIndices.append(index.row());
+ }
+ ++cit;
+ }
+ ++it;
+ }
+
+ global_capture_opts.num_selected = 0;
+
+ for ( unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++ )
+ {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+ if ( !device.locked )
+ {
+ if ( selectedIndices.contains(idx) )
+ {
+ if (! device.selected )
+ selectionHasChanged = true;
+ device.selected = TRUE;
+ global_capture_opts.num_selected++;
+ } else {
+ if ( device.selected )
+ selectionHasChanged = true;
+ device.selected = FALSE;
+ }
+ device.locked = TRUE;
+ global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, idx);
+ g_array_insert_val(global_capture_opts.all_ifaces, idx, device);
+
+ device.locked = FALSE;
+ global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, idx);
+ g_array_insert_val(global_capture_opts.all_ifaces, idx, device);
+ }
+ }
+#else
+ Q_UNUSED(sourceSelection)
+#endif
+ return selectionHasChanged;
+}
+
+
+/*
+ * 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/interface_tree_model.h b/ui/qt/models/interface_tree_model.h
new file mode 100644
index 0000000000..a441b39e33
--- /dev/null
+++ b/ui/qt/models/interface_tree_model.h
@@ -0,0 +1,125 @@
+/* interface_tree_model.h
+ * Model for the interface data for display in the interface frame
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TREE_MODEL_H
+#define INTERFACE_TREE_MODEL_H
+
+#include <config.h>
+
+#ifdef HAVE_LIBPCAP
+#include "ui/capture.h"
+#include "ui/capture_globals.h"
+#endif
+
+#include <glib.h>
+
+#include <QAbstractTableModel>
+#include <QList>
+#include <QMap>
+#include <QItemSelection>
+
+typedef QList<int> PointList;
+
+enum InterfaceTreeColumns
+{
+#ifdef HAVE_EXTCAP
+ IFTREE_COL_EXTCAP,
+ IFTREE_COL_EXTCAP_PATH,
+#endif
+ IFTREE_COL_NAME,
+ IFTREE_COL_INTERFACE_NAME,
+ IFTREE_COL_INTERFACE_COMMENT,
+ IFTREE_COL_HIDDEN,
+ IFTREE_COL_DLT,
+ IFTREE_COL_PROMISCUOUSMODE,
+ IFTREE_COL_TYPE,
+ IFTREE_COL_STATS,
+ IFTREE_COL_SNAPLEN,
+#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
+ IFTREE_COL_BUFFERLEN,
+#endif
+#ifdef HAVE_PCAP_CREATE
+ IFTREE_COL_MONITOR_MODE,
+#endif
+ IFTREE_COL_CAPTURE_FILTER,
+ IFTREE_COL_PIPE_PATH,
+ IFTREE_COL_MAX /* is not being displayed, it is the definition for the maximum numbers of columns */
+};
+
+class InterfaceTreeModel : public QAbstractTableModel
+{
+ Q_OBJECT
+public:
+ InterfaceTreeModel(QObject *parent);
+ ~InterfaceTreeModel();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+ void updateStatistic(unsigned int row);
+#ifdef HAVE_LIBPCAP
+ void stopStatistic();
+#endif
+
+ QString interfaceError();
+ QItemSelection selectedDevices();
+ bool updateSelectedDevices(QItemSelection sourceSelection);
+
+ QVariant getColumnContent(int idx, int col, int role = Qt::DisplayRole);
+
+#ifdef HAVE_PCAP_REMOTE
+ bool isRemote(int idx);
+#endif
+
+ static const QString DefaultNumericValue;
+
+public slots:
+ void getPoints(int idx, PointList *pts);
+
+protected slots:
+ void interfaceListChanged();
+
+private:
+ QVariant toolTipForInterface(int idx) const;
+ QMap<QString, PointList> points;
+
+#ifdef HAVE_LIBPCAP
+ if_stat_cache_t *stat_cache_;
+#endif // HAVE_LIBPCAP
+};
+
+#endif // INTERFACE_TREE_MODEL_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:
+ */
diff --git a/ui/qt/models/numeric_value_chooser_delegate.cpp b/ui/qt/models/numeric_value_chooser_delegate.cpp
new file mode 100644
index 0000000000..cdcd41f359
--- /dev/null
+++ b/ui/qt/models/numeric_value_chooser_delegate.cpp
@@ -0,0 +1,117 @@
+/* numeric_value_chooser_delegate.cpp
+ * Delegate to select a numeric value for a treeview entry
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <ui/qt/models/numeric_value_chooser_delegate.h>
+
+#include <QStyledItemDelegate>
+#include <QSpinBox>
+
+NumericValueChooserDelegate::NumericValueChooserDelegate(int min, int max, QObject *parent)
+ : QStyledItemDelegate(parent)
+{
+ _min = min;
+ _max = max;
+ _default = min;
+}
+
+NumericValueChooserDelegate::~NumericValueChooserDelegate()
+{
+}
+
+void NumericValueChooserDelegate::setMinMaxRange(int min, int max)
+{
+ _min = qMin(min, max);
+ _max = qMax(min, max);
+ /* ensure, that the default value is within the new min<->max */
+ _default = qMin(_max, qMax(_min, _default));
+ _defReturn = qVariantFromValue(_default);
+}
+
+void NumericValueChooserDelegate::setDefaultValue(int defValue, QVariant defaultReturn)
+{
+ /* ensure, that the new default value is within min<->max */
+ _default = qMin(_max, qMax(_min, defValue));
+ _defReturn = defaultReturn;
+}
+
+QWidget* NumericValueChooserDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ if (!index.isValid()) {
+ return QStyledItemDelegate::createEditor(parent, option, index);
+ }
+
+ QSpinBox * editor = new QSpinBox(parent);
+ editor->setMinimum(_min);
+ editor->setMaximum(_max);
+ editor->setWrapping(true);
+
+ connect(editor, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int)));
+
+ return editor;
+}
+
+void NumericValueChooserDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+ if ( index.isValid() )
+ {
+ bool canConvert = false;
+ int val = index.data().toInt(&canConvert);
+ if ( ! canConvert )
+ val = _default;
+
+ QSpinBox * spinBox = qobject_cast<QSpinBox *>(editor);
+ spinBox->setValue(val);
+ }
+ else
+ QStyledItemDelegate::setEditorData(editor, index);
+}
+
+void NumericValueChooserDelegate::setModelData(QWidget *editor, QAbstractItemModel * model, const QModelIndex &index) const
+{
+ if ( index.isValid() ) {
+ QSpinBox * spinBox = qobject_cast<QSpinBox *>(editor);
+ model->setData(index, _default == spinBox->value() ? _defReturn : qVariantFromValue(spinBox->value()));
+ } else {
+ QStyledItemDelegate::setModelData(editor, model, index);
+ }
+}
+
+void NumericValueChooserDelegate::onValueChanged(int)
+{
+ QSpinBox * spinBox = qobject_cast<QSpinBox *>(sender());
+ emit commitData(spinBox);
+}
+
+/*
+ * 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/numeric_value_chooser_delegate.h b/ui/qt/models/numeric_value_chooser_delegate.h
new file mode 100644
index 0000000000..87a504ada1
--- /dev/null
+++ b/ui/qt/models/numeric_value_chooser_delegate.h
@@ -0,0 +1,69 @@
+/* numeric_value_chooser_delegate.h
+ * Delegate to select a numeric value for a treeview entry
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef NUMERIC_VALUE_CHOOSER_DELEGATE_H_
+#define NUMERIC_VALUE_CHOOSER_DELEGATE_H_
+
+
+#include <QStyledItemDelegate>
+
+class NumericValueChooserDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ NumericValueChooserDelegate(int min = 0, int max = 0, QObject *parent = 0);
+ ~NumericValueChooserDelegate();
+
+ void setMinMaxRange(int min, int max);
+ void setDefaultValue(int defValue, QVariant defaultReturn);
+
+protected:
+ 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:
+
+ int _min;
+ int _max;
+ int _default;
+ QVariant _defReturn;
+
+private slots:
+ void onValueChanged(int i);
+};
+
+#endif /* NUMERIC_VALUE_CHOOSER_DELEGATE_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:
+ */
diff --git a/ui/qt/models/packet_list_model.cpp b/ui/qt/models/packet_list_model.cpp
new file mode 100644
index 0000000000..d2d5b2f15a
--- /dev/null
+++ b/ui/qt/models/packet_list_model.cpp
@@ -0,0 +1,753 @@
+/* packet_list_model.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <algorithm>
+
+#include "packet_list_model.h"
+
+#include "file.h"
+
+#include <wsutil/nstime.h>
+#include <epan/column.h>
+#include <epan/prefs.h>
+
+#include "ui/packet_list_utils.h"
+#include "ui/recent.h"
+
+#include <epan/color_filters.h>
+#include "frame_tvbuff.h"
+
+#include <ui/qt/utils/color_utils.h>
+#include "wireshark_application.h"
+
+#include <QColor>
+#include <QElapsedTimer>
+#include <QFontMetrics>
+#include <QModelIndex>
+#include <QElapsedTimer>
+
+// Print timing information
+//#define DEBUG_PACKET_LIST_MODEL 1
+
+#ifdef DEBUG_PACKET_LIST_MODEL
+#include <wsutil/time_util.h>
+#endif
+
+static const int reserved_packets_ = 100000;
+
+PacketListModel::PacketListModel(QObject *parent, capture_file *cf) :
+ QAbstractItemModel(parent),
+ number_to_row_(QVector<int>()),
+ max_row_height_(0),
+ max_line_count_(1),
+ idle_dissection_row_(0)
+{
+ setCaptureFile(cf);
+ PacketListRecord::clearStringPool();
+
+ physical_rows_.reserve(reserved_packets_);
+ visible_rows_.reserve(reserved_packets_);
+ new_visible_rows_.reserve(1000);
+ number_to_row_.reserve(reserved_packets_);
+
+ connect(this, SIGNAL(maxLineCountChanged(QModelIndex)),
+ this, SLOT(emitItemHeightChanged(QModelIndex)),
+ Qt::QueuedConnection);
+ idle_dissection_timer_ = new QElapsedTimer();
+}
+
+PacketListModel::~PacketListModel()
+{
+ delete idle_dissection_timer_;
+}
+
+void PacketListModel::setCaptureFile(capture_file *cf)
+{
+ cap_file_ = cf;
+ resetColumns();
+}
+
+// Packet list records have no children (for now, at least).
+QModelIndex PacketListModel::index(int row, int column, const QModelIndex &) const
+{
+ if (row >= visible_rows_.count() || row < 0 || !cap_file_ || column >= prefs.num_cols)
+ return QModelIndex();
+
+ PacketListRecord *record = visible_rows_[row];
+
+ return createIndex(row, column, record);
+}
+
+// Everything is under the root.
+QModelIndex PacketListModel::parent(const QModelIndex &) const
+{
+ return QModelIndex();
+}
+
+int PacketListModel::packetNumberToRow(int packet_num) const
+{
+ // map 1-based values to 0-based row numbers. Invisible rows are stored as
+ // the default value (0) and should map to -1.
+ return number_to_row_.value(packet_num) - 1;
+}
+
+guint PacketListModel::recreateVisibleRows()
+{
+ int pos = visible_rows_.count();
+
+ beginResetModel();
+ visible_rows_.resize(0);
+ number_to_row_.fill(0);
+ endResetModel();
+
+ beginInsertRows(QModelIndex(), pos, pos);
+ foreach (PacketListRecord *record, physical_rows_) {
+ frame_data *fdata = record->frameData();
+
+ if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
+ visible_rows_ << record;
+ if (number_to_row_.size() <= (int)fdata->num) {
+ number_to_row_.resize(fdata->num + 10000);
+ }
+ number_to_row_[fdata->num] = visible_rows_.count();
+ }
+ }
+ endInsertRows();
+ idle_dissection_row_ = 0;
+ return visible_rows_.count();
+}
+
+void PacketListModel::clear() {
+ beginResetModel();
+ qDeleteAll(physical_rows_);
+ physical_rows_.resize(0);
+ visible_rows_.resize(0);
+ new_visible_rows_.resize(0);
+ number_to_row_.resize(0);
+ PacketListRecord::clearStringPool();
+ endResetModel();
+ max_row_height_ = 0;
+ max_line_count_ = 1;
+ idle_dissection_row_ = 0;
+}
+
+void PacketListModel::resetColumns()
+{
+ if (cap_file_) {
+ PacketListRecord::resetColumns(&cap_file_->cinfo);
+ }
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+ headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
+}
+
+void PacketListModel::resetColorized()
+{
+ foreach (PacketListRecord *record, physical_rows_) {
+ record->resetColorized();
+ }
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void PacketListModel::toggleFrameMark(const QModelIndex &fm_index)
+{
+ if (!cap_file_ || !fm_index.isValid()) return;
+
+ PacketListRecord *record = static_cast<PacketListRecord*>(fm_index.internalPointer());
+ if (!record) return;
+
+ frame_data *fdata = record->frameData();
+ if (!fdata) return;
+
+ if (fdata->flags.marked)
+ cf_unmark_frame(cap_file_, fdata);
+ else
+ cf_mark_frame(cap_file_, fdata);
+
+ dataChanged(fm_index, fm_index);
+}
+
+void PacketListModel::setDisplayedFrameMark(gboolean set)
+{
+ foreach (PacketListRecord *record, visible_rows_) {
+ if (set) {
+ cf_mark_frame(cap_file_, record->frameData());
+ } else {
+ cf_unmark_frame(cap_file_, record->frameData());
+ }
+ }
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void PacketListModel::toggleFrameIgnore(const QModelIndex &i_index)
+{
+ if (!cap_file_ || !i_index.isValid()) return;
+
+ PacketListRecord *record = static_cast<PacketListRecord*>(i_index.internalPointer());
+ if (!record) return;
+
+ frame_data *fdata = record->frameData();
+ if (!fdata) return;
+
+ if (fdata->flags.ignored)
+ cf_unignore_frame(cap_file_, fdata);
+ else
+ cf_ignore_frame(cap_file_, fdata);
+}
+
+void PacketListModel::setDisplayedFrameIgnore(gboolean set)
+{
+ foreach (PacketListRecord *record, visible_rows_) {
+ if (set) {
+ cf_ignore_frame(cap_file_, record->frameData());
+ } else {
+ cf_unignore_frame(cap_file_, record->frameData());
+ }
+ }
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void PacketListModel::toggleFrameRefTime(const QModelIndex &rt_index)
+{
+ if (!cap_file_ || !rt_index.isValid()) return;
+
+ PacketListRecord *record = static_cast<PacketListRecord*>(rt_index.internalPointer());
+ if (!record) return;
+
+ frame_data *fdata = record->frameData();
+ if (!fdata) return;
+
+ if (fdata->flags.ref_time) {
+ fdata->flags.ref_time=0;
+ cap_file_->ref_time_count--;
+ } else {
+ fdata->flags.ref_time=1;
+ cap_file_->ref_time_count++;
+ }
+ cf_reftime_packets(cap_file_);
+ if (!fdata->flags.ref_time && !fdata->flags.passed_dfilter) {
+ cap_file_->displayed_count--;
+ }
+ record->resetColumns(&cap_file_->cinfo);
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void PacketListModel::unsetAllFrameRefTime()
+{
+ if (!cap_file_) return;
+
+ /* XXX: we might need a progressbar here */
+
+ foreach (PacketListRecord *record, physical_rows_) {
+ frame_data *fdata = record->frameData();
+ if (fdata->flags.ref_time) {
+ fdata->flags.ref_time = 0;
+ }
+ }
+ cap_file_->ref_time_count = 0;
+ cf_reftime_packets(cap_file_);
+ PacketListRecord::resetColumns(&cap_file_->cinfo);
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void PacketListModel::applyTimeShift()
+{
+ resetColumns();
+ dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
+}
+
+void PacketListModel::setMaximiumRowHeight(int height)
+{
+ max_row_height_ = height;
+ // As the QTreeView uniformRowHeights documentation says,
+ // "The height is obtained from the first item in the view. It is
+ // updated when the data changes on that item."
+ dataChanged(index(0, 0), index(0, columnCount() - 1));
+}
+
+//void PacketListModel::setMonospaceFont(const QFont &mono_font, int row_height)
+//{
+// QFontMetrics fm(mono_font_);
+// mono_font_ = mono_font;
+// row_height_ = row_height;
+// line_spacing_ = fm.lineSpacing();
+//}
+
+// The Qt MVC documentation suggests using QSortFilterProxyModel for sorting
+// and filtering. That seems like overkill but it might be something we want
+// to do in the future.
+
+int PacketListModel::sort_column_;
+int PacketListModel::sort_column_is_numeric_;
+int PacketListModel::text_sort_column_;
+Qt::SortOrder PacketListModel::sort_order_;
+capture_file *PacketListModel::sort_cap_file_;
+
+QElapsedTimer busy_timer_;
+const int busy_timeout_ = 65; // ms, approximately 15 fps
+void PacketListModel::sort(int column, Qt::SortOrder order)
+{
+ // packet_list_store.c:packet_list_dissect_and_cache_all
+ if (!cap_file_ || visible_rows_.count() < 1) return;
+ if (column < 0) return;
+
+ sort_column_ = column;
+ text_sort_column_ = PacketListRecord::textColumn(column);
+ sort_order_ = order;
+ sort_cap_file_ = cap_file_;
+
+ gboolean stop_flag = FALSE;
+ QString col_title = get_column_title(column);
+
+ busy_timer_.start();
+ emit pushProgressStatus(tr("Dissecting"), true, true, &stop_flag);
+ int row_num = 0;
+ foreach (PacketListRecord *row, physical_rows_) {
+ row->columnString(sort_cap_file_, column);
+ row_num++;
+ if (busy_timer_.elapsed() > busy_timeout_) {
+ if (stop_flag) {
+ emit popProgressStatus();
+ return;
+ }
+ emit updateProgressStatus(row_num * 100 / physical_rows_.count());
+ // What's the least amount of processing that we can do which will draw
+ // the progress indicator?
+ wsApp->processEvents(QEventLoop::AllEvents, 1);
+ busy_timer_.restart();
+ }
+ }
+ emit popProgressStatus();
+
+ // XXX Use updateProgress instead. We'd have to switch from std::sort to
+ // something we can interrupt.
+ if (!col_title.isEmpty()) {
+ QString busy_msg = tr("Sorting \"%1\"").arg(col_title);
+ emit pushBusyStatus(busy_msg);
+ }
+
+ busy_timer_.restart();
+ sort_column_is_numeric_ = isNumericColumn(sort_column_);
+ std::sort(physical_rows_.begin(), physical_rows_.end(), recordLessThan);
+
+ beginResetModel();
+ visible_rows_.resize(0);
+ number_to_row_.fill(0);
+ foreach (PacketListRecord *record, physical_rows_) {
+ frame_data *fdata = record->frameData();
+
+ if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
+ visible_rows_ << record;
+ if (number_to_row_.size() <= (int)fdata->num) {
+ number_to_row_.resize(fdata->num + 10000);
+ }
+ number_to_row_[fdata->num] = visible_rows_.count();
+ }
+ }
+ endResetModel();
+
+ if (!col_title.isEmpty()) {
+ emit popBusyStatus();
+ }
+
+ if (cap_file_->current_frame) {
+ emit goToPacket(cap_file_->current_frame->num);
+ }
+}
+
+bool PacketListModel::isNumericColumn(int column)
+{
+ if (column < 0 || sort_cap_file_->cinfo.columns[column].col_fmt != COL_CUSTOM) {
+ return false;
+ }
+
+ gchar **fields = g_regex_split_simple(COL_CUSTOM_PRIME_REGEX,
+ sort_cap_file_->cinfo.columns[column].col_custom_fields,
+ G_REGEX_ANCHORED, G_REGEX_MATCH_ANCHORED);
+
+ for (guint i = 0; i < g_strv_length(fields); i++) {
+ if (!*fields[i]) {
+ continue;
+ }
+
+ header_field_info *hfi = proto_registrar_get_byname(fields[i]);
+ /*
+ * Reject a field when there is no numeric field type or when:
+ * - there are (value_string) "strings"
+ * (but do accept fields which have a unit suffix).
+ * - BASE_HEX or BASE_HEX_DEC (these have a constant width, string
+ * comparison is faster than conversion to double).
+ * - BASE_CUSTOM (these can be formatted in any way).
+ */
+ if (!hfi ||
+ (hfi->strings != NULL && !(hfi->display & BASE_UNIT_STRING)) ||
+ !(((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
+ ((FIELD_DISPLAY(hfi->display) == BASE_DEC) ||
+ (FIELD_DISPLAY(hfi->display) == BASE_OCT) ||
+ (FIELD_DISPLAY(hfi->display) == BASE_DEC_HEX))) ||
+ (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
+ (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
+ (hfi->type == FT_RELATIVE_TIME))) {
+ g_strfreev(fields);
+ return false;
+ }
+ }
+
+ g_strfreev(fields);
+ return true;
+}
+
+bool PacketListModel::recordLessThan(PacketListRecord *r1, PacketListRecord *r2)
+{
+ int cmp_val = 0;
+
+ // Wherein we try to cram the logic of packet_list_compare_records,
+ // _packet_list_compare_records, and packet_list_compare_custom from
+ // gtk/packet_list_store.c into one function
+
+ if (busy_timer_.elapsed() > busy_timeout_) {
+ // What's the least amount of processing that we can do which will draw
+ // the busy indicator?
+ wsApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers, 1);
+ busy_timer_.restart();
+ }
+ if (sort_column_ < 0) {
+ // No column.
+ cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), COL_NUMBER);
+ } else if (text_sort_column_ < 0) {
+ // Column comes directly from frame data
+ cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), sort_cap_file_->cinfo.columns[sort_column_].col_fmt);
+ } else {
+ if (r1->columnString(sort_cap_file_, sort_column_).constData() == r2->columnString(sort_cap_file_, sort_column_).constData()) {
+ cmp_val = 0;
+ } else if (sort_cap_file_->cinfo.columns[sort_column_].col_fmt == COL_CUSTOM) {
+ // Column comes from custom data
+ if (sort_column_is_numeric_) {
+ // Attempt to convert to numbers.
+ // XXX This is slow. Can we avoid doing this?
+ bool ok_r1, ok_r2;
+ double num_r1 = parseNumericColumn(r1->columnString(sort_cap_file_, sort_column_), &ok_r1);
+ double num_r2 = parseNumericColumn(r2->columnString(sort_cap_file_, sort_column_), &ok_r2);
+
+ if (!ok_r1 && !ok_r2) {
+ cmp_val = 0;
+ } else if (!ok_r1 || (ok_r2 && num_r1 < num_r2)) {
+ // either r1 is invalid (and sort it before others) or both
+ // r1 and r2 are valid (sort normally)
+ cmp_val = -1;
+ } else if (!ok_r2 || (ok_r1 && num_r1 > num_r2)) {
+ cmp_val = 1;
+ }
+ } else {
+ cmp_val = strcmp(r1->columnString(sort_cap_file_, sort_column_).constData(), r2->columnString(sort_cap_file_, sort_column_).constData());
+ }
+ } else {
+ cmp_val = strcmp(r1->columnString(sort_cap_file_, sort_column_).constData(), r2->columnString(sort_cap_file_, sort_column_).constData());
+ }
+
+ if (cmp_val == 0) {
+ // All else being equal, compare column numbers.
+ cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), COL_NUMBER);
+ }
+ }
+
+ if (sort_order_ == Qt::AscendingOrder) {
+ return cmp_val < 0;
+ } else {
+ return cmp_val > 0;
+ }
+}
+
+// Parses a field as a double. Handle values with suffixes ("12ms"), negative
+// values ("-1.23") and fields with multiple occurrences ("1,2"). Marks values
+// that do not contain any numeric value ("Unknown") as invalid.
+double PacketListModel::parseNumericColumn(const QString &val, bool *ok)
+{
+ QByteArray ba = val.toUtf8();
+ const char *strval = ba.constData();
+ gchar *end = NULL;
+ double num = g_ascii_strtod(strval, &end);
+ *ok = strval != end;
+ return num;
+}
+
+// ::data is const so we have to make changes here.
+void PacketListModel::emitItemHeightChanged(const QModelIndex &ih_index)
+{
+ if (!ih_index.isValid()) return;
+
+ PacketListRecord *record = static_cast<PacketListRecord*>(ih_index.internalPointer());
+ if (!record) return;
+
+ if (record->lineCount() > max_line_count_) {
+ max_line_count_ = record->lineCount();
+ emit itemHeightChanged(ih_index);
+ }
+}
+
+int PacketListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.column() >= prefs.num_cols)
+ return 0;
+
+ return visible_rows_.count();
+}
+
+int PacketListModel::columnCount(const QModelIndex &) const
+{
+ return prefs.num_cols;
+}
+
+QVariant PacketListModel::data(const QModelIndex &d_index, int role) const
+{
+ if (!d_index.isValid())
+ return QVariant();
+
+ PacketListRecord *record = static_cast<PacketListRecord*>(d_index.internalPointer());
+ if (!record)
+ return QVariant();
+ const frame_data *fdata = record->frameData();
+ if (!fdata)
+ return QVariant();
+
+ switch (role) {
+ case Qt::TextAlignmentRole:
+ switch(recent_get_column_xalign(d_index.column())) {
+ case COLUMN_XALIGN_RIGHT:
+ return Qt::AlignRight;
+ break;
+ case COLUMN_XALIGN_CENTER:
+ return Qt::AlignCenter;
+ break;
+ case COLUMN_XALIGN_LEFT:
+ return Qt::AlignLeft;
+ break;
+ case COLUMN_XALIGN_DEFAULT:
+ default:
+ if (right_justify_column(d_index.column(), cap_file_)) {
+ return Qt::AlignRight;
+ }
+ break;
+ }
+ return Qt::AlignLeft;
+
+ case Qt::BackgroundRole:
+ const color_t *color;
+ if (fdata->flags.ignored) {
+ color = &prefs.gui_ignored_bg;
+ } else if (fdata->flags.marked) {
+ color = &prefs.gui_marked_bg;
+ } else if (fdata->color_filter && recent.packet_list_colorize) {
+ const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
+ color = &color_filter->bg_color;
+ } else {
+ return QVariant();
+ }
+ return ColorUtils::fromColorT(color);
+ case Qt::ForegroundRole:
+ if (fdata->flags.ignored) {
+ color = &prefs.gui_ignored_fg;
+ } else if (fdata->flags.marked) {
+ color = &prefs.gui_marked_fg;
+ } else if (fdata->color_filter && recent.packet_list_colorize) {
+ const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
+ color = &color_filter->fg_color;
+ } else {
+ return QVariant();
+ }
+ return ColorUtils::fromColorT(color);
+ case Qt::DisplayRole:
+ {
+ int column = d_index.column();
+ QByteArray column_string = record->columnString(cap_file_, column, true);
+ // We don't know an item's sizeHint until we fetch its text here.
+ // Assume each line count is 1. If the line count changes, emit
+ // itemHeightChanged which triggers another redraw (including a
+ // fetch of SizeHintRole and DisplayRole) in the next event loop.
+ if (column == 0 && record->lineCountChanged() && record->lineCount() > max_line_count_) {
+ emit maxLineCountChanged(d_index);
+ }
+ return column_string;
+ }
+ case Qt::SizeHintRole:
+ {
+ // If this is the first row and column, return the maximum row height...
+ if (d_index.row() < 1 && d_index.column() < 1 && max_row_height_ > 0) {
+ QSize size = QSize(-1, max_row_height_);
+ return size;
+ }
+ // ...otherwise punt so that the item delegate can correctly calculate the item width.
+ return QVariant();
+ }
+ default:
+ return QVariant();
+ }
+}
+
+QVariant PacketListModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (!cap_file_) return QVariant();
+
+ if (orientation == Qt::Horizontal && section < prefs.num_cols) {
+ switch (role) {
+ case Qt::DisplayRole:
+ return get_column_title(section);
+ case Qt::ToolTipRole:
+ {
+ gchar *tooltip = get_column_tooltip(section);
+ QVariant data(tooltip);
+ g_free (tooltip);
+ return data;
+ }
+ default:
+ break;
+ }
+ }
+
+ return QVariant();
+}
+
+void PacketListModel::flushVisibleRows()
+{
+ gint pos = visible_rows_.count();
+
+ if (new_visible_rows_.count() > 0) {
+ beginInsertRows(QModelIndex(), pos, pos + new_visible_rows_.count());
+ foreach (PacketListRecord *record, new_visible_rows_) {
+ frame_data *fdata = record->frameData();
+
+ visible_rows_ << record;
+ if (number_to_row_.size() <= (int)fdata->num) {
+ number_to_row_.resize(fdata->num + 10000);
+ }
+ number_to_row_[fdata->num] = visible_rows_.count();
+ }
+ endInsertRows();
+ new_visible_rows_.resize(0);
+ }
+}
+
+// Fill our column string and colorization cache while the application is
+// idle. Try to be as conservative with the CPU and disk as possible.
+static const int idle_dissection_interval_ = 5; // ms
+void PacketListModel::dissectIdle(bool reset)
+{
+ if (reset) {
+// qDebug() << "=di reset" << idle_dissection_row_;
+ idle_dissection_row_ = 0;
+ } else if (!idle_dissection_timer_->isValid()) {
+ return;
+ }
+
+ idle_dissection_timer_->restart();
+
+ int first = idle_dissection_row_;
+ while (idle_dissection_timer_->elapsed() < idle_dissection_interval_
+ && idle_dissection_row_ < physical_rows_.count()) {
+ ensureRowColorized(idle_dissection_row_);
+ idle_dissection_row_++;
+// if (idle_dissection_row_ % 1000 == 0) qDebug() << "=di row" << idle_dissection_row_;
+ }
+
+ if (idle_dissection_row_ < physical_rows_.count()) {
+ QTimer::singleShot(idle_dissection_interval_, this, SLOT(dissectIdle()));
+ } else {
+ idle_dissection_timer_->invalidate();
+ }
+
+ // report colorization progress
+ bgColorizationProgress(first+1, idle_dissection_row_+1);
+}
+
+// XXX Pass in cinfo from packet_list_append so that we can fill in
+// line counts?
+gint PacketListModel::appendPacket(frame_data *fdata)
+{
+ PacketListRecord *record = new PacketListRecord(fdata);
+ gint pos = -1;
+
+#ifdef DEBUG_PACKET_LIST_MODEL
+ if (fdata->num % 10000 == 1) {
+ log_resource_usage(fdata->num == 1, "%u packets", fdata->num);
+ }
+#endif
+
+ physical_rows_ << record;
+
+ if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
+ new_visible_rows_ << record;
+ if (new_visible_rows_.count() < 2) {
+ // This is the first queued packet. Schedule an insertion for
+ // the next UI update.
+ QTimer::singleShot(0, this, SLOT(flushVisibleRows()));
+ }
+ pos = visible_rows_.count() + new_visible_rows_.count() - 1;
+ }
+
+ return pos;
+}
+
+frame_data *PacketListModel::getRowFdata(int row) {
+ if (row < 0 || row >= visible_rows_.count())
+ return NULL;
+ PacketListRecord *record = visible_rows_[row];
+ if (!record)
+ return NULL;
+ return record->frameData();
+}
+
+void PacketListModel::ensureRowColorized(int row)
+{
+ if (row < 0 || row >= visible_rows_.count())
+ return;
+ PacketListRecord *record = visible_rows_[row];
+ if (!record)
+ return;
+ if (!record->colorized()) {
+ record->columnString(cap_file_, 1, true);
+ }
+}
+
+int PacketListModel::visibleIndexOf(frame_data *fdata) const
+{
+ int row = 0;
+ foreach (PacketListRecord *record, visible_rows_) {
+ if (record->frameData() == fdata) {
+ return row;
+ }
+ row++;
+ }
+
+ return -1;
+}
+
+/*
+ * 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/packet_list_model.h b/ui/qt/models/packet_list_model.h
new file mode 100644
index 0000000000..3dadb55011
--- /dev/null
+++ b/ui/qt/models/packet_list_model.h
@@ -0,0 +1,138 @@
+/* packet_list_model.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PACKET_LIST_MODEL_H
+#define PACKET_LIST_MODEL_H
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <epan/packet.h>
+
+#include <QAbstractItemModel>
+#include <QFont>
+#include <QVector>
+
+#include "packet_list_record.h"
+
+#include "cfile.h"
+
+class QElapsedTimer;
+
+class PacketListModel : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ explicit PacketListModel(QObject *parent = 0, capture_file *cf = NULL);
+ ~PacketListModel();
+ void setCaptureFile(capture_file *cf);
+ QModelIndex index(int row, int column,
+ const QModelIndex & = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &) const;
+ int packetNumberToRow(int packet_num) const;
+ guint recreateVisibleRows();
+ void clear();
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex & = QModelIndex()) const;
+ QVariant data(const QModelIndex &d_index, int role) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole | Qt::ToolTipRole) const;
+
+ gint appendPacket(frame_data *fdata);
+ frame_data *getRowFdata(int row);
+ void ensureRowColorized(int row);
+ int visibleIndexOf(frame_data *fdata) const;
+ void resetColumns();
+ void resetColorized();
+ void toggleFrameMark(const QModelIndex &fm_index);
+ void setDisplayedFrameMark(gboolean set);
+ void toggleFrameIgnore(const QModelIndex &i_index);
+ void setDisplayedFrameIgnore(gboolean set);
+ void toggleFrameRefTime(const QModelIndex &rt_index);
+ void unsetAllFrameRefTime();
+ void applyTimeShift();
+
+ void setMaximiumRowHeight(int height);
+
+signals:
+ void goToPacket(int);
+ void maxLineCountChanged(const QModelIndex &ih_index) const;
+ void itemHeightChanged(const QModelIndex &ih_index);
+ void pushBusyStatus(const QString &status);
+ void popBusyStatus();
+
+ void pushProgressStatus(const QString &status, bool animate, bool terminate_is_stop, gboolean *stop_flag);
+ void updateProgressStatus(int value);
+ void popProgressStatus();
+
+ void bgColorizationProgress(int first, int last);
+
+public slots:
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
+ void flushVisibleRows();
+ void dissectIdle(bool reset = false);
+
+private:
+ capture_file *cap_file_;
+ QList<QString> col_names_;
+ QVector<PacketListRecord *> physical_rows_;
+ QVector<PacketListRecord *> visible_rows_;
+ QVector<PacketListRecord *> new_visible_rows_;
+ QVector<int> number_to_row_;
+
+ int max_row_height_; // px
+ int max_line_count_;
+
+ static int sort_column_;
+ static int sort_column_is_numeric_;
+ static int text_sort_column_;
+ static Qt::SortOrder sort_order_;
+ static capture_file *sort_cap_file_;
+ static bool recordLessThan(PacketListRecord *r1, PacketListRecord *r2);
+ static double parseNumericColumn(const QString &val, bool *ok);
+
+ QElapsedTimer *idle_dissection_timer_;
+ int idle_dissection_row_;
+
+ bool isNumericColumn(int column);
+
+private slots:
+ void emitItemHeightChanged(const QModelIndex &ih_index);
+};
+
+#endif // PACKET_LIST_MODEL_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:
+ */
diff --git a/ui/qt/models/packet_list_record.cpp b/ui/qt/models/packet_list_record.cpp
new file mode 100644
index 0000000000..bf6f866702
--- /dev/null
+++ b/ui/qt/models/packet_list_record.cpp
@@ -0,0 +1,333 @@
+/* packet_list_record.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "packet_list_record.h"
+
+#include <file.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/column-info.h>
+#include <epan/column.h>
+#include <epan/conversation.h>
+#include <epan/wmem/wmem.h>
+
+#include <epan/color_filters.h>
+
+#include "frame_tvbuff.h"
+
+#include <QStringList>
+
+class ColumnTextList : public QList<const char *> {
+public:
+ // Allocate our records using wmem.
+ static void *operator new(size_t size) {
+ return wmem_alloc(wmem_file_scope(), size);
+ }
+
+ static void operator delete(void *) {}
+};
+
+QMap<int, int> PacketListRecord::cinfo_column_;
+unsigned PacketListRecord::col_data_ver_ = 1;
+
+PacketListRecord::PacketListRecord(frame_data *frameData) :
+ col_text_(0),
+ fdata_(frameData),
+ lines_(1),
+ line_count_changed_(false),
+ data_ver_(0),
+ colorized_(false),
+ conv_(NULL)
+{
+}
+
+void *PacketListRecord::operator new(size_t size)
+{
+ return wmem_alloc(wmem_file_scope(), size);
+}
+
+// We might want to return a const char * instead. This would keep us from
+// creating excessive QByteArrays, e.g. in PacketListModel::recordLessThan.
+const QByteArray PacketListRecord::columnString(capture_file *cap_file, int column, bool colorized)
+{
+ // packet_list_store.c:packet_list_get_value
+ g_assert(fdata_);
+
+ if (!cap_file || column < 0 || column > cap_file->cinfo.num_cols) {
+ return QByteArray();
+ }
+
+ bool dissect_color = colorized && !colorized_;
+ if (!col_text_ || column >= col_text_->size() || !col_text_->at(column) || data_ver_ != col_data_ver_ || dissect_color) {
+ dissect(cap_file, dissect_color);
+ }
+
+ return col_text_->value(column, QByteArray());
+}
+
+void PacketListRecord::resetColumns(column_info *cinfo)
+{
+ col_data_ver_++;
+
+ if (!cinfo) {
+ return;
+ }
+
+ cinfo_column_.clear();
+ int i, j;
+ for (i = 0, j = 0; i < cinfo->num_cols; i++) {
+ if (!col_based_on_frame_data(cinfo, i)) {
+ cinfo_column_[i] = j;
+ j++;
+ }
+ }
+}
+
+void PacketListRecord::resetColorized()
+{
+ colorized_ = false;
+}
+
+void PacketListRecord::dissect(capture_file *cap_file, bool dissect_color)
+{
+ // packet_list_store.c:packet_list_dissect_and_cache_record
+ epan_dissect_t edt;
+ column_info *cinfo = NULL;
+ gboolean create_proto_tree;
+ struct wtap_pkthdr phdr; /* Packet header */
+ Buffer buf; /* Packet data */
+
+ if (!col_text_) col_text_ = new ColumnTextList;
+ gboolean dissect_columns = col_text_->isEmpty() || data_ver_ != col_data_ver_;
+
+ if (!cap_file) {
+ return;
+ }
+
+ memset(&phdr, 0, sizeof(struct wtap_pkthdr));
+
+ if (dissect_columns) {
+ cinfo = &cap_file->cinfo;
+ }
+
+ ws_buffer_init(&buf, 1500);
+ if (!cf_read_record_r(cap_file, fdata_, &phdr, &buf)) {
+ /*
+ * Error reading the record.
+ *
+ * Don't set the color filter for now (we might want
+ * to colorize it in some fashion to warn that the
+ * row couldn't be filled in or colorized), and
+ * set the columns to placeholder values, except
+ * for the Info column, where we'll put in an
+ * error message.
+ */
+ if (dissect_columns) {
+ col_fill_in_error(cinfo, fdata_, FALSE, FALSE /* fill_fd_columns */);
+
+ cacheColumnStrings(cinfo);
+ }
+ if (dissect_color) {
+ fdata_->color_filter = NULL;
+ colorized_ = true;
+ }
+ ws_buffer_free(&buf);
+ return; /* error reading the record */
+ }
+
+ /*
+ * Determine whether we need to create a protocol tree.
+ * We do if:
+ *
+ * we're going to apply a color filter to this packet;
+ *
+ * we're need to fill in the columns and we have custom columns
+ * (which require field values, which currently requires that
+ * we build a protocol tree).
+ *
+ * XXX - field extractors? (Not done for GTK+....)
+ */
+ create_proto_tree = ((dissect_color && color_filters_used()) ||
+ (dissect_columns && (have_custom_cols(cinfo) ||
+ have_field_extractors())));
+
+ epan_dissect_init(&edt, cap_file->epan,
+ create_proto_tree,
+ FALSE /* proto_tree_visible */);
+
+ /* Re-color when the coloring rules are changed via the UI. */
+ if (dissect_color) {
+ color_filters_prime_edt(&edt);
+ fdata_->flags.need_colorize = 1;
+ }
+ if (dissect_columns)
+ col_custom_prime_edt(&edt, cinfo);
+
+ /*
+ * XXX - need to catch an OutOfMemoryError exception and
+ * attempt to recover from it.
+ */
+ epan_dissect_run(&edt, cap_file->cd_t, &phdr, frame_tvbuff_new_buffer(fdata_, &buf), fdata_, cinfo);
+
+ if (dissect_columns) {
+ /* "Stringify" non frame_data vals */
+ epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
+ cacheColumnStrings(cinfo);
+ }
+
+ if (dissect_color) {
+ colorized_ = true;
+ }
+ data_ver_ = col_data_ver_;
+
+ packet_info *pi = &edt.pi;
+ conv_ = find_conversation(pi->num, &pi->src, &pi->dst, pi->ptype,
+ pi->srcport, pi->destport, 0);
+
+ epan_dissect_cleanup(&edt);
+ ws_buffer_free(&buf);
+}
+
+// This assumes only one packet list. We might want to move this to
+// PacketListModel (or replace this with a wmem allocator).
+struct _GStringChunk *PacketListRecord::string_pool_ = g_string_chunk_new(1 * 1024 * 1024);
+void PacketListRecord::clearStringPool()
+{
+ g_string_chunk_clear(string_pool_);
+}
+
+//#define MINIMIZE_STRING_COPYING 1
+void PacketListRecord::cacheColumnStrings(column_info *cinfo)
+{
+ // packet_list_store.c:packet_list_change_record(PacketList *packet_list, PacketListRecord *record, gint col, column_info *cinfo)
+ if (!cinfo) {
+ return;
+ }
+
+ if (col_text_) {
+ col_text_->clear();
+ } else {
+ col_text_ = new ColumnTextList;
+ }
+ lines_ = 1;
+ line_count_changed_ = false;
+
+ for (int column = 0; column < cinfo->num_cols; ++column) {
+ int col_lines = 1;
+
+#ifdef MINIMIZE_STRING_COPYING
+ int text_col = cinfo_column_.value(column, -1);
+
+ /* Column based on frame_data or it already contains a value */
+ if (text_col < 0) {
+ col_fill_in_frame_data(fdata_, cinfo, column, FALSE);
+ col_text_->append(cinfo->columns[column].col_data);
+ continue;
+ }
+
+ switch (cinfo->col_fmt[column]) {
+ case COL_PROTOCOL:
+ case COL_INFO:
+ case COL_IF_DIR:
+ case COL_DCE_CALL:
+ case COL_8021Q_VLAN_ID:
+ case COL_EXPERT:
+ case COL_FREQ_CHAN:
+ if (cinfo->columns[column].col_data && cinfo->columns[column].col_data != cinfo->columns[column].col_buf) {
+ /* This is a constant string, so we don't have to copy it */
+ // XXX - ui/gtk/packet_list_store.c uses G_MAXUSHORT. We don't do proper UTF8
+ // truncation in either case.
+ int col_text_len = MIN(qstrlen(cinfo->col_data[column]) + 1, COL_MAX_INFO_LEN);
+ col_text_->append(QByteArray::fromRawData(cinfo->columns[column].col_data, col_text_len));
+ break;
+ }
+ /* !! FALL-THROUGH!! */
+
+ case COL_DEF_SRC:
+ case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
+ case COL_UNRES_SRC:
+ case COL_DEF_DL_SRC:
+ case COL_RES_DL_SRC:
+ case COL_UNRES_DL_SRC:
+ case COL_DEF_NET_SRC:
+ case COL_RES_NET_SRC:
+ case COL_UNRES_NET_SRC:
+ case COL_DEF_DST:
+ case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
+ case COL_UNRES_DST:
+ case COL_DEF_DL_DST:
+ case COL_RES_DL_DST:
+ case COL_UNRES_DL_DST:
+ case COL_DEF_NET_DST:
+ case COL_RES_NET_DST:
+ case COL_UNRES_NET_DST:
+ default:
+ if (!get_column_resolved(column) && cinfo->col_expr.col_expr_val[column]) {
+ /* Use the unresolved value in col_expr_val */
+ // XXX Use QContiguousCache?
+ col_text_->append(cinfo->col_expr.col_expr_val[column]);
+ } else {
+ col_text_->append(cinfo->columns[column].col_data);
+ }
+ break;
+ }
+#else // MINIMIZE_STRING_COPYING
+ const char *col_str;
+ if (!get_column_resolved(column) && cinfo->col_expr.col_expr_val[column]) {
+ /* Use the unresolved value in col_expr_val */
+ col_str = cinfo->col_expr.col_expr_val[column];
+ } else {
+ int text_col = cinfo_column_.value(column, -1);
+
+ if (text_col < 0) {
+ col_fill_in_frame_data(fdata_, cinfo, column, FALSE);
+ }
+ col_str = cinfo->columns[column].col_data;
+ }
+ // g_string_chunk_insert_const manages a hash table of pointers to
+ // strings:
+ // https://git.gnome.org/browse/glib/tree/glib/gstringchunk.c
+ // We might be better off adding the equivalent functionality to
+ // wmem_tree.
+ col_text_->append(g_string_chunk_insert_const(string_pool_, col_str));
+ for (int i = 0; col_str[i]; i++) {
+ if (col_str[i] == '\n') col_lines++;
+ }
+ if (col_lines > lines_) {
+ lines_ = col_lines;
+ line_count_changed_ = true;
+ }
+#endif // MINIMIZE_STRING_COPYING
+ }
+}
+
+/*
+ * 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/packet_list_record.h b/ui/qt/models/packet_list_record.h
new file mode 100644
index 0000000000..80d9cf7efa
--- /dev/null
+++ b/ui/qt/models/packet_list_record.h
@@ -0,0 +1,106 @@
+/* packet_list_record.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PACKET_LIST_RECORD_H
+#define PACKET_LIST_RECORD_H
+
+#include <config.h>
+
+#include <glib.h>
+
+#include "cfile.h"
+
+#include <epan/column-info.h>
+#include <epan/packet.h>
+
+#include <QByteArray>
+#include <QList>
+#include <QVariant>
+
+struct conversation;
+struct _GStringChunk;
+
+class ColumnTextList;
+
+class PacketListRecord
+{
+public:
+ PacketListRecord(frame_data *frameData);
+
+ // Allocate our records using wmem.
+ static void *operator new(size_t size);
+ static void operator delete(void *) {}
+
+ // Return the string value for a column. Data is cached if possible.
+ const QByteArray columnString(capture_file *cap_file, int column, bool colorized = false);
+ frame_data *frameData() const { return fdata_; }
+ // packet_list->col_to_text in gtk/packet_list_store.c
+ static int textColumn(int column) { return cinfo_column_.value(column, -1); }
+ bool colorized() { return colorized_; }
+ struct conversation *conversation() { return conv_; }
+
+ int columnTextSize(const char *str);
+ static void resetColumns(column_info *cinfo);
+ void resetColorized();
+ inline int lineCount() { return lines_; }
+ inline int lineCountChanged() { return line_count_changed_; }
+
+ static void clearStringPool();
+
+private:
+ /** The column text for some columns */
+ ColumnTextList *col_text_;
+
+ frame_data *fdata_;
+ int lines_;
+ bool line_count_changed_;
+ static QMap<int, int> cinfo_column_;
+
+ /** Data versions. Used to invalidate col_text_ */
+ static unsigned col_data_ver_;
+ unsigned data_ver_;
+ /** Has this record been colorized? */
+ bool colorized_;
+
+ /** Conversation. Used by RelatedPacketDelegate */
+ struct conversation *conv_;
+
+ void dissect(capture_file *cap_file, bool dissect_color = false);
+ void cacheColumnStrings(column_info *cinfo);
+
+ static struct _GStringChunk *string_pool_;
+
+};
+
+#endif // PACKET_LIST_RECORD_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:
+ */
diff --git a/ui/qt/models/path_chooser_delegate.cpp b/ui/qt/models/path_chooser_delegate.cpp
new file mode 100644
index 0000000000..8538c75ea9
--- /dev/null
+++ b/ui/qt/models/path_chooser_delegate.cpp
@@ -0,0 +1,152 @@
+/* path_chooser_delegate.cpp
+ * Delegate to select a file path for a treeview entry
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include "epan/prefs.h"
+#include "ui/last_open_dir.h"
+
+#include <ui/qt/models/path_chooser_delegate.h>
+
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QFileDialog>
+#include <QWidget>
+#include <QLineEdit>
+
+PathChooserDelegate::PathChooserDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{
+}
+
+QWidget* PathChooserDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &) const
+{
+ QWidget * pathEditor = new QWidget(parent);
+ QHBoxLayout *hbox = new QHBoxLayout(pathEditor);
+ pathEditor->setLayout(hbox);
+ QLineEdit * lineEdit = new QLineEdit(pathEditor);
+ QPushButton *btnBrowse = new QPushButton(pathEditor);
+
+ btnBrowse->setText(tr("Browse"));
+
+ hbox->setContentsMargins(0, 0, 0, 0);
+ hbox->addWidget(lineEdit);
+ hbox->addWidget(btnBrowse);
+ hbox->setSizeConstraint(QLayout::SetMinimumSize);
+
+ // Grow the item to match the editor. According to the QAbstractItemDelegate
+ // documenation we're supposed to reimplement sizeHint but this seems to work.
+ QSize size = option.rect.size();
+ size.setHeight(qMax(option.rect.height(), hbox->sizeHint().height()));
+
+ lineEdit->selectAll();
+ pathEditor->setFocusProxy(lineEdit);
+ pathEditor->setFocusPolicy(lineEdit->focusPolicy());
+
+ connect(btnBrowse, SIGNAL(pressed()), this, SLOT(browse_button_clicked()));
+ return pathEditor;
+}
+
+void PathChooserDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const
+{
+ QRect rect = option.rect;
+
+ // Make sure the editor doesn't get squashed.
+ editor->adjustSize();
+ rect.setHeight(qMax(option.rect.height(), editor->height()));
+ editor->setGeometry(rect);
+}
+
+void PathChooserDelegate::browse_button_clicked()
+{
+ char *open_dir = NULL;
+
+ switch ( prefs.gui_fileopen_style )
+ {
+
+ case FO_STYLE_LAST_OPENED:
+ open_dir = get_last_open_dir();
+ break;
+
+ case FO_STYLE_SPECIFIED:
+ if ( prefs.gui_fileopen_dir[0] != '\0' )
+ open_dir = prefs.gui_fileopen_dir;
+ break;
+ }
+
+ QString file_name = QFileDialog::getOpenFileName(new QWidget(), tr("Open Pipe"), open_dir);
+ if ( !file_name.isEmpty() )
+ {
+ QWidget * parent = ((QPushButton *)sender())->parentWidget();
+ QLineEdit * lineEdit = parent->findChild<QLineEdit*>();
+ if ( lineEdit )
+ {
+ lineEdit->setText(file_name);
+ emit commitData(parent);
+
+ }
+ }
+}
+
+void PathChooserDelegate::setEditorData(QWidget *editor, const QModelIndex &idx) const
+{
+ if ( idx.isValid() )
+ {
+ QString content = idx.data().toString();
+ QLineEdit * lineEdit = editor->findChild<QLineEdit*>();
+ if ( lineEdit )
+ {
+ lineEdit->setText(content);
+ }
+ }
+ else
+ QStyledItemDelegate::setEditorData(editor, idx);
+}
+
+void PathChooserDelegate::setModelData(QWidget *editor, QAbstractItemModel * model, const QModelIndex &idx) const
+{
+ if ( idx.isValid() )
+ {
+ QLineEdit * lineEdit = editor->findChild<QLineEdit*>();
+ if ( lineEdit )
+ {
+ model->setData(idx, lineEdit->text());
+ }
+ }
+ else
+ {
+ QStyledItemDelegate::setModelData(editor, model, idx);
+ }
+}
+
+/*
+ * 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/path_chooser_delegate.h b/ui/qt/models/path_chooser_delegate.h
new file mode 100644
index 0000000000..32ecd96eab
--- /dev/null
+++ b/ui/qt/models/path_chooser_delegate.h
@@ -0,0 +1,58 @@
+/* path_chooser_delegate.h
+ * Delegate to select a file path for a treeview entry
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PATH_CHOOSER_DELEGATE_H_
+#define PATH_CHOOSER_DELEGATE_H_
+
+#include <QStyledItemDelegate>
+
+class PathChooserDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ PathChooserDelegate(QObject *parent = 0);
+
+protected:
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &idx) const;
+ void updateEditorGeometry (QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & idx) const;
+ void setEditorData(QWidget *editor, const QModelIndex &idx) const;
+ void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &idx) const;
+
+private slots:
+ void browse_button_clicked();
+};
+
+#endif /* PATH_CHOOSER_DELEGATE_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:
+ */
diff --git a/ui/qt/models/percent_bar_delegate.cpp b/ui/qt/models/percent_bar_delegate.cpp
new file mode 100644
index 0000000000..5158ac6ca0
--- /dev/null
+++ b/ui/qt/models/percent_bar_delegate.cpp
@@ -0,0 +1,116 @@
+/* percent_bar_delegate.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/percent_bar_delegate.h>
+
+#include <ui/qt/utils/color_utils.h>
+
+#include <QApplication>
+#include <QPainter>
+
+static const int bar_em_width_ = 8;
+static const double bar_blend_ = 0.15;
+
+void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionViewItem option_vi = option;
+ QStyledItemDelegate::initStyleOption(&option_vi, index);
+
+ // Paint our rect with no text using the current style, then draw our
+ // bar and text over it.
+ QStyledItemDelegate::paint(painter, option, index);
+
+ bool ok = false;
+ double value = index.data(Qt::UserRole).toDouble(&ok);
+
+ if (!ok || !index.data(Qt::DisplayRole).toString().isEmpty()) {
+ // We don't have a valid value or the item has visible text.
+ return;
+ }
+
+ // If our value is out range our caller has a bug. Clamp the graph and
+ // Print the numeric value so that the bug is obvious.
+ QString pct_str = QString::number(value, 'f', 1);
+ if (value < 0) {
+ value = 0;
+ }
+ if (value > 100.0) {
+ value = 100.0;
+ }
+
+ if (QApplication::style()->objectName().contains("vista")) {
+ // QWindowsVistaStyle::drawControl does this internally. Unfortunately there
+ // doesn't appear to be a more general way to do this.
+ option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText,
+ option_vi.palette.color(QPalette::Active, QPalette::Text));
+ }
+
+ QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ QColor text_color = option_vi.palette.color(cg, QPalette::Text);
+ QColor bar_color = ColorUtils::alphaBlend(option_vi.palette.windowText(),
+ option_vi.palette.window(), bar_blend_);
+
+ if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ if (option_vi.state & QStyle::State_Selected) {
+ text_color = option_vi.palette.color(cg, QPalette::HighlightedText);
+ bar_color = ColorUtils::alphaBlend(option_vi.palette.color(cg, QPalette::Window),
+ option_vi.palette.color(cg, QPalette::Highlight),
+ bar_blend_);
+ }
+
+ painter->save();
+ int border_radius = 3; // We use 3 px elsewhere, e.g. filter combos.
+ QRect pct_rect = option.rect;
+ pct_rect.adjust(1, 1, -1, -1);
+ pct_rect.setWidth(((pct_rect.width() * value) / 100.0) + 0.5);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(bar_color);
+ painter->drawRoundedRect(pct_rect, border_radius, border_radius);
+ painter->restore();
+
+ painter->save();
+ painter->setPen(text_color);
+ painter->drawText(option.rect, Qt::AlignCenter, pct_str);
+ painter->restore();
+}
+
+QSize PercentBarDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ return QSize(option.fontMetrics.height() * bar_em_width_,
+ QStyledItemDelegate::sizeHint(option, index).height());
+}
+
+/*
+ * 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/percent_bar_delegate.h b/ui/qt/models/percent_bar_delegate.h
new file mode 100644
index 0000000000..2c8d47288f
--- /dev/null
+++ b/ui/qt/models/percent_bar_delegate.h
@@ -0,0 +1,79 @@
+/* percent_bar_delegate.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PERCENTBARDELEGATE_H
+#define PERCENTBARDELEGATE_H
+
+/*
+ * @file Percent bar delegate.
+ *
+ * QStyledItemDelegate subclass that will draw a percentage value and a
+ * single-item bar chart for the specified value.
+ *
+ * This is intended to be used in QTreeWidgets to show percentage values.
+ * To use it, first call setItemDelegate:
+ *
+ * myTreeWidget()->setItemDelegateForColumn(col_pct_, new PercentBarDelegate());
+ *
+ * Then, for each QTreeWidgetItem, set a double value using setData:
+ *
+ * setData(col_pct_, Qt::UserRole, QVariant::fromValue<double>(packets_ * 100.0 / num_packets));
+ *
+ * If the item data cannot be converted to a valid double value or if its
+ * text string is non-empty then it will be rendered normally (i.e. the
+ * percent text and bar will not be drawn). This lets you mix normal and
+ * percent bar rendering between rows.
+ */
+
+#include <QStyledItemDelegate>
+
+class PercentBarDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ PercentBarDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) { }
+
+ // Make sure QStyledItemDelegate::paint doesn't draw any text.
+ virtual QString displayText(const QVariant &, const QLocale &) const { return QString(); }
+
+protected:
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+};
+
+#endif // PERCENTBARDELEGATE_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:
+ */
diff --git a/ui/qt/models/related_packet_delegate.cpp b/ui/qt/models/related_packet_delegate.cpp
new file mode 100644
index 0000000000..6d02224f70
--- /dev/null
+++ b/ui/qt/models/related_packet_delegate.cpp
@@ -0,0 +1,273 @@
+/* related_packet_delegate.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/related_packet_delegate.h>
+#include "packet_list_record.h"
+
+#include <ui/qt/utils/color_utils.h>
+
+#include <QApplication>
+#include <QPainter>
+
+// To do:
+// - Add other frame types and symbols. If `tshark -G fields | grep FT_FRAMENUM`
+// is any indication, we should add "reassembly" and "reassembly error"
+// fields.
+// - Don't add *too* many frame types and symbols. The goal is context, not
+// clutter.
+// - Add tooltips. It looks like this needs to be done in ::helpEvent
+// or PacketListModel::data.
+// - Add "Go -> Next Related" and "Go -> Previous Related"?
+// - Apply as filter?
+
+RelatedPacketDelegate::RelatedPacketDelegate(QWidget *parent) :
+ QStyledItemDelegate(parent),
+ conv_(NULL),
+ current_frame_(0)
+{
+ clear();
+}
+
+void RelatedPacketDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ QStyleOptionViewItemV4 option_vi = option;
+#else
+ QStyleOptionViewItem option_vi = option;
+#endif
+ QStyledItemDelegate::initStyleOption(&option_vi, index);
+ int em_w = option_vi.fontMetrics.height();
+ int en_w = (em_w + 1) / 2;
+ int line_w = (option_vi.fontMetrics.lineWidth());
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ option_vi.features |= QStyleOptionViewItemV4::HasDecoration;
+#else
+ option_vi.features |= QStyleOptionViewItem::HasDecoration;
+#endif
+ option_vi.decorationSize.setHeight(1);
+ option_vi.decorationSize.setWidth(em_w);
+ QStyledItemDelegate::paint(painter, option_vi, index);
+
+ guint32 setup_frame = 0, last_frame = 0;
+ if (conv_) {
+ setup_frame = (int) conv_->setup_frame;
+ last_frame = (int) conv_->last_frame;
+ }
+
+ const frame_data *fd;
+ PacketListRecord *record = static_cast<PacketListRecord*>(index.internalPointer());
+ if (!record || (fd = record->frameData()) == NULL) {
+ return;
+ }
+
+ painter->save();
+
+ if (QApplication::style()->objectName().contains("vista")) {
+ // QWindowsVistaStyle::drawControl does this internally. Unfortunately there
+ // doesn't appear to be a more general way to do this.
+ option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText, option_vi.palette.color(QPalette::Active, QPalette::Text));
+ }
+
+ QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ QColor fg;
+ if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ if (option_vi.state & QStyle::State_Selected) {
+ fg = option_vi.palette.color(cg, QPalette::HighlightedText);
+ } else {
+ fg = option_vi.palette.color(cg, QPalette::Text);
+ }
+
+ fg = ColorUtils::alphaBlend(fg, option_vi.palette.color(cg, QPalette::Base), 0.5);
+ QPen line_pen(fg);
+ line_pen.setWidth(line_w);
+ line_pen.setJoinStyle(Qt::RoundJoin);
+
+ painter->setPen(line_pen);
+ painter->translate(option_vi.rect.x(), option_vi.rect.y());
+ painter->translate(en_w + 0.5, 0.5);
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ int height = option_vi.rect.height();
+
+ // Uncomment to make the boundary visible.
+// painter->save();
+// painter->setPen(Qt::darkRed);
+// painter->drawRect(QRectF(0.5, 0.5, en_w - 1, height - 1));
+// painter->restore();
+
+ // The current decorations are based on what looked good and were easy
+ // to code.
+
+ // It might be useful to have a JACKPOT_MODE define that shows each
+ // decoration in sequence in order to make it easier to create
+ // screenshots for the User's Guide.
+
+ // Vertical line. Lower and upper half for the start and end of the
+ // conversation respectively, solid for conversation member, dashed
+ // for other packets in the start-end range.
+ if (setup_frame > 0 && last_frame > 0 && setup_frame != last_frame) {
+ if (fd->num == setup_frame) {
+ QPoint start_line[] = {
+ QPoint(en_w - 1, height / 2),
+ QPoint(0, height / 2),
+ QPoint(0, height)
+ };
+ painter->drawPolyline(start_line, 3);
+ } else if (fd->num > setup_frame && fd->num < last_frame) {
+ painter->save();
+ if (conv_ != record->conversation()) {
+ QPen other_pen(line_pen);
+ other_pen.setStyle(Qt::DashLine);
+ painter->setPen(other_pen);
+ }
+ painter->drawLine(0, 0, 0, height);
+ painter->restore();
+ } else if (fd->num == last_frame) {
+ QPoint end_line[] = {
+ QPoint(en_w - 1, height / 2),
+ QPoint(0, height / 2),
+ QPoint(0, 0)
+ };
+ painter->drawPolyline(end_line, 3);
+ }
+ }
+
+ // Related packet indicator. Rightward arrow for requests, leftward
+ // arrow for responses, circle for others.
+ // XXX These are comically oversized when we have multi-line rows.
+ if (related_frames_.contains(fd->num)) {
+ painter->setBrush(fg);
+ switch (related_frames_[fd->num]) {
+ // Request and response arrows are moved forward one pixel in order to
+ // maximize white space between the heads and the conversation line.
+ case FT_FRAMENUM_REQUEST:
+ {
+ int hh = height / 2;
+ QPoint tail(2 - en_w, hh);
+ QPoint head(en_w, hh);
+ drawArrow(painter, tail, head, hh / 2);
+ break;
+ }
+ case FT_FRAMENUM_RESPONSE:
+ {
+ int hh = height / 2;
+ QPoint tail(en_w - 1, hh);
+ QPoint head(1 - en_w, hh);
+ drawArrow(painter, tail, head, hh / 2);
+ break;
+ }
+ case FT_FRAMENUM_ACK:
+ {
+ QRect bbox (2 - en_w, height / 3, em_w - 2, height / 2);
+ drawCheckMark(painter, bbox);
+ break;
+ }
+ case FT_FRAMENUM_DUP_ACK:
+ {
+ QRect bbox (2 - en_w, (height / 3) - (line_w * 2), em_w - 2, height / 2);
+ drawCheckMark(painter, bbox);
+ bbox.moveTop(bbox.top() + (line_w * 3));
+ drawCheckMark(painter, bbox);
+ break;
+ }
+ case FT_FRAMENUM_NONE:
+ default:
+ painter->drawEllipse(QPointF(0.0, option_vi.rect.height() / 2), 2, 2);
+ }
+ }
+
+ painter->restore();
+}
+
+QSize RelatedPacketDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ return QSize(option.fontMetrics.height() + QStyledItemDelegate::sizeHint(option, index).width(),
+ QStyledItemDelegate::sizeHint(option, index).height());
+}
+
+void RelatedPacketDelegate::drawArrow(QPainter *painter, const QPoint tail, const QPoint head, int head_size) const
+{
+ int x_mul = head.x() > tail.x() ? -1 : 1;
+ QPoint head_points[] = {
+ head,
+ QPoint(head.x() + (head_size * x_mul), head.y() + (head_size / 2)),
+ QPoint(head.x() + (head_size * x_mul), head.y() - (head_size / 2)),
+ };
+
+ painter->drawLine(tail.x(), tail.y(), head.x() + (head_size * x_mul), head.y());
+ painter->drawPolygon(head_points, 3);
+}
+
+void RelatedPacketDelegate::drawCheckMark(QPainter *painter, const QRect bbox) const
+{
+ QPoint cm_points[] = {
+ QPoint(bbox.x(), bbox.y() + (bbox.height() / 2)),
+ QPoint(bbox.x() + (bbox.width() / 4), bbox.y() + (bbox.height() * 3 / 4)),
+ bbox.topRight()
+ };
+ painter->drawPolyline(cm_points, 3);
+}
+
+void RelatedPacketDelegate::clear()
+{
+ related_frames_.clear();
+ current_frame_ = 0;
+ conv_ = NULL;
+}
+
+void RelatedPacketDelegate::addRelatedFrame(int frame_num, ft_framenum_type_t framenum_type)
+{
+ related_frames_[frame_num] = framenum_type;
+ // Last match wins. Last match might not make sense, however.
+ if (current_frame_ > 0) {
+ switch (framenum_type) {
+ case FT_FRAMENUM_REQUEST:
+ related_frames_[current_frame_] = FT_FRAMENUM_RESPONSE;
+ break;
+ case FT_FRAMENUM_RESPONSE:
+ related_frames_[current_frame_] = FT_FRAMENUM_REQUEST;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void RelatedPacketDelegate::setConversation(conversation *conv)
+{
+ conv_ = conv;
+}
+
+/*
+ * 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/related_packet_delegate.h b/ui/qt/models/related_packet_delegate.h
new file mode 100644
index 0000000000..152448dfe6
--- /dev/null
+++ b/ui/qt/models/related_packet_delegate.h
@@ -0,0 +1,78 @@
+/* related_packet_delegate.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RELATED_PACKET_DELEGATE_H
+#define RELATED_PACKET_DELEGATE_H
+
+#include <config.h>
+
+#include "epan/conversation.h"
+
+#include <QHash>
+#include <QStyledItemDelegate>
+
+class QPainter;
+struct conversation;
+
+class RelatedPacketDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+ RelatedPacketDelegate(QWidget *parent = 0);
+ void clear();
+ void setCurrentFrame(guint32 current_frame) { current_frame_ = current_frame; }
+ void setConversation(struct conversation *conv);
+
+public slots:
+ void addRelatedFrame(int frame_num, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE);
+
+protected:
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+
+private:
+ QHash<int, ft_framenum_type_t> related_frames_;
+ struct conversation *conv_;
+ guint32 current_frame_;
+
+ void drawArrow(QPainter *painter, const QPoint tail, const QPoint head, int head_size) const;
+ void drawCheckMark(QPainter *painter, const QRect bbox) const;
+signals:
+
+
+};
+
+#endif // RELATED_PACKET_DELEGATE_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:
+ */
diff --git a/ui/qt/models/sparkline_delegate.cpp b/ui/qt/models/sparkline_delegate.cpp
new file mode 100644
index 0000000000..2c3fc9c232
--- /dev/null
+++ b/ui/qt/models/sparkline_delegate.cpp
@@ -0,0 +1,120 @@
+/* sparkline_delegate.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/sparkline_delegate.h>
+
+#include <QPainter>
+#include <QApplication>
+
+#define SPARKLINE_MIN_EM_WIDTH 10
+
+void SparkLineDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QList<int> points = qvariant_cast<QList<int> >(index.data(Qt::UserRole));
+ int max = 1;
+ int em_w = option.fontMetrics.height();
+ int content_w = option.rect.width() - (em_w / 4);
+ int content_h = option.fontMetrics.ascent() - 1;
+ int val;
+ qreal idx = 0.0;
+ qreal step_w = em_w / 10.0;
+ qreal steps = content_w / step_w;
+ QVector<QPointF> fpoints;
+
+ QStyledItemDelegate::paint(painter, option, index);
+
+ if (points.isEmpty() || steps < 1.0 || content_h <= 0) {
+ return;
+ }
+
+ while((qreal) points.length() > steps) {
+ points.removeFirst();
+ }
+
+ foreach (val, points) {
+ if (val > max) max = val;
+ }
+
+ foreach (val, points) {
+ fpoints.append(QPointF(idx, (qreal) content_h - (val * content_h / max)));
+ idx = idx + step_w;
+ }
+
+ QStyleOptionViewItem option_vi = option;
+ QStyledItemDelegate::initStyleOption(&option_vi, index);
+
+ painter->save();
+
+ if (QApplication::style()->objectName().contains("vista")) {
+ // QWindowsVistaStyle::drawControl does this internally. Unfortunately there
+ // doesn't appear to be a more general way to do this.
+ option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText, option_vi.palette.color(QPalette::Active, QPalette::Text));
+ }
+
+ QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ if (option_vi.state & QStyle::State_Selected) {
+ painter->setPen(option_vi.palette.color(cg, QPalette::HighlightedText));
+ } else {
+ painter->setPen(option_vi.palette.color(cg, QPalette::Text));
+ }
+
+ painter->setRenderHint(QPainter::Antialiasing, true);
+ painter->translate(
+ option.rect.x() + (em_w / 8) + 0.5,
+ option.rect.y() + ((option.rect.height() - option.fontMetrics.height()) / 2) + 1 + 0.5);
+ painter->drawPolyline(QPolygonF(fpoints));
+
+// painter->setPen(Qt::NoPen);
+// painter->setBrush(option.palette.foreground());
+// painter->drawEllipse(fpoints.first(), 2, 2);
+
+// painter->setBrush(Qt::red);
+// painter->drawEllipse(fpoints.last(), 2, 2);
+
+ painter->restore();
+}
+
+QSize SparkLineDelegate::sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ return QSize(option.fontMetrics.height() * SPARKLINE_MIN_EM_WIDTH, QStyledItemDelegate::sizeHint(option, index).height());
+}
+
+QWidget *SparkLineDelegate::createEditor(QWidget *, const QStyleOptionViewItem &, const QModelIndex &) const
+{
+ return NULL;
+}
+
+/*
+ * 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/sparkline_delegate.h b/ui/qt/models/sparkline_delegate.h
new file mode 100644
index 0000000000..62599abea6
--- /dev/null
+++ b/ui/qt/models/sparkline_delegate.h
@@ -0,0 +1,60 @@
+/* sparkline_delegate.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef SPARKLINE_DELEGATE_H
+#define SPARKLINE_DELEGATE_H
+
+#include <QStyledItemDelegate>
+
+class SparkLineDelegate : public QStyledItemDelegate
+{
+public:
+ SparkLineDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
+
+protected:
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QSize sizeHint(const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+signals:
+
+public slots:
+
+};
+
+Q_DECLARE_METATYPE(QList<int>)
+
+#endif // SPARKLINE_DELEGATE_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:
+ */
diff --git a/ui/qt/models/timeline_delegate.cpp b/ui/qt/models/timeline_delegate.cpp
new file mode 100644
index 0000000000..c9b0122b6a
--- /dev/null
+++ b/ui/qt/models/timeline_delegate.cpp
@@ -0,0 +1,102 @@
+/* timeline_delegate.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/timeline_delegate.h>
+
+#include <ui/qt/utils/color_utils.h>
+
+#include <QApplication>
+#include <QPainter>
+
+// XXX We might want to move this to conversation_dialog.cpp.
+
+// PercentBarDelegate uses a stronger blend value, but its bars are also
+// more of a prominent feature. Make the blend weaker here so that we don't
+// obscure our text.
+static const double bar_blend_ = 0.08;
+
+TimelineDelegate::TimelineDelegate(QWidget *parent) :
+ QStyledItemDelegate(parent)
+{}
+
+void TimelineDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ QStyleOptionViewItem option_vi = option;
+ QStyledItemDelegate::initStyleOption(&option_vi, index);
+
+ struct timeline_span span_px = index.data(Qt::UserRole).value<struct timeline_span>();
+
+ // Paint our rect with no text using the current style, then draw our
+ // bar and text over it.
+ QStyledItemDelegate::paint(painter, option, index);
+
+ if (QApplication::style()->objectName().contains("vista")) {
+ // QWindowsVistaStyle::drawControl does this internally. Unfortunately there
+ // doesn't appear to be a more general way to do this.
+ option_vi.palette.setColor(QPalette::All, QPalette::HighlightedText,
+ option_vi.palette.color(QPalette::Active, QPalette::Text));
+ }
+
+ QPalette::ColorGroup cg = option_vi.state & QStyle::State_Enabled
+ ? QPalette::Normal : QPalette::Disabled;
+ QColor text_color = option_vi.palette.color(cg, QPalette::Text);
+ QColor bar_color = ColorUtils::alphaBlend(option_vi.palette.windowText(),
+ option_vi.palette.window(), bar_blend_);
+
+ if (cg == QPalette::Normal && !(option_vi.state & QStyle::State_Active))
+ cg = QPalette::Inactive;
+ if (option_vi.state & QStyle::State_Selected) {
+ text_color = option_vi.palette.color(cg, QPalette::HighlightedText);
+ bar_color = ColorUtils::alphaBlend(option_vi.palette.color(cg, QPalette::Window),
+ option_vi.palette.color(cg, QPalette::Highlight),
+ bar_blend_);
+ }
+
+ painter->save();
+ int border_radius = 3; // We use 3 px elsewhere, e.g. filter combos.
+ QRect timeline_rect = option.rect;
+ timeline_rect.adjust(span_px.start, 1, 0, -1);
+ timeline_rect.setWidth(span_px.width);
+ painter->setClipRect(option.rect);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(bar_color);
+ painter->drawRoundedRect(timeline_rect, border_radius, border_radius);
+ painter->restore();
+
+ painter->save();
+ painter->setPen(text_color);
+ painter->drawText(option.rect, Qt::AlignCenter, index.data(Qt::DisplayRole).toString());
+ painter->restore();
+}
+
+/*
+ * 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/timeline_delegate.h b/ui/qt/models/timeline_delegate.h
new file mode 100644
index 0000000000..bc64c24877
--- /dev/null
+++ b/ui/qt/models/timeline_delegate.h
@@ -0,0 +1,84 @@
+/* timeline_delegate.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TIMELINE_DELEGATE_H
+#define TIMELINE_DELEGATE_H
+
+/*
+ * @file Timeline delegate.
+ *
+ * QStyledItemDelegate subclass that will draw a timeline indicator for
+ * the specified value.
+ *
+ * This is intended to be used in QTreeWidgets to show timelines, e.g. for
+ * conversations.
+ * To use it, first call setItemDelegate:
+ *
+ * myTreeWidget()->setItemDelegateForColumn(col_time_start_, new TimelineDelegate());
+ *
+ * Then, for each QTreeWidgetItem, set or return a timeline_span for the start and end
+ * of the timeline in pixels relative to the column width.
+ *
+ * setData(col_start_, Qt::UserRole, start_span);
+ * setData(col_end_, Qt::UserRole, end_span);
+ *
+ */
+
+#include <QStyledItemDelegate>
+
+// Pixels are relative to item rect and will be clipped.
+struct timeline_span {
+ int start;
+ int width;
+};
+
+Q_DECLARE_METATYPE(timeline_span)
+
+class TimelineDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ TimelineDelegate(QWidget *parent = 0);
+
+ // Make sure QStyledItemDelegate::paint doesn't draw any text.
+ virtual QString displayText(const QVariant &, const QLocale &) const { return QString(); }
+
+protected:
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const;
+private:
+};
+
+#endif // TIMELINE_DELEGATE_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:
+ */
diff --git a/ui/qt/models/uat_delegate.cpp b/ui/qt/models/uat_delegate.cpp
new file mode 100644
index 0000000000..ee149a2fc8
--- /dev/null
+++ b/ui/qt/models/uat_delegate.cpp
@@ -0,0 +1,217 @@
+/* uat_delegate.cpp
+ * Delegates for editing various field types in a UAT record.
+ *
+ * Copyright 2016 Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <ui/qt/models/uat_delegate.h>
+#include "epan/value_string.h"
+#include <QComboBox>
+#include <QEvent>
+#include <QFileDialog>
+#include <QLineEdit>
+#include <QCheckBox>
+
+#include <ui/qt/widgets/display_filter_edit.h>
+
+UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent)
+{
+}
+
+uat_field_t *UatDelegate::indexToField(const QModelIndex &index) const
+{
+ const QVariant v = index.model()->data(index, Qt::UserRole);
+ return static_cast<uat_field_t *>(v.value<void *>());
+}
+
+QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ uat_field_t *field = indexToField(index);
+
+ switch (field->mode) {
+ case PT_TXTMOD_DIRECTORYNAME:
+ case PT_TXTMOD_FILENAME:
+ // TODO tab navigation from this field is broken.
+ // Do not create editor, a dialog will be opened in editorEvent
+ return 0;
+
+ case PT_TXTMOD_ENUM:
+ {
+ // Note: the string repr. is written, not the integer value.
+ QComboBox *editor = new QComboBox(parent);
+ const value_string *enum_vals = (const value_string *)field->fld_data;
+ for (int i = 0; enum_vals[i].strptr != NULL; i++) {
+ editor->addItem(enum_vals[i].strptr);
+ }
+ return editor;
+ }
+
+ case PT_TXTMOD_STRING:
+ // TODO add a live validator? Should SyntaxLineEdit be used?
+ return QStyledItemDelegate::createEditor(parent, option, index);
+
+ case PT_TXTMOD_DISPLAY_FILTER:
+ {
+ DisplayFilterEdit *editor = new DisplayFilterEdit(parent);
+ return editor;
+ }
+ case PT_TXTMOD_HEXBYTES:
+ {
+ // Requires input of the form "ab cd ef" (with possibly no or a colon
+ // separator instead of a single whitespace) for the editor to accept.
+ QRegExp hexbytes_regex("([0-9a-f]{2}[ :]?)*");
+ hexbytes_regex.setCaseSensitivity(Qt::CaseInsensitive);
+ // QString types from QStyledItemDelegate are documented to return a
+ // QLineEdit. Note that Qt returns a subclass from QLineEdit which
+ // automatically adapts the width to the typed contents.
+ QLineEdit *editor = static_cast<QLineEdit *>(
+ QStyledItemDelegate::createEditor(parent, option, index));
+ editor->setValidator(new QRegExpValidator(hexbytes_regex, editor));
+ return editor;
+ }
+
+ case PT_TXTMOD_BOOL:
+ {
+ // model will handle creating checkbox
+ return 0;
+ }
+
+ case PT_TXTMOD_NONE:
+ return 0;
+
+ default:
+ g_assert_not_reached();
+ return 0;
+ }
+}
+
+void UatDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+ uat_field_t *field = indexToField(index);
+
+ switch (field->mode) {
+ case PT_TXTMOD_ENUM:
+ {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = index.model()->data(index, Qt::EditRole).toString();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ combobox->setCurrentText(data);
+#else
+ int new_index = combobox->findText(data);
+ if (new_index >= 0) {
+ combobox->setCurrentIndex(new_index);
+ }
+#endif
+
+ break;
+ }
+
+ default:
+ QStyledItemDelegate::setEditorData(editor, index);
+ }
+}
+
+void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
+ const QModelIndex &index) const
+{
+ uat_field_t *field = indexToField(index);
+
+ switch (field->mode) {
+ case PT_TXTMOD_ENUM:
+ {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = combobox->currentText();
+ model->setData(index, data, Qt::EditRole);
+ break;
+ }
+
+ default:
+ QStyledItemDelegate::setModelData(editor, model, index);
+ }
+}
+
+#if 0
+// Qt docs suggest overriding updateEditorGeometry, but the defaults seem sane.
+void UatDelegate::updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyledItemDelegate::updateEditorGeometry(editor, option, index);
+}
+#endif
+
+bool UatDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
+{
+ uat_field_t *field = indexToField(index);
+
+ switch (field->mode) {
+ case PT_TXTMOD_DIRECTORYNAME:
+ case PT_TXTMOD_FILENAME:
+ if (event && (event->type() == QEvent::MouseButtonRelease ||
+ event->type() == QEvent::MouseButtonDblClick)) {
+ // Ignore these mouse events, only handle MouseButtonPress.
+ return false;
+ }
+ if (index.isValid()) {
+ QString filename_old = model->data(index, Qt::EditRole).toString();
+ QString filename = openFileDialog(field, filename_old);
+ // TODO should this overwrite only when !filename.isEmpty()?
+ model->setData(index, filename, Qt::EditRole);
+ }
+ // returns false to ensure that QAbstractItemView::edit does not assume
+ // the editing state. This causes the view's currentIndex to be changed
+ // to the cell where this delegate was "created", as desired.
+ return false;
+
+ default:
+ return QStyledItemDelegate::editorEvent(event, model, option, index);
+ }
+}
+
+QString UatDelegate::openFileDialog(uat_field_t *field, const QString &cur_path) const
+{
+ // Note: file dialogs have their parent widget set to NULL because we do not
+ // have an editor nor the view that would attach us.
+ switch (field->mode) {
+ case PT_TXTMOD_DIRECTORYNAME:
+ return QFileDialog::getExistingDirectory(NULL, field->title, cur_path);
+
+ case PT_TXTMOD_FILENAME:
+ return QFileDialog::getOpenFileName(NULL, field->title, cur_path,
+ QString(), NULL, QFileDialog::DontConfirmOverwrite);
+
+ default:
+ g_assert_not_reached();
+ return 0;
+ }
+}
+
+/* * 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/uat_delegate.h b/ui/qt/models/uat_delegate.h
new file mode 100644
index 0000000000..795161a686
--- /dev/null
+++ b/ui/qt/models/uat_delegate.h
@@ -0,0 +1,57 @@
+/* uat_delegate.h
+ * Delegates for editing various field types in a UAT record.
+ *
+ * Copyright 2016 Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef UAT_DELEGATE_H
+#define UAT_DELEGATE_H
+
+#include <config.h>
+#include <glib.h>
+#include <epan/uat-int.h>
+#include <QStyledItemDelegate>
+
+class UatDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+
+public:
+ UatDelegate(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;
+
+#if 0
+ void updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option, const QModelIndex &index) const;
+#endif
+
+ bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index);
+
+private:
+ uat_field_t *indexToField(const QModelIndex &index) const;
+ QString openFileDialog(uat_field_t *field, const QString &cur_path) const;
+};
+#endif // UAT_DELEGATE_H
diff --git a/ui/qt/models/uat_model.cpp b/ui/qt/models/uat_model.cpp
new file mode 100644
index 0000000000..5b0813f0a6
--- /dev/null
+++ b/ui/qt/models/uat_model.cpp
@@ -0,0 +1,417 @@
+/* uat_model.cpp
+ * Data model for UAT records.
+ *
+ * Copyright 2016 Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "uat_model.h"
+#include <epan/to_str.h>
+#include <QBrush>
+#include <QDebug>
+
+UatModel::UatModel(QObject *parent, epan_uat *uat) :
+ QAbstractTableModel(parent),
+ uat_(0)
+{
+ loadUat(uat);
+}
+
+UatModel::UatModel(QObject * parent, QString tableName) :
+ QAbstractTableModel(parent),
+ uat_(0)
+{
+ loadUat(uat_get_table_by_name(tableName.toStdString().c_str()));
+}
+
+void UatModel::loadUat(epan_uat * uat)
+{
+ uat_ = uat;
+
+ dirty_records.reserve(uat_->raw_data->len);
+ // Validate existing data such that they can be marked as invalid if necessary.
+ record_errors.reserve(uat_->raw_data->len);
+ for (int i = 0; i < (int)uat_->raw_data->len; i++) {
+ record_errors.push_back(QMap<int, QString>());
+ checkRow(i);
+ // Assume that records are initially not modified.
+ dirty_records.push_back(false);
+ }
+}
+
+Qt::ItemFlags UatModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+
+ uat_field_t *field = &uat_->fields[index.column()];
+
+ Qt::ItemFlags flags = QAbstractTableModel::flags(index);
+ if (field->mode == PT_TXTMOD_BOOL)
+ {
+ flags |= Qt::ItemIsUserCheckable;
+ }
+ flags |= Qt::ItemIsEditable;
+ return flags;
+}
+
+QVariant UatModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ void *rec = UAT_INDEX_PTR(uat_, index.row());
+ uat_field_t *field = &uat_->fields[index.column()];
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ char *str = NULL;
+ guint length = 0;
+ field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data);
+
+ if (field->mode == PT_TXTMOD_HEXBYTES) {
+ char* temp_str = bytes_to_str(NULL, (const guint8 *) str, length);
+ g_free(str);
+ QString qstr(temp_str);
+ wmem_free(NULL, temp_str);
+ return qstr;
+ } else if (field->mode == PT_TXTMOD_BOOL) {
+ return "";
+ } else {
+ QString qstr(str);
+ g_free(str);
+ return qstr;
+ }
+ }
+
+ if ((role == Qt::CheckStateRole) && (field->mode == PT_TXTMOD_BOOL))
+ {
+ char *str = NULL;
+ guint length = 0;
+ enum Qt::CheckState state = Qt::Unchecked;
+ field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data);
+ if (g_strcmp0(str, "TRUE") == 0)
+ state = Qt::Checked;
+
+ g_free(str);
+ return state;
+ }
+
+ if (role == Qt::UserRole) {
+ return QVariant::fromValue(static_cast<void *>(field));
+ }
+
+ const QMap<int, QString> &errors = record_errors[index.row()];
+ // mark fields that fail the validation.
+ if (role == Qt::BackgroundRole) {
+ if (errors.contains(index.column())) {
+ // TODO is it OK to color cells like this? Maybe some other marker is better?
+ return QBrush("pink");
+ }
+ return QVariant();
+ }
+
+ // expose error message if any.
+ if (role == Qt::UserRole + 1) {
+ if (errors.contains(index.column())) {
+ return errors[index.column()];
+ }
+ return QVariant();
+ }
+
+ return QVariant();
+}
+
+QModelIndex UatModel::findRowForColumnContent(QVariant columnContent, int columnToCheckAgainst, int role)
+{
+ if (! columnContent.isValid())
+ return QModelIndex();
+
+ for(int i = 0; i < rowCount(); i++)
+ {
+ QVariant r_expr = data(index(i, columnToCheckAgainst), role);
+ if ( r_expr == columnContent )
+ return index(i, columnToCheckAgainst);
+ }
+
+ return QModelIndex();
+}
+
+QVariant UatModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal) {
+ return QVariant();
+ }
+
+ if (role == Qt::ToolTipRole && uat_->fields[section].desc) {
+ return uat_->fields[section].desc;
+ }
+
+ if (role == Qt::DisplayRole) {
+ return uat_->fields[section].title;
+ }
+
+ return QVariant();
+}
+
+int UatModel::rowCount(const QModelIndex &parent) const
+{
+ // there are no children
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return uat_->raw_data->len;
+}
+
+int UatModel::columnCount(const QModelIndex &parent) const
+{
+ // there are no children
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return uat_->ncols;
+}
+
+bool UatModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+
+ uat_field_t *field = &uat_->fields[index.column()];
+
+ if ((role != Qt::EditRole) &&
+ ((field->mode == PT_TXTMOD_BOOL) && (role != Qt::CheckStateRole)))
+ return false;
+
+ if (data(index, role) == value) {
+ // Data appears unchanged, do not do additional checks.
+ return true;
+ }
+
+ const int row = index.row();
+ void *rec = UAT_INDEX_PTR(uat_, row);
+
+ //qDebug() << "Changing (" << row << "," << index.column() << ") from " << data(index, Qt::EditRole) << " to " << value;
+ if (field->mode != PT_TXTMOD_BOOL) {
+ const QByteArray &str = value.toString().toUtf8();
+ const QByteArray &bytes = field->mode == PT_TXTMOD_HEXBYTES ? QByteArray::fromHex(str) : str;
+ field->cb.set(rec, bytes.constData(), (unsigned) bytes.size(), field->cbdata.set, field->fld_data);
+ } else {
+ if (value == Qt::Checked) {
+ field->cb.set(rec, "TRUE", 4, field->cbdata.set, field->fld_data);
+ } else {
+ field->cb.set(rec, "FALSE", 5, field->cbdata.set, field->fld_data);
+ }
+ }
+
+ QVector<int> roles;
+ roles << role;
+
+ // Check validity of all rows, obtaining a list of columns where the
+ // validity status has changed.
+ const QList<int> &updated_cols = checkRow(row);
+ if (!updated_cols.isEmpty()) {
+ roles << Qt::BackgroundRole;
+ //qDebug() << "validation status changed:" << updated_cols;
+ }
+
+ if (record_errors[row].isEmpty()) {
+ // If all fields are valid, invoke the update callback
+ if (uat_->update_cb) {
+ char *err = NULL;
+ if (!uat_->update_cb(rec, &err)) {
+ // TODO the error is not exactly on the first column, but we need a way to show the error.
+ record_errors[row].insert(0, err);
+ g_free(err);
+ }
+ }
+ uat_update_record(uat_, rec, TRUE);
+ } else {
+ uat_update_record(uat_, rec, FALSE);
+ }
+ dirty_records[row] = true;
+ uat_->changed = TRUE;
+
+ if (updated_cols.size() > updated_cols.count(index.column())) {
+ // The validation status for other columns were also affected by
+ // changing this field, mark those as dirty!
+ emit dataChanged(this->index(row, updated_cols.first()),
+ this->index(row, updated_cols.last())
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ , roles
+#endif
+ );
+ } else {
+
+ emit dataChanged(index, index
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ , roles
+#endif
+ );
+ }
+ return true;
+}
+
+bool UatModel::insertRows(int row, int count, const QModelIndex &/*parent*/)
+{
+ // support insertion of just one item for now.
+ if (count != 1 || row < 0 || row > rowCount())
+ return false;
+
+ beginInsertRows(QModelIndex(), row, row);
+
+ // Initialize with empty values, caller should use setData to populate it.
+ void *record = g_malloc0(uat_->record_size);
+ for (int col = 0; col < columnCount(); col++) {
+ uat_field_t *field = &uat_->fields[col];
+ field->cb.set(record, "", 0, field->cbdata.set, field->fld_data);
+ }
+ uat_insert_record_idx(uat_, row, record);
+ if (uat_->free_cb) {
+ uat_->free_cb(record);
+ }
+ g_free(record);
+
+ record_errors.insert(row, QMap<int, QString>());
+ // a new row is created. For the moment all fields are empty, so validation
+ // will likely mark everything as invalid. Ideally validation should be
+ // postponed until the row (in the view) is not selected anymore
+ checkRow(row);
+ dirty_records.insert(row, true);
+ uat_->changed = TRUE;
+ endInsertRows();
+ return true;
+}
+
+bool UatModel::removeRows(int row, int count, const QModelIndex &/*parent*/)
+{
+ if (count != 1 || row < 0 || row >= rowCount())
+ return false;
+
+ beginRemoveRows(QModelIndex(), row, row);
+ uat_remove_record_idx(uat_, row);
+ record_errors.removeAt(row);
+ dirty_records.removeAt(row);
+ uat_->changed = TRUE;
+ endRemoveRows();
+ return true;
+}
+
+bool UatModel::copyRow(int dst_row, int src_row)
+{
+ if (src_row < 0 || src_row >= rowCount() || dst_row < 0 || dst_row >= rowCount()) {
+ return false;
+ }
+
+ const void *src_record = UAT_INDEX_PTR(uat_, src_row);
+ void *dst_record = UAT_INDEX_PTR(uat_, dst_row);
+ // insertRows always initializes the record with empty value. Before copying
+ // over the new values, be sure to clear the old fields.
+ if (uat_->free_cb) {
+ uat_->free_cb(dst_record);
+ }
+ if (uat_->copy_cb) {
+ uat_->copy_cb(dst_record, src_record, uat_->record_size);
+ } else {
+ /* According to documentation of uat_copy_cb_t memcpy should be used if uat_->copy_cb is NULL */
+ memcpy(dst_record, src_record, uat_->record_size);
+ }
+ gboolean src_valid = g_array_index(uat_->valid_data, gboolean, src_row);
+ uat_update_record(uat_, dst_record, src_valid);
+ record_errors[dst_row] = record_errors[src_row];
+ dirty_records[dst_row] = true;
+
+ QVector<int> roles;
+ roles << Qt::EditRole << Qt::BackgroundRole;
+ emit dataChanged(index(dst_row, 0), index(dst_row, columnCount())
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ , roles
+#endif
+ );
+
+ return true;
+}
+
+
+bool UatModel::hasErrors() const
+{
+ for (int i = 0; i < rowCount(); i++) {
+ // Ignore errors on unmodified records, these should not prevent the OK
+ // button from saving modifications to other entries.
+ if (dirty_records[i] && !record_errors[i].isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool UatModel::checkField(int row, int col, char **error) const
+{
+ uat_field_t *field = &uat_->fields[col];
+ void *rec = UAT_INDEX_PTR(uat_, row);
+
+ if (!field->cb.chk) {
+ return true;
+ }
+
+ char *str = NULL;
+ guint length;
+ field->cb.tostr(rec, &str, &length, field->cbdata.tostr, field->fld_data);
+
+ bool ok = field->cb.chk(rec, str, length, field->cbdata.chk, field->fld_data, error);
+ g_free(str);
+ return ok;
+}
+
+// Validates all fields in the given record, setting error messages as needed.
+// Returns the columns that have changed (not the columns with errors).
+QList<int> UatModel::checkRow(int row)
+{
+ Q_ASSERT(0 <= row && row < rowCount());
+
+ QList<int> changed;
+ QMap<int, QString> &errors = record_errors[row];
+ for (int col = 0; col < columnCount(); col++) {
+ char *err;
+ bool error_changed = errors.remove(col) > 0;
+ if (!checkField(row, col, &err)) {
+ errors.insert(col, err);
+ g_free(err);
+ error_changed = !error_changed;
+ }
+ if (error_changed) {
+ changed << col;
+ }
+ }
+ return changed;
+}
+
+/* * 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/uat_model.h b/ui/qt/models/uat_model.h
new file mode 100644
index 0000000000..c52646d7c3
--- /dev/null
+++ b/ui/qt/models/uat_model.h
@@ -0,0 +1,70 @@
+/* uat_model.h
+ * Data model for UAT records.
+ *
+ * Copyright 2016 Peter Wu <peter@lekensteyn.nl>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef UAT_MODEL_H
+#define UAT_MODEL_H
+
+#include <config.h>
+#include <glib.h>
+
+#include <QAbstractItemModel>
+#include <QList>
+#include <QMap>
+#include <epan/uat-int.h>
+
+class UatModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ UatModel(QObject *parent, uat_t *uat = 0);
+ UatModel(QObject *parent, QString tableName);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+
+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
+
+ bool copyRow(int dst_row, int src_row);
+ bool hasErrors() const;
+
+ QModelIndex findRowForColumnContent(QVariant columnContent, int columnToCheckAgainst, int role = Qt::DisplayRole);
+
+private:
+ bool checkField(int row, int col, char **error) const;
+ QList<int> checkRow(int row);
+ void loadUat(uat_t * uat = 0);
+
+ epan_uat *uat_;
+ QList<bool> dirty_records;
+ QList<QMap<int, QString> > record_errors;
+};
+#endif // UAT_MODEL_H
diff --git a/ui/qt/models/voip_calls_info_model.cpp b/ui/qt/models/voip_calls_info_model.cpp
new file mode 100644
index 0000000000..dbb691fe8b
--- /dev/null
+++ b/ui/qt/models/voip_calls_info_model.cpp
@@ -0,0 +1,246 @@
+/* voip_calls_info_model.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "voip_calls_info_model.h"
+#include <wsutil/utf8_entities.h>
+#include <ui/qt/utils/qt_ui_utils.h>
+
+#include <QDateTime>
+
+VoipCallsInfoModel::VoipCallsInfoModel(QObject *parent) :
+ QAbstractTableModel(parent),
+ mTimeOfDay_(false)
+{
+}
+
+voip_calls_info_t *VoipCallsInfoModel::indexToCallInfo(const QModelIndex &index)
+{
+ return VariantPointer<voip_calls_info_t>::asPtr(index.data(Qt::UserRole));
+}
+
+QVariant VoipCallsInfoModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ // call_info will be non-NULL since the index is valid
+ voip_calls_info_t *call_info = static_cast<voip_calls_info_t *>(callinfos_[index.row()]);
+
+ if (role == Qt::UserRole) {
+ return VariantPointer<voip_calls_info_t>::asQVariant(call_info);
+ }
+
+ if (role != Qt::DisplayRole) {
+ return QVariant();
+ }
+
+ switch ((Column) index.column()) {
+ case StartTime:
+ return timeData(&(call_info->start_fd->abs_ts), &(call_info->start_rel_ts));
+ case StopTime:
+ return timeData(&(call_info->stop_fd->abs_ts), &(call_info->stop_rel_ts));
+ case InitialSpeaker:
+ return address_to_display_qstring(&(call_info->initial_speaker));
+ case From:
+ return call_info->from_identity;
+ case To:
+ return call_info->to_identity;
+ case Protocol:
+ return ((call_info->protocol == VOIP_COMMON) && call_info->protocol_name) ?
+ call_info->protocol_name : voip_protocol_name[call_info->protocol];
+ case Duration:
+ {
+ guint callDuration = nstime_to_sec(&(call_info->stop_fd->abs_ts)) - nstime_to_sec(&(call_info->start_fd->abs_ts));
+ return QString("%1:%2:%3").arg(callDuration / 3600, 2, 10, QChar('0')).arg((callDuration % 3600) / 60, 2, 10, QChar('0')).arg(callDuration % 60, 2, 10, QChar('0'));
+ }
+ case Packets:
+ return call_info->npackets;
+ case State:
+ return QString(voip_call_state_name[call_info->call_state]);
+ case Comments:
+ /* Add comments based on the protocol */
+ switch (call_info->protocol) {
+ case VOIP_ISUP:
+ {
+ isup_calls_info_t *isup_info = (isup_calls_info_t *)call_info->prot_info;
+ return QString("%1-%2 %3 %4-%5")
+ .arg(isup_info->ni)
+ .arg(isup_info->opc)
+ .arg(UTF8_RIGHTWARDS_ARROW)
+ .arg(isup_info->ni)
+ .arg(isup_info->dpc);
+ }
+ break;
+ case VOIP_H323:
+ {
+ h323_calls_info_t *h323_info = (h323_calls_info_t *)call_info->prot_info;
+ gboolean flag = FALSE;
+ static const QString on_str = tr("On");
+ static const QString off_str = tr("Off");
+ if (call_info->call_state == VOIP_CALL_SETUP) {
+ flag = h323_info->is_faststart_Setup;
+ } else {
+ if ((h323_info->is_faststart_Setup) && (h323_info->is_faststart_Proc)) {
+ flag = TRUE;
+ }
+ }
+ return tr("Tunneling: %1 Fast Start: %2")
+ .arg(h323_info->is_h245Tunneling ? on_str : off_str)
+ .arg(flag ? on_str : off_str);
+ }
+ break;
+ case VOIP_COMMON:
+ default:
+ return call_info->call_comment;
+ }
+ case ColumnCount:
+ g_assert_not_reached();
+ }
+ return QVariant();
+}
+
+QVariant VoipCallsInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+ switch ((Column) section) {
+ case StartTime:
+ return tr("Start Time");
+ case StopTime:
+ return tr("Stop Time");
+ case InitialSpeaker:
+ return tr("Initial Speaker");
+ case From:
+ return tr("From");
+ case To:
+ return tr("To");
+ case Protocol:
+ return tr("Protocol");
+ case Duration:
+ return tr("Duration");
+ case Packets:
+ return tr("Packets");
+ case State:
+ return tr("State");
+ case Comments:
+ return tr("Comments");
+ case ColumnCount:
+ g_assert_not_reached();
+ }
+ }
+ return QVariant();
+}
+
+int VoipCallsInfoModel::rowCount(const QModelIndex &parent) const
+{
+ // there are no children
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return callinfos_.size();
+}
+
+int VoipCallsInfoModel::columnCount(const QModelIndex &parent) const
+{
+ // there are no children
+ if (parent.isValid()) {
+ return 0;
+ }
+
+ return ColumnCount;
+}
+
+QVariant VoipCallsInfoModel::timeData(nstime_t *abs_ts, nstime_t *rel_ts) const
+{
+ if (mTimeOfDay_) {
+ return QDateTime::fromTime_t(nstime_to_sec(abs_ts)).toTimeSpec(Qt::LocalTime).toString("yyyy-MM-dd hh:mm:ss");
+ } else {
+ // XXX Pull digit count from capture file precision
+ return QString::number(nstime_to_sec(rel_ts), 'f', 6);
+ }
+}
+
+void VoipCallsInfoModel::setTimeOfDay(bool timeOfDay)
+{
+ mTimeOfDay_ = timeOfDay;
+ if (rowCount() > 0) {
+ // Update both the start and stop column in all rows.
+ emit dataChanged(index(0, StartTime), index(rowCount() - 1, StopTime));
+ }
+}
+
+void VoipCallsInfoModel::updateCalls(GQueue *callsinfos)
+{
+ if (callsinfos) {
+ GList *cur_call = g_queue_peek_nth_link(callsinfos, rowCount());
+ guint extra = g_list_length(cur_call);
+ if (extra > 0) {
+ beginInsertRows(QModelIndex(), rowCount(), rowCount() + extra - 1);
+ while (cur_call && cur_call->data) {
+ voip_calls_info_t *call_info = (voip_calls_info_t*) cur_call->data;
+ callinfos_.push_back(call_info);
+ cur_call = g_list_next(cur_call);
+ }
+ endInsertRows();
+ }
+ }
+}
+
+
+// Proxy model that allows columns to be sorted.
+VoipCallsInfoSortedModel::VoipCallsInfoSortedModel(QObject *parent) :
+ QSortFilterProxyModel(parent)
+{
+}
+
+bool VoipCallsInfoSortedModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ voip_calls_info_t *a = VoipCallsInfoModel::indexToCallInfo(source_left);
+ voip_calls_info_t *b = VoipCallsInfoModel::indexToCallInfo(source_right);
+
+ if (a && b) {
+ switch (source_left.column()) {
+ case VoipCallsInfoModel::StartTime:
+ return nstime_cmp(&(a->start_rel_ts), &(b->start_rel_ts)) < 0;
+ case VoipCallsInfoModel::StopTime:
+ return nstime_cmp(&(a->stop_rel_ts), &(b->stop_rel_ts)) < 0;
+ case VoipCallsInfoModel::InitialSpeaker:
+ return cmp_address(&(a->initial_speaker), &(b->initial_speaker)) < 0;
+ }
+ }
+
+ // fallback to string cmp on other fields
+ return QSortFilterProxyModel::lessThan(source_left, source_right);
+}
+
+/*
+ * 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/voip_calls_info_model.h b/ui/qt/models/voip_calls_info_model.h
new file mode 100644
index 0000000000..835d38e76a
--- /dev/null
+++ b/ui/qt/models/voip_calls_info_model.h
@@ -0,0 +1,96 @@
+/* voip_calls_info_model.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef VOIP_CALLS_INFO_MODEL_H
+#define VOIP_CALLS_INFO_MODEL_H
+
+#include <config.h>
+#include <glib.h>
+
+#include "ui/voip_calls.h"
+#include <ui/qt/utils/variant_pointer.h>
+
+#include <QAbstractTableModel>
+#include <QSortFilterProxyModel>
+
+class VoipCallsInfoModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ VoipCallsInfoModel(QObject *parent = 0);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation,
+ int role = Qt::DisplayRole) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ void setTimeOfDay(bool timeOfDay);
+ void updateCalls(GQueue *callsinfos);
+
+ static voip_calls_info_t *indexToCallInfo(const QModelIndex &index);
+
+ enum Column
+ {
+ StartTime,
+ StopTime,
+ InitialSpeaker,
+ From,
+ To,
+ Protocol,
+ Duration,
+ Packets,
+ State,
+ Comments,
+ ColumnCount /* not an actual column, but used to find max. cols. */
+ };
+
+private:
+ QList<void *> callinfos_;
+ bool mTimeOfDay_;
+
+ QVariant timeData(nstime_t *abs_ts, nstime_t *rel_ts) const;
+};
+
+class VoipCallsInfoSortedModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ VoipCallsInfoSortedModel(QObject *parent = 0);
+
+protected:
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const;
+};
+
+#endif // VOIP_CALLS_INFO_MODEL_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:
+ */