diff options
author | Roland Knall <rknall@gmail.com> | 2022-06-19 11:18:16 +0200 |
---|---|---|
committer | Roland Knall <rknall@gmail.com> | 2022-06-19 16:39:35 +0200 |
commit | 2b4878d452a5b38924168a2baa6a59ddeddc0c4e (patch) | |
tree | 5056f962261bddda5ceea367004b3507b31b72b0 /ui | |
parent | f75f577579e552aa6c6f3f59b101f1febc49fc83 (diff) |
Qt: Hide columns for traffic tree
Allow columns to be hidden for the traffic tree dialogs
Conversations and Endpoints and store the information profile
specific
Diffstat (limited to 'ui')
-rw-r--r-- | ui/qt/conversation_dialog.cpp | 2 | ||||
-rw-r--r-- | ui/qt/endpoint_dialog.cpp | 2 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tab.cpp | 39 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tab.h | 18 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tree.cpp | 122 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tree.h | 36 | ||||
-rw-r--r-- | ui/recent.c | 20 | ||||
-rw-r--r-- | ui/recent.h | 2 |
8 files changed, 229 insertions, 12 deletions
diff --git a/ui/qt/conversation_dialog.cpp b/ui/qt/conversation_dialog.cpp index 1dc5f02b2a..9521144ea9 100644 --- a/ui/qt/conversation_dialog.cpp +++ b/ui/qt/conversation_dialog.cpp @@ -95,7 +95,7 @@ ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf) : { trafficList()->setProtocolInfo(table_name_, &(recent.conversation_tabs)); - trafficTab()->setProtocolInfo(table_name_, trafficList()->protocols(), trafficList()->selectedProtocols(), &createModel); + trafficTab()->setProtocolInfo(table_name_, trafficList(), &(recent.conversation_tabs_columns), &createModel); trafficTab()->setDelegate(CONV_COLUMN_START, &createDelegate); trafficTab()->setDelegate(CONV_COLUMN_DURATION, &createDelegate); trafficTab()->setFilter(cf.displayFilter()); diff --git a/ui/qt/endpoint_dialog.cpp b/ui/qt/endpoint_dialog.cpp index c1a81235f8..9cb2dfb32a 100644 --- a/ui/qt/endpoint_dialog.cpp +++ b/ui/qt/endpoint_dialog.cpp @@ -69,7 +69,7 @@ EndpointDialog::EndpointDialog(QWidget &parent, CaptureFile &cf) : { trafficList()->setProtocolInfo(table_name_, &(recent.endpoint_tabs)); - trafficTab()->setProtocolInfo(table_name_, trafficList()->protocols(), trafficList()->selectedProtocols(), &createModel); + trafficTab()->setProtocolInfo(table_name_, trafficList(), &(recent.endpoint_tabs_columns), &createModel); trafficTab()->setFilter(cf.displayFilter()); displayFilterCheckBox()->setChecked(cf.displayFilter().length() > 0); connect(trafficTab(), &TrafficTab::filterAction, this, &EndpointDialog::filterAction); diff --git a/ui/qt/widgets/traffic_tab.cpp b/ui/qt/widgets/traffic_tab.cpp index 65807425a4..637b2d6310 100644 --- a/ui/qt/widgets/traffic_tab.cpp +++ b/ui/qt/widgets/traffic_tab.cpp @@ -22,6 +22,7 @@ #include <ui/qt/utils/variant_pointer.h> #include <ui/qt/widgets/traffic_tab.h> #include <ui/qt/widgets/traffic_tree.h> +#include <ui/qt/widgets/traffic_types_list.h> #include <ui/qt/widgets/detachable_tabwidget.h> #include <QStringList> @@ -176,6 +177,24 @@ bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QMod return QSortFilterProxyModel::lessThan(source_left, source_right); } +bool TrafficDataFilterProxy::filterAcceptsColumn(int source_column, const QModelIndex &) const +{ + return (!hideColumns_.contains(source_column)); +} + +void TrafficDataFilterProxy::setColumnVisibility(int column, bool visible) +{ + hideColumns_.removeAll(column); + if (!visible) + hideColumns_.append(column); + invalidateFilter(); +} + +bool TrafficDataFilterProxy::columnVisible(int column) const +{ + return ! hideColumns_.contains(column); +} + TrafficTab::TrafficTab(QWidget * parent) : DetachableTabWidget(parent) @@ -189,14 +208,17 @@ TrafficTab::TrafficTab(QWidget * parent) : TrafficTab::~TrafficTab() {} -void TrafficTab::setProtocolInfo(QString tableName, QList<int> allProtocols, QList<int> openTabs, ATapModelCallback createModel) +void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel) { setTabBasename(tableName); - _allProtocols = allProtocols; + + _allProtocols = trafficList->protocols(); if (createModel) _createModel = createModel; - setOpenTabs(openTabs); + _recentColumnList = recentColumnList; + + setOpenTabs(trafficList->selectedProtocols()); } void TrafficTab::setDelegate(int column, ATapCreateDelegate createDelegate) @@ -228,7 +250,7 @@ void TrafficTab::setDelegate(int column, ATapCreateDelegate createDelegate) QTreeView * TrafficTab::createTree(int protoId) { - TrafficTree * tree = new TrafficTree(tabBasename(), this); + TrafficTree * tree = new TrafficTree(tabBasename(), _recentColumnList, this); if (_createModel) { ATapDataModel * model = _createModel(protoId, ""); @@ -253,6 +275,8 @@ QTreeView * TrafficTab::createTree(int protoId) tree->setSelectionModel(ism); connect(ism, &QItemSelectionModel::currentChanged, this, &TrafficTab::doCurrentIndexChange); + tree->applyRecentColumns(); + tree->sortByColumn(0, Qt::AscendingOrder); connect(proxyModel, &TrafficDataFilterProxy::modelReset, this, [tree]() { @@ -262,6 +286,13 @@ QTreeView * TrafficTab::createTree(int protoId) } }); connect(proxyModel, &TrafficDataFilterProxy::modelReset, this, &TrafficTab::modelReset); + + /* If the columns for the tree have changed, contact the tab. By also having the tab + * columns changed signal connecting back to the tree, it will propagate to all trees + * registered with this tab. Attention, this heavily relies on the fact, that all + * tree data models are identical */ + connect(tree, &TrafficTree::columnsHaveChanged, this, &TrafficTab::columnsHaveChanged); + connect(this, &TrafficTab::columnsHaveChanged, tree, &TrafficTree::columnsChanged); } return tree; diff --git a/ui/qt/widgets/traffic_tab.h b/ui/qt/widgets/traffic_tab.h index c58a7ed592..a1605255a6 100644 --- a/ui/qt/widgets/traffic_tab.h +++ b/ui/qt/widgets/traffic_tab.h @@ -12,9 +12,12 @@ #include "config.h" +#include <glib.h> + #include <ui/qt/models/atap_data_model.h> #include <ui/qt/filter_action.h> #include <ui/qt/widgets/detachable_tabwidget.h> +#include <ui/qt/widgets/traffic_types_list.h> #include <QTabWidget> #include <QTreeView> @@ -67,10 +70,17 @@ class TrafficDataFilterProxy : public QSortFilterProxyModel public: TrafficDataFilterProxy(QObject *parent = nullptr); + void setColumnVisibility(int column, bool visible); + bool columnVisible(int column) const; + protected: virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const; virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; +private: + QList<int> hideColumns_; + }; /** @@ -97,13 +107,13 @@ public: * without having to removing the predefined object during setup of the UI. * * @param tableName The name for the table. Used for the protocol selection button - * @param allProtocols a list of all possible protocols. It's order will set the tab oder - * @param openTabs a list of protocol ids to open at start of dialog + * @param trafficList an element of traffictypeslist, which handles all profile selections + * @param recentColumnList a list of columns to be displayed for this traffic type * @param createModel A callback, which will create the correct model for the trees * * @see ATapModelCallback */ - void setProtocolInfo(QString tableName, QList<int> allProtocols, QList<int> openTabs, ATapModelCallback createModel); + void setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel); /** * @brief Set the Delegate object for a specific column @@ -219,6 +229,7 @@ signals: void retapRequired(); void disablingTaps(); void tabsChanged(QList<int> protocols); + void columnsHaveChanged(QList<int> columns); protected slots: @@ -230,6 +241,7 @@ private: QMap<int, int> _tabs; ATapModelCallback _createModel; QMap<int, ATapCreateDelegate> _createDelegates; + GList ** _recentColumnList; bool _disableTaps; bool _nameResolution; diff --git a/ui/qt/widgets/traffic_tree.cpp b/ui/qt/widgets/traffic_tree.cpp index 3e370da83c..40f34fa12d 100644 --- a/ui/qt/widgets/traffic_tree.cpp +++ b/ui/qt/widgets/traffic_tree.cpp @@ -25,6 +25,7 @@ #include <ui/qt/filter_action.h> #include <ui/qt/models/atap_data_model.h> #include <ui/qt/utils/variant_pointer.h> +#include <ui/qt/widgets/traffic_tab.h> #include <ui/qt/widgets/traffic_tree.h> #include <QStringList> @@ -39,20 +40,120 @@ #include <QJsonArray> #include <QJsonObject> #include <QJsonDocument> +#include <QHeaderView> -TrafficTree::TrafficTree(QString baseName, QWidget *parent) : +TrafficTreeHeaderView::TrafficTreeHeaderView(GList ** recentColumnList, QWidget * parent): + QHeaderView(Qt::Horizontal, parent) +{ + _recentColumnList = recentColumnList; + + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, &QHeaderView::customContextMenuRequested, this, &TrafficTreeHeaderView::headerContextMenu); +} + +TrafficTreeHeaderView::~TrafficTreeHeaderView() +{} + +void TrafficTreeHeaderView::headerContextMenu(const QPoint &pos) +{ + TrafficTree * tree = qobject_cast<TrafficTree *>(parent()); + if (!tree) + return; + + TrafficDataFilterProxy * proxy = qobject_cast<TrafficDataFilterProxy *>(tree->model()); + if (sender() != this || ! proxy) + return; + + QMenu ctxMenu; + + for (int col = 0; col < tree->dataModel()->columnCount(); col++) + { + QString name = tree->dataModel()->headerData(col).toString(); + QAction * action = new QAction(name); + action->setCheckable(true); + action->setChecked(proxy->columnVisible(col)); + action->setProperty("col_nr", col); + ctxMenu.addAction(action); + + connect(action, &QAction::triggered, this, &TrafficTreeHeaderView::columnTriggered); + } + + ctxMenu.exec(mapToGlobal(pos)); +} + +void TrafficTreeHeaderView::applyRecent() +{ + TrafficTree * tree = qobject_cast<TrafficTree *>(parent()); + if (!tree) + return; + + QList<int> columns; + for (GList * endTab = *_recentColumnList; endTab; endTab = endTab->next) { + QString colStr = QString((const char *)endTab->data); + bool ok = false; + int col = colStr.toInt(&ok); + if (ok) + columns << col; + } + + if (columns.count() > 0) { + TrafficDataFilterProxy * proxy = qobject_cast<TrafficDataFilterProxy *>(tree->model()); + for (int col = 0; col < tree->dataModel()->columnCount(); col++) { + proxy->setColumnVisibility(col, columns.contains(col)); + } + } +} + +void TrafficTreeHeaderView::columnTriggered(bool checked) +{ + TrafficTree * tree = qobject_cast<TrafficTree *>(parent()); + if (!tree) + return; + + TrafficDataFilterProxy * proxy = qobject_cast<TrafficDataFilterProxy *>(tree->model()); + QAction * entry = qobject_cast<QAction *>(sender()); + if (! proxy || ! entry || ! entry->property("col_nr").isValid()) + return; + + int col = entry->property("col_nr").toInt(); + proxy->setColumnVisibility(col, checked); + + prefs_clear_string_list(*_recentColumnList); + *_recentColumnList = NULL; + + QList<int> visible; + + for (int col = 0; col < tree->dataModel()->columnCount(); col++) { + if (proxy->columnVisible(col)) { + visible << col; + gchar *nr = qstring_strdup(QString::number(col)); + *_recentColumnList = g_list_append(*_recentColumnList, nr); + } + } + + emit columnsHaveChanged(visible); +} + + +TrafficTree::TrafficTree(QString baseName, GList ** recentColumnList, QWidget *parent) : QTreeView(parent) { _tapEnabled = true; _saveRaw = true; _baseName = baseName; _exportRole = ATapDataModel::UNFORMATTED_DISPLAYDATA; + _header = nullptr; setAlternatingRowColors(true); setRootIsDecorated(false); setSortingEnabled(true); setContextMenuPolicy(Qt::CustomContextMenu); + _header = new TrafficTreeHeaderView(recentColumnList); + setHeader(_header); + + connect(_header, &TrafficTreeHeaderView::columnsHaveChanged, this, &TrafficTree::columnsHaveChanged); connect(this, &QTreeView::customContextMenuRequested, this, &TrafficTree::customContextMenu); } @@ -277,3 +378,22 @@ void TrafficTree::disableTap() return; model->disableTap(); } + +void TrafficTree::applyRecentColumns() +{ + if (_header) + _header->applyRecent(); +} + +void TrafficTree::columnsChanged(QList<int> columns) +{ + TrafficDataFilterProxy * proxy = qobject_cast<TrafficDataFilterProxy *>(model()); + if (!proxy) + return; + + for (int col = 0; col < dataModel()->columnCount(); col++) { + proxy->setColumnVisibility(col, columns.contains(col)); + } + + resizeAction(); +} diff --git a/ui/qt/widgets/traffic_tree.h b/ui/qt/widgets/traffic_tree.h index 34f87b6f37..037a28d099 100644 --- a/ui/qt/widgets/traffic_tree.h +++ b/ui/qt/widgets/traffic_tree.h @@ -12,6 +12,8 @@ #include "config.h" +#include <glib.h> + #include <ui/recent.h> #include <ui/qt/models/atap_data_model.h> @@ -19,6 +21,29 @@ #include <QTreeView> #include <QMenu> +#include <QHeaderView> + +class TrafficTreeHeaderView : public QHeaderView +{ + Q_OBJECT +public: + TrafficTreeHeaderView(GList ** recentColumnList, QWidget * parent = nullptr); + ~TrafficTreeHeaderView(); + + void applyRecent(); + +signals: + void columnsHaveChanged(QList<int> visible); + +private: + GList ** _recentColumnList; + +private slots: + void headerContextMenu(const QPoint &pos); + void columnTriggered(bool checked = false); + +}; + class TrafficTree : public QTreeView { @@ -35,7 +60,7 @@ public: CLIPBOARD_JSON /* export as JSON */ } eTrafficTreeClipboard; - TrafficTree(QString baseName, QWidget *parent = nullptr); + TrafficTree(QString baseName, GList ** recentColumnList, QWidget *parent = nullptr); /** * @brief Create a menu containing clipboard copy entries for this tab @@ -48,12 +73,16 @@ public: */ QMenu * createCopyMenu(QWidget * parent = nullptr); + void applyRecentColumns(); + signals: void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); + void columnsHaveChanged(QList<int> columns); public slots: void tapListenerEnabled(bool enable); void disableTap(); + void columnsChanged(QList<int> columns); private: bool _tapEnabled; @@ -61,18 +90,21 @@ private: bool _saveRaw; QString _baseName; + TrafficTreeHeaderView * _header; + ATapDataModel * dataModel(); QMenu * createActionSubMenu(FilterAction::Action cur_action, QModelIndex idx, bool isConversation); void copyToClipboard(eTrafficTreeClipboard type); + friend class TrafficTreeHeaderView; + private slots: void customContextMenu(const QPoint &pos); void useFilterAction(); void clipboardAction(); void resizeAction(); void toggleSaveRawAction(); - }; #endif // TRAFFIC_TREE_H diff --git a/ui/recent.c b/ui/recent.c index 5f1f713a2e..c59d67da81 100644 --- a/ui/recent.c +++ b/ui/recent.c @@ -60,7 +60,9 @@ #define RECENT_LAST_USED_PROFILE "gui.last_used_profile" #define RECENT_GUI_FILEOPEN_REMEMBERED_DIR "gui.fileopen_remembered_dir" #define RECENT_GUI_CONVERSATION_TABS "gui.conversation_tabs" +#define RECENT_GUI_CONVERSATION_TABS_COLUMNS "gui.conversation_tabs_columns" #define RECENT_GUI_ENDPOINT_TABS "gui.endpoint_tabs" +#define RECENT_GUI_ENDPOINT_TABS_COLUMNS "gui.endpoint_tabs_columns" #define RECENT_GUI_RLC_PDUS_FROM_MAC_FRAMES "gui.rlc_pdus_from_mac_frames" #define RECENT_GUI_CUSTOM_COLORS "gui.custom_colors" #define RECENT_GUI_TOOLBAR_SHOW "gui.additional_toolbar_show" @@ -918,12 +920,24 @@ write_profile_recent(void) fprintf(rf, RECENT_GUI_CONVERSATION_TABS ": %s\n", string_list); g_free(string_list); + fprintf(rf, "\n# Conversation dialog tabs columns.\n"); + fprintf(rf, "# List of conversation columns numbers.\n"); + string_list = join_string_list(recent.conversation_tabs_columns); + fprintf(rf, RECENT_GUI_CONVERSATION_TABS_COLUMNS ": %s\n", string_list); + g_free(string_list); + fprintf(rf, "\n# Open endpoint dialog tabs.\n"); fprintf(rf, "# List of endpoint names, e.g. \"TCP\", \"IPv6\".\n"); string_list = join_string_list(recent.endpoint_tabs); fprintf(rf, RECENT_GUI_ENDPOINT_TABS ": %s\n", string_list); g_free(string_list); + fprintf(rf, "\n# Endpoint dialog tabs columns.\n"); + fprintf(rf, "# List of endpoint columns numbers.\n"); + string_list = join_string_list(recent.endpoint_tabs_columns); + fprintf(rf, RECENT_GUI_ENDPOINT_TABS_COLUMNS ": %s\n", string_list); + g_free(string_list); + write_recent_boolean(rf, "For RLC stats, whether to use RLC PDUs found inside MAC frames", RECENT_GUI_RLC_PDUS_FROM_MAC_FRAMES, recent.gui_rlc_use_pdus_from_mac); @@ -1122,8 +1136,12 @@ read_set_recent_pair_static(gchar *key, const gchar *value, recent.has_gui_geometry_main_lower_pane = TRUE; } else if (strcmp(key, RECENT_GUI_CONVERSATION_TABS) == 0) { recent.conversation_tabs = prefs_get_string_list(value); + } else if (strcmp(key, RECENT_GUI_CONVERSATION_TABS_COLUMNS) == 0) { + recent.conversation_tabs_columns = prefs_get_string_list(value); } else if (strcmp(key, RECENT_GUI_ENDPOINT_TABS) == 0) { recent.endpoint_tabs = prefs_get_string_list(value); + } else if (strcmp(key, RECENT_GUI_ENDPOINT_TABS_COLUMNS) == 0) { + recent.endpoint_tabs_columns = prefs_get_string_list(value); } else if (strcmp(key, RECENT_GUI_RLC_PDUS_FROM_MAC_FRAMES) == 0) { parse_recent_boolean(value, &recent.gui_rlc_use_pdus_from_mac); } else if (strcmp(key, RECENT_KEY_COL_WIDTH) == 0) { @@ -1611,6 +1629,8 @@ recent_cleanup(void) g_list_free_full(recent.gui_additional_toolbars, g_free); g_list_free_full(recent.interface_toolbars, g_free); prefs_clear_string_list(recent.conversation_tabs); + prefs_clear_string_list(recent.conversation_tabs_columns); prefs_clear_string_list(recent.endpoint_tabs); + prefs_clear_string_list(recent.endpoint_tabs_columns); prefs_clear_string_list(recent.custom_colors); } diff --git a/ui/recent.h b/ui/recent.h index beb94a96a9..682f25d2ed 100644 --- a/ui/recent.h +++ b/ui/recent.h @@ -128,7 +128,9 @@ typedef struct recent_settings_tag { gboolean sys_warn_if_no_capture; GList *col_width_list; /* column widths */ GList *conversation_tabs; /* enabled conversation dialog tabs */ + GList *conversation_tabs_columns; /* save the columns for conversation dialogs */ GList *endpoint_tabs; /* enabled endpoint dialog tabs */ + GList *endpoint_tabs_columns; /* save the columns for endpoint dialogs */ gchar *gui_fileopen_remembered_dir; /* folder of last capture loaded in File Open dialog */ gboolean gui_rlc_use_pdus_from_mac; GList *custom_colors; |