diff options
author | Roland Knall <rknall@gmail.com> | 2022-06-08 14:33:51 +0200 |
---|---|---|
committer | Roland Knall <rknall@gmail.com> | 2022-06-14 09:36:30 +0000 |
commit | a4f25e51150d81a76e6cf8644b60fdd000cd15ee (patch) | |
tree | 0f94a3dc68be55a5fafbe0a9edc099f255953e0b | |
parent | f1cbc6b6623074ad6f403993e9f0cfee9bee4300 (diff) |
Qt: Redesign TrafficTree Dialogs UI
The new UI should better group functionality and as well as better
showing which taps are available and can be used.
-rw-r--r-- | docbook/release-notes.adoc | 3 | ||||
-rw-r--r-- | ui/logwolf/CMakeLists.txt | 2 | ||||
-rw-r--r-- | ui/qt/CMakeLists.txt | 2 | ||||
-rw-r--r-- | ui/qt/conversation_dialog.cpp | 12 | ||||
-rw-r--r-- | ui/qt/endpoint_dialog.cpp | 12 | ||||
-rw-r--r-- | ui/qt/models/atap_data_model.cpp | 46 | ||||
-rw-r--r-- | ui/qt/models/atap_data_model.h | 4 | ||||
-rw-r--r-- | ui/qt/traffic_table_dialog.cpp | 25 | ||||
-rw-r--r-- | ui/qt/traffic_table_dialog.h | 2 | ||||
-rw-r--r-- | ui/qt/traffic_table_dialog.ui | 151 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tab.cpp | 240 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tab.h | 21 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_tree.cpp | 3 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_types_list.cpp | 236 | ||||
-rw-r--r-- | ui/qt/widgets/traffic_types_list.h | 100 |
15 files changed, 591 insertions, 268 deletions
diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc index c874ef985c..b9b56d9f3f 100644 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -37,6 +37,9 @@ wsbuglink:17779[] - Conversations will be sorted via second address and first port number - Endpoints will be sorted via port numbers - IPv6 addresses are sorted correctly after IPv4 addresses + - The dialog elements have been moved to make it easier to handle for new users. + - Selection of tap elements is done via list + - All configurations and options are done via a left side button row * The PCRE2 library (https://www.pcre.org/) is now a required dependency to build Wireshark. diff --git a/ui/logwolf/CMakeLists.txt b/ui/logwolf/CMakeLists.txt index 0a0b002a0e..c07b516dda 100644 --- a/ui/logwolf/CMakeLists.txt +++ b/ui/logwolf/CMakeLists.txt @@ -52,6 +52,7 @@ set(WIRESHARK_WIDGET_HEADERS ../qt/widgets/tabnav_tree_view.h ../qt/widgets/traffic_tab.h ../qt/widgets/traffic_tree.h + ../qt/widgets/traffic_types_list.h ../qt/widgets/wireshark_file_dialog.h ) @@ -279,6 +280,7 @@ set(WIRESHARK_WIDGET_SRCS ../qt/widgets/tabnav_tree_view.cpp ../qt/widgets/traffic_tab.cpp ../qt/widgets/traffic_tree.cpp + ../qt/widgets/traffic_types_list.cpp ../qt/widgets/wireshark_file_dialog.cpp ) diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 77d4452a62..e2a9f591bd 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -52,6 +52,7 @@ set(WIRESHARK_WIDGET_HEADERS widgets/tabnav_tree_view.h widgets/traffic_tab.h widgets/traffic_tree.h + widgets/traffic_types_list.h widgets/wireless_timeline.h widgets/wireshark_file_dialog.h ) @@ -304,6 +305,7 @@ set(WIRESHARK_WIDGET_SRCS widgets/tabnav_tree_view.cpp widgets/traffic_tab.cpp widgets/traffic_tree.cpp + widgets/traffic_types_list.cpp widgets/wireless_timeline.cpp widgets/wireshark_file_dialog.cpp ) diff --git a/ui/qt/conversation_dialog.cpp b/ui/qt/conversation_dialog.cpp index 61966b37aa..1dc5f02b2a 100644 --- a/ui/qt/conversation_dialog.cpp +++ b/ui/qt/conversation_dialog.cpp @@ -22,6 +22,7 @@ #include <ui/qt/models/timeline_delegate.h> #include <ui/qt/models/atap_data_model.h> #include <ui/qt/widgets/traffic_tab.h> +#include <ui/qt/widgets/traffic_types_list.h> #include "main_application.h" #include <QCheckBox> @@ -92,7 +93,9 @@ ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf) : TrafficTableDialog(parent, cf, table_name_), tcp_graph_requested_(false) { - trafficTab()->setProtocolInfo(tr("Conversation"), &(recent.conversation_tabs), &createModel); + trafficList()->setProtocolInfo(table_name_, &(recent.conversation_tabs)); + + trafficTab()->setProtocolInfo(table_name_, trafficList()->protocols(), trafficList()->selectedProtocols(), &createModel); trafficTab()->setDelegate(CONV_COLUMN_START, &createDelegate); trafficTab()->setDelegate(CONV_COLUMN_DURATION, &createDelegate); trafficTab()->setFilter(cf.displayFilter()); @@ -114,13 +117,6 @@ ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf) : absoluteTimeCheckBox()->show(); - addProgressFrame(&parent); - - QPushButton *close_bt = buttonBox()->button(QDialogButtonBox::Close); - if (close_bt) { - close_bt->setDefault(true); - } - updateWidgets(); } diff --git a/ui/qt/endpoint_dialog.cpp b/ui/qt/endpoint_dialog.cpp index 4fe9cf92d7..c1a81235f8 100644 --- a/ui/qt/endpoint_dialog.cpp +++ b/ui/qt/endpoint_dialog.cpp @@ -26,6 +26,7 @@ #include <ui/qt/utils/variant_pointer.h> #include <ui/qt/widgets/wireshark_file_dialog.h> #include <ui/qt/widgets/traffic_tab.h> +#include <ui/qt/widgets/traffic_types_list.h> #include "main_application.h" #include <QCheckBox> @@ -66,7 +67,9 @@ static ATapDataModel * createModel(int protoId, QString filter) EndpointDialog::EndpointDialog(QWidget &parent, CaptureFile &cf) : TrafficTableDialog(parent, cf, table_name_) { - trafficTab()->setProtocolInfo(tr("Endpoints"), &(recent.endpoint_tabs), &createModel); + trafficList()->setProtocolInfo(table_name_, &(recent.endpoint_tabs)); + + trafficTab()->setProtocolInfo(table_name_, trafficList()->protocols(), trafficList()->selectedProtocols(), &createModel); trafficTab()->setFilter(cf.displayFilter()); displayFilterCheckBox()->setChecked(cf.displayFilter().length() > 0); connect(trafficTab(), &TrafficTab::filterAction, this, &EndpointDialog::filterAction); @@ -86,13 +89,6 @@ EndpointDialog::EndpointDialog(QWidget &parent, CaptureFile &cf) : map_bt_->setMenu(map_menu_); #endif - addProgressFrame(&parent); - - QPushButton *close_bt = buttonBox()->button(QDialogButtonBox::Close); - if (close_bt) { - close_bt->setDefault(true); - } - updateWidgets(); } diff --git a/ui/qt/models/atap_data_model.cpp b/ui/qt/models/atap_data_model.cpp index 9b3a0669ff..d57b877bb8 100644 --- a/ui/qt/models/atap_data_model.cpp +++ b/ui/qt/models/atap_data_model.cpp @@ -76,24 +76,20 @@ QString ATapDataModel::tap() const #ifdef HAVE_MAXMINDDB bool ATapDataModel::hasGeoIPData() { - QString key = QString("geoip_found_%1").arg(_protoId); - if (! _lookUp.keys().contains(key)) { - bool coordsFound = false; - int row = 0; - int count = rowCount(QModelIndex()); - while (!coordsFound && row < count) - { - QModelIndex idx = index(row, 0); - if (_type == ATapDataModel::DATAMODEL_ENDPOINT) - coordsFound = qobject_cast<EndpointDataModel *>(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool(); - else if (_type == ATapDataModel::DATAMODEL_CONVERSATION) - coordsFound = qobject_cast<ConversationDataModel *>(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool(); - row++; - } - _lookUp.insert(key, coordsFound); + bool coordsFound = false; + int row = 0; + int count = rowCount(); + while (!coordsFound && row < count) + { + QModelIndex idx = index(row, 0); + if (_type == ATapDataModel::DATAMODEL_ENDPOINT) + coordsFound = qobject_cast<EndpointDataModel *>(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool(); + else if (_type == ATapDataModel::DATAMODEL_CONVERSATION) + coordsFound = qobject_cast<ConversationDataModel *>(this)->data(idx, ATapDataModel::GEODATA_AVAILABLE).toBool(); + row++; } - return _lookUp.value(key, false).toBool(); + return coordsFound; } #endif @@ -190,7 +186,6 @@ void ATapDataModel::resetData() return; beginResetModel(); - _lookUp.clear(); storage_ = nullptr; if (_type == ATapDataModel::DATAMODEL_ENDPOINT) reset_hostlist_table_data(&hash_); @@ -209,7 +204,6 @@ void ATapDataModel::updateData(GArray * newData) return; beginResetModel(); - _lookUp.clear(); storage_ = newData; endResetModel(); @@ -510,15 +504,15 @@ QVariant EndpointDataModel::data(const QModelIndex &idx, int role) const if (column == EndpointDataModel::ENDP_COLUMN_ADDR) return (int)item->myaddress.type; return (int) AT_NONE; - } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_VECTOR) { + } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_LIST) { if (column == EndpointDataModel::ENDP_COLUMN_ADDR) { if (role == ATapDataModel::DATA_IPV4_INTEGER && item->myaddress.type == AT_IPv4) { const ws_in4_addr * ip4 = (const ws_in4_addr *) item->myaddress.data; return (quint32) GUINT32_TO_BE(*ip4); } - else if (role == ATapDataModel::DATA_IPV6_VECTOR && item->myaddress.type == AT_IPv6) { + else if (role == ATapDataModel::DATA_IPV6_LIST && item->myaddress.type == AT_IPv6) { const ws_in6_addr * ip6 = (const ws_in6_addr *) item->myaddress.data; - QVector<quint8> result; + QList<quint8> result; result.reserve(16); std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result)); return QVariant::fromValue(result); @@ -538,7 +532,7 @@ void ConversationDataModel::doDataUpdate() _minRelStartTime = 0; _maxRelStopTime = 0; - for (int row = 0; row < rowCount(QModelIndex()); row ++) { + for (int row = 0; row < rowCount(); row ++) { conv_item_t *conv_item = &g_array_index(storage_, conv_item_t, row); if (row == 0) { @@ -801,16 +795,16 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const return (int)tst_address.type; } return (int) AT_NONE; - } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_VECTOR) { + } else if (role == ATapDataModel::DATA_IPV4_INTEGER || role == ATapDataModel::DATA_IPV6_LIST) { if (column == ConversationDataModel::CONV_COLUMN_SRC_ADDR || column == ConversationDataModel::CONV_COLUMN_DST_ADDR) { address tst_address = column == ConversationDataModel::CONV_COLUMN_SRC_ADDR ? conv_item->src_address : conv_item->dst_address; if (role == ATapDataModel::DATA_IPV4_INTEGER && tst_address.type == AT_IPv4) { const ws_in4_addr * ip4 = (const ws_in4_addr *) tst_address.data; return (quint32) GUINT32_TO_BE(*ip4); } - else if (role == ATapDataModel::DATA_IPV6_VECTOR && tst_address.type == AT_IPv6) { + else if (role == ATapDataModel::DATA_IPV6_LIST && tst_address.type == AT_IPv6) { const ws_in6_addr * ip6 = (const ws_in6_addr *) tst_address.data; - QVector<quint8> result; + QList<quint8> result; result.reserve(16); std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result)); return QVariant::fromValue(result); @@ -823,7 +817,7 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const conv_item_t * ConversationDataModel::itemForRow(int row) { - if (row < 0 || row >= rowCount(QModelIndex())) + if (row < 0 || row >= rowCount()) return nullptr; return (conv_item_t *)&g_array_index(storage_, conv_item_t, row); } diff --git a/ui/qt/models/atap_data_model.h b/ui/qt/models/atap_data_model.h index 2f71646fed..63b92eded7 100644 --- a/ui/qt/models/atap_data_model.h +++ b/ui/qt/models/atap_data_model.h @@ -50,7 +50,7 @@ public: ROW_IS_FILTERED, DATA_ADDRESS_TYPE, DATA_IPV4_INTEGER, - DATA_IPV6_VECTOR, + DATA_IPV6_LIST, }; typedef enum { @@ -232,8 +232,6 @@ protected: private: int _protoId; - QMap<QString, QVariant> _lookUp; - conv_hash_t hash_; }; diff --git a/ui/qt/traffic_table_dialog.cpp b/ui/qt/traffic_table_dialog.cpp index 5f827d109c..ee4e81b639 100644 --- a/ui/qt/traffic_table_dialog.cpp +++ b/ui/qt/traffic_table_dialog.cpp @@ -19,6 +19,7 @@ #include "main_application.h" #include <ui/qt/widgets/traffic_tab.h> +#include <ui/qt/widgets/traffic_types_list.h> #include <QCheckBox> #include <QClipboard> @@ -51,20 +52,28 @@ TrafficTableDialog::TrafficTableDialog(QWidget &parent, CaptureFile &cf, const Q ui->absoluteTimeCheckBox->hide(); setWindowSubtitle(QString("%1s").arg(table_name)); + ui->grpSettings->setTitle(QString("%1 Settings").arg(table_name)); - copy_bt_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + copy_bt_ = buttonBox()->addButton(tr("Copy"), QDialogButtonBox::ActionRole); copy_bt_->setMenu(ui->trafficTab->createCopyMenu(copy_bt_)); ui->trafficTab->setFocus(); - ui->trafficTab->useNanosecondTimestamps(cf.timestampPrecision() == WTAP_TSPREC_NSEC); + connect(ui->trafficList, &TrafficTypesList::protocolsChanged, ui->trafficTab, &TrafficTab::setOpenTabs); + connect(ui->trafficTab, &TrafficTab::tabsChanged, ui->trafficList, &TrafficTypesList::selectProtocols); connect(mainApp, SIGNAL(addressResolutionChanged()), this, SLOT(currentTabChanged())); connect(ui->trafficTab, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged())); connect(&cap_file_, SIGNAL(captureEvent(CaptureEvent)), this, SLOT(captureEvent(CaptureEvent))); - connect(ui->absoluteTimeCheckBox, &QCheckBox::toggled, trafficTab(), &TrafficTab::useAbsoluteTime); - connect(trafficTab(), &TrafficTab::retapRequired, &cap_file_, &CaptureFile::delayedRetapPackets); + connect(ui->absoluteTimeCheckBox, &QCheckBox::toggled, ui->trafficTab, &TrafficTab::useAbsoluteTime); + connect(ui->trafficTab, &TrafficTab::retapRequired, &cap_file_, &CaptureFile::delayedRetapPackets); + + QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close); + if (close_bt) + close_bt->setDefault(true); + + addProgressFrame(&parent); } TrafficTableDialog::~TrafficTableDialog() @@ -79,7 +88,7 @@ void TrafficTableDialog::addProgressFrame(QObject *parent) QDialogButtonBox *TrafficTableDialog::buttonBox() const { - return ui->buttonBox; + return ui->btnBoxSettings; } QCheckBox *TrafficTableDialog::displayFilterCheckBox() const @@ -97,9 +106,15 @@ TrafficTab *TrafficTableDialog::trafficTab() const return ui->trafficTab; } +TrafficTypesList *TrafficTableDialog::trafficList() const +{ + return ui->trafficList; +} + void TrafficTableDialog::currentTabChanged() { bool has_resolution = ui->trafficTab->hasNameResolution(); + copy_bt_->setMenu(ui->trafficTab->createCopyMenu(copy_bt_)); ui->nameResolutionCheckBox->setEnabled(has_resolution); if (! has_resolution) { diff --git a/ui/qt/traffic_table_dialog.h b/ui/qt/traffic_table_dialog.h index 69e288dbeb..a417d59efb 100644 --- a/ui/qt/traffic_table_dialog.h +++ b/ui/qt/traffic_table_dialog.h @@ -31,6 +31,7 @@ class QPushButton; class QTabWidget; class QTreeWidget; class TrafficTab; +class TrafficTypesList; namespace Ui { class TrafficTableDialog; @@ -69,6 +70,7 @@ protected: QCheckBox *displayFilterCheckBox() const; QCheckBox *absoluteTimeCheckBox() const; TrafficTab *trafficTab() const; + TrafficTypesList *trafficList() const; protected slots: virtual void currentTabChanged(); diff --git a/ui/qt/traffic_table_dialog.ui b/ui/qt/traffic_table_dialog.ui index 0e12d11a58..d86ce3e9ed 100644 --- a/ui/qt/traffic_table_dialog.ui +++ b/ui/qt/traffic_table_dialog.ui @@ -10,80 +10,94 @@ <height>475</height> </rect> </property> - <layout class="QVBoxLayout" name="verticalLayout"> + <layout class="QVBoxLayout" name="verticalLayout_3"> <item> - <widget class="TrafficTab" name="trafficTab"/> - </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,1"> - <item> - <widget class="QCheckBox" name="nameResolutionCheckBox"> - <property name="toolTip"> - <string><html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html></string> - </property> - <property name="text"> - <string>Name resolution</string> - </property> - </widget> - </item> + <layout class="QHBoxLayout" name="horizontalLayout"> <item> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> + <widget class="QWidget" name="widget" native="true"> + <property name="maximumSize"> <size> - <width>40</width> - <height>20</height> + <width>210</width> + <height>16777215</height> </size> </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="displayFilterCheckBox"> - <property name="toolTip"> - <string><html><head/><body><p>Only show conversations matching the current display filter</p></body></html></string> - </property> - <property name="text"> - <string>Limit to display filter</string> - </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="grpSettings"> + <property name="title"> + <string>GroupBox</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="nameResolutionCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show resolved addresses and port names rather than plain values. The corresponding name resolution preference must be enabled.</p></body></html></string> + </property> + <property name="text"> + <string>Name resolution</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="absoluteTimeCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show absolute times in the start time column.</p></body></html></string> + </property> + <property name="text"> + <string>Absolute start time</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="displayFilterCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Only show conversations matching the current display filter</p></body></html></string> + </property> + <property name="text"> + <string>Limit to display filter</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="btnBoxSettings"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::NoButton</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="TrafficTypesList" name="trafficList"/> + </item> + </layout> </widget> </item> <item> - <spacer name="horizontalSpacer_3"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QCheckBox" name="absoluteTimeCheckBox"> - <property name="toolTip"> - <string><html><head/><body><p>Show absolute times in the start time column.</p></body></html></string> - </property> - <property name="text"> - <string>Absolute start time</string> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> + <widget class="TrafficTab" name="trafficTab"/> </item> </layout> </item> @@ -106,6 +120,11 @@ <header>ui/qt/widgets/traffic_tab.h</header> <container>1</container> </customwidget> + <customwidget> + <class>TrafficTypesList</class> + <extends>QTreeView</extends> + <header>ui/qt/widgets/traffic_types_list.h</header> + </customwidget> </customwidgets> <resources/> <connections> diff --git a/ui/qt/widgets/traffic_tab.cpp b/ui/qt/widgets/traffic_tab.cpp index 8cfaeb5316..d517e46529 100644 --- a/ui/qt/widgets/traffic_tab.cpp +++ b/ui/qt/widgets/traffic_tab.cpp @@ -16,8 +16,6 @@ #include <wsutil/utf8_entities.h> #include <wsutil/filesystem.h> -#include "ui/recent.h" - #include <ui/qt/main_application.h> #include <ui/qt/filter_action.h> #include <ui/qt/models/atap_data_model.h> @@ -26,7 +24,6 @@ #include <ui/qt/widgets/traffic_tree.h> #include <ui/qt/widgets/detachable_tabwidget.h> -#include <QVector> #include <QStringList> #include <QTreeView> #include <QList> @@ -175,103 +172,26 @@ bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QMod } -static gboolean iterateProtocols(const void *key, void *value, void *userdata) -{ - QMap<int, QString> *protocols = (QMap<int, QString> *)userdata; - register_ct_t* ct = (register_ct_t*)value; - const QString title = (const gchar*)key; - int proto_id = get_conversation_proto_id(ct); - protocols->insert(proto_id, title); - - return FALSE; -} - TrafficTab::TrafficTab(QWidget * parent) : DetachableTabWidget(parent) { _createModel = nullptr; _disableTaps = false; _nameResolution = false; - _recentList = nullptr; setTabBasename(QString()); - } TrafficTab::~TrafficTab() -{ - prefs_clear_string_list(*_recentList); - *_recentList = NULL; - _protocolButtons.clear(); - - foreach (int protoId, _tabs.keys()) - { - char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(protoId))); - *_recentList = g_list_append(*_recentList, title); - } -} +{} -void TrafficTab::setProtocolInfo(QString tableName, GList ** recentList, ATapModelCallback createModel) +void TrafficTab::setProtocolInfo(QString tableName, QList<int> allProtocols, QList<int> openTabs, ATapModelCallback createModel) { setTabBasename(tableName); - _recentList = recentList; + _allProtocols = allProtocols; if (createModel) _createModel = createModel; - for (GList * endTab = *_recentList; endTab; endTab = endTab->next) { - int protoId = proto_get_id_by_short_name((const char *)endTab->data); - if (protoId > -1 && ! _protocols.contains(protoId)) - _protocols.append(protoId); - } - - if (_protocols.isEmpty()) { - QStringList protoNames = QStringList() << "eth" << "ip" << "ipv6" << "tcp" << "udp"; - foreach(QString name, protoNames) - _protocols << proto_get_id_by_filter_name(name.toStdString().c_str()); - } - - QWidget * container = new QWidget(this); - container->setFixedHeight(tabBar()->height()); - container->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); - - QHBoxLayout * layout = new QHBoxLayout(container); - layout->setContentsMargins(1, 0, 1, 0); - - QPushButton * cornerButton = new QPushButton(tr("%1 Types").arg(tableName)); - cornerButton->setFixedHeight(tabBar()->height()); - cornerButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - QMenu * cornerMenu = new QMenu(); - conversation_table_iterate_tables(iterateProtocols, &_allTaps); - foreach (int protoId, _allTaps.keys()) - { - QAction * endPoint = new QAction(_allTaps[protoId], this); - endPoint->setProperty("protocol", QVariant::fromValue(protoId)); - endPoint->setCheckable(true); - endPoint->setChecked(_protocols.contains(protoId)); - connect(endPoint, &QAction::triggered, this, &TrafficTab::toggleTab); - _protocolButtons.insert(protoId, endPoint); - cornerMenu->addAction(endPoint); - } - cornerButton->setMenu(cornerMenu); - - layout->addWidget(cornerButton); - setCornerWidget(container, Qt::TopRightCorner); - - updateTabs(); -} - -void TrafficTab::toggleTab(bool checked) -{ - QAction * orig = qobject_cast<QAction *>(sender()); - if (!orig || ! orig->property("protocol").isValid()) - return; - - int protocol = orig->property("protocol").toInt(); - if (!checked && _protocols.contains(protocol)) - _protocols.removeAll(protocol); - else if (checked && ! _protocols.contains(protocol)) - _protocols.append(protocol); - - updateTabs(); + setOpenTabs(openTabs); } void TrafficTab::setDelegate(int column, ATapCreateDelegate createDelegate) @@ -376,61 +296,79 @@ void TrafficTab::disableTap() emit disablingTaps(); } -void TrafficTab::updateTabs() +void TrafficTab::setOpenTabs(QList<int> protocols) { - QList<int> keys = _tabs.keys(); - QList<int> allProtocols = _allTaps.keys(); - - /* Adding new Tabs, and keeping the same order they are in the drop-down menu */ - foreach (int proto, _protocols) { - if (!keys.contains(proto)) { - - int insertIndex = -1; - auto bIdx = allProtocols.indexOf(proto); - int idx = 0; - while (insertIndex < 0 && idx < keys.count()) - { - auto aIdx = allProtocols.indexOf(keys[idx]); - if (aIdx < 0) /* Key not in all protocols. This would be a fluke */ - break; - if (aIdx > bIdx) /* Should never be equal, as proto is not yet in keys */ - insertIndex = _tabs[keys[idx]]; - idx++; - } + QList<int> tabs = _tabs.keys(); + QList<int> remove; + blockSignals(true); - QTreeView * tree = createTree(proto); - QString tableName = proto_get_protocol_short_name(find_protocol_by_id(proto)); - TabData tabData(tableName, proto); - QVariant storage; - storage.setValue(tabData); - if (tree->model()->rowCount() > 0) - tableName += QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(tree->model()->rowCount()); - - int tabId = insertTab(insertIndex, tree, tableName); - _protocolButtons[proto]->setChecked(true); - tabBar()->setTabData(tabId, storage); + foreach(int protocol, protocols) + { + if (! tabs.contains(protocol)) { + insertProtoTab(protocol, false); } + tabs.removeAll(protocol); } - /* Removing tabs no longer required. First filter the key array, for all tabs which - * are still being displayed */ - foreach(int key, keys) + foreach(int protocol, tabs) + removeProtoTab(protocol, false); + + blockSignals(false); + + emit tabsChanged(_tabs.keys()); + emit retapRequired(); +} + +void TrafficTab::insertProtoTab(int protoId, bool emitSignals) +{ + QList<int> lUsed = _tabs.keys(); + + if (lUsed.contains(protoId) && lUsed.count() != count()) { - if ( _protocols.contains(key)) { - _protocolButtons[key]->setChecked(true); - keys.removeAll(key); + _tabs.clear(); + for (int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast<TabData>(tabBar()->tabData(idx)); + _tabs.insert(tabData.protoId(), idx); } + lUsed = _tabs.keys(); } - /* Removal step 2, now actually remove all elements. Counting down, otherwise removing - * a tab will shift the indeces */ - for(int idx = count(); idx > 0; idx--) { - TabData tabData = qvariant_cast<TabData>(tabBar()->tabData(idx - 1)); - if (keys.contains(tabData.protoId())) { - removeTab(idx - 1); - _protocolButtons[tabData.protoId()]->setChecked(false); + + if (protoId <= 0 || lUsed.contains(protoId)) + return; + + QList<int> lFull = _allProtocols; + int idx = lFull.indexOf(protoId); + if (idx < 0) + return; + + QList<int> part = lFull.mid(0, idx); + int insertAt = 0; + if (part.count() > 0) { + for (int cnt = idx - 1; cnt >= 0; cnt--) { + if (lUsed.contains(part[cnt]) && part[cnt] != protoId) { + insertAt = lUsed.indexOf(part[cnt]) + 1; + break; + } } } + QTreeView * tree = createTree(protoId); + QString tableName = proto_get_protocol_short_name(find_protocol_by_id(protoId)); + TabData tabData(tableName, protoId); + QVariant storage; + storage.setValue(tabData); + if (tree->model()->rowCount() > 0) + tableName += QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(tree->model()->rowCount()); + + int tabId = -1; + if (insertAt > -1) + tabId = insertTab(insertAt, tree, tableName); + else + tabId = addTab(tree, tableName); + if (tabId >= 0) + tabBar()->setTabData(tabId, storage); + + /* We reset the correct tab idxs. That operations is costly, but it is only * called during this operation and ensures, that other operations do not * need to iterate, but rather can lookup the indeces. */ @@ -440,7 +378,37 @@ void TrafficTab::updateTabs() _tabs.insert(tabData.protoId(), idx); } - emit retapRequired(); + if (emitSignals) { + emit tabsChanged(_tabs.keys()); + emit retapRequired(); + } +} + +void TrafficTab::removeProtoTab(int protoId, bool emitSignals) +{ + if (_tabs.keys().contains(protoId)) { + for(int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast<TabData>(tabBar()->tabData(idx)); + if (protoId == tabData.protoId()) { + removeTab(idx); + break; + } + } + } + + /* We reset the correct tab idxs. That operations is costly, but it is only + * called during this operation and ensures, that other operations do not + * need to iterate, but rather can lookup the indeces. */ + _tabs.clear(); + for (int idx = 0; idx < count(); idx++) { + TabData tabData = qvariant_cast<TabData>(tabBar()->tabData(idx)); + _tabs.insert(tabData.protoId(), idx); + } + + if (emitSignals) { + emit tabsChanged(_tabs.keys()); + emit retapRequired(); + } } void TrafficTab::doCurrentIndexChange(const QModelIndex & cur, const QModelIndex &) @@ -484,7 +452,7 @@ void TrafficTab::modelReset() return; TrafficDataFilterProxy * qsfpm = qobject_cast<TrafficDataFilterProxy *>(sender()); - if (! qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) + if (!qsfpm || ! qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) return; ATapDataModel * atdm = qobject_cast<ATapDataModel *>(qsfpm->sourceModel()); @@ -519,7 +487,7 @@ ATapDataModel * TrafficTab::modelForWidget(QWidget * searchWidget) QTreeView * tree = qobject_cast<QTreeView *>(searchWidget); if (qobject_cast<TrafficDataFilterProxy *>(tree->model())) { TrafficDataFilterProxy * qsfpm = qobject_cast<TrafficDataFilterProxy *>(tree->model()); - if (qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) { + if (qsfpm && qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) { return qobject_cast<ATapDataModel *>(qsfpm->sourceModel()); } } @@ -736,9 +704,6 @@ void TrafficTab::detachTab(int tabIdx, QPoint pos) { if (!model) return; - int protocol = model->protoId(); - _protocols.removeAll(protocol); - TrafficTree * tree = qobject_cast<TrafficTree *>(widget(tabIdx)); if (!tree) return; @@ -746,7 +711,7 @@ void TrafficTab::detachTab(int tabIdx, QPoint pos) { connect(this, &TrafficTab::disablingTaps ,tree , &TrafficTree::disableTap); DetachableTabWidget::detachTab(tabIdx, pos); - updateTabs(); + removeProtoTab(model->protoId()); } void TrafficTab::attachTab(QWidget * content, QString name) @@ -757,8 +722,5 @@ void TrafficTab::attachTab(QWidget * content, QString name) return; } - int protocol = model->protoId(); - _protocols.append(protocol); - - updateTabs(); + insertProtoTab(model->protoId()); } diff --git a/ui/qt/widgets/traffic_tab.h b/ui/qt/widgets/traffic_tab.h index 6a47984fec..c58a7ed592 100644 --- a/ui/qt/widgets/traffic_tab.h +++ b/ui/qt/widgets/traffic_tab.h @@ -12,8 +12,6 @@ #include "config.h" -#include <ui/recent.h> - #include <ui/qt/models/atap_data_model.h> #include <ui/qt/filter_action.h> #include <ui/qt/widgets/detachable_tabwidget.h> @@ -99,12 +97,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 recentList The list to store the selected protocols in + * @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 createModel A callback, which will create the correct model for the trees * * @see ATapModelCallback */ - void setProtocolInfo(QString tableName, GList ** recentList, ATapModelCallback createModel); + void setProtocolInfo(QString tableName, QList<int> allProtocols, QList<int> openTabs, ATapModelCallback createModel); /** * @brief Set the Delegate object for a specific column @@ -212,11 +211,14 @@ public slots: */ void useAbsoluteTime(bool absolute); + void setOpenTabs(QList<int> protocols); + signals: void filterAction(QString filter, FilterAction::Action action, FilterAction::ActionType type); void tabDataChanged(int idx); void retapRequired(); void disablingTaps(); + void tabsChanged(QList<int> protocols); protected slots: @@ -224,29 +226,26 @@ protected slots: virtual void attachTab(QWidget * content, QString name) override; private: - QVector<int> _protocols; - QMap<int, QString> _allTaps; - QMap<int, QAction *> _protocolButtons; + QList<int> _allProtocols; QMap<int, int> _tabs; - GList ** _recentList; ATapModelCallback _createModel; QMap<int, ATapCreateDelegate> _createDelegates; bool _disableTaps; bool _nameResolution; - void updateTabs(); QTreeView * createTree(int protoId); ATapDataModel * modelForTabIndex(int tabIdx = -1); ATapDataModel * modelForWidget(QWidget * widget); + void insertProtoTab(int protoId, bool emitSignals = true); + void removeProtoTab(int protoId, bool emitSignals = true); + #ifdef HAVE_MAXMINDDB bool writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataModel); #endif private slots: - void toggleTab(bool checked = false); - void modelReset(); void doCurrentIndexChange(const QModelIndex & cur, const QModelIndex & prev); diff --git a/ui/qt/widgets/traffic_tree.cpp b/ui/qt/widgets/traffic_tree.cpp index 31a1ab2bc6..3e370da83c 100644 --- a/ui/qt/widgets/traffic_tree.cpp +++ b/ui/qt/widgets/traffic_tree.cpp @@ -27,7 +27,6 @@ #include <ui/qt/utils/variant_pointer.h> #include <ui/qt/widgets/traffic_tree.h> -#include <QVector> #include <QStringList> #include <QTreeView> #include <QList> @@ -53,7 +52,7 @@ TrafficTree::TrafficTree(QString baseName, QWidget *parent) : setRootIsDecorated(false); setSortingEnabled(true); setContextMenuPolicy(Qt::CustomContextMenu); - + connect(this, &QTreeView::customContextMenuRequested, this, &TrafficTree::customContextMenu); } diff --git a/ui/qt/widgets/traffic_types_list.cpp b/ui/qt/widgets/traffic_types_list.cpp new file mode 100644 index 0000000000..031f8c0295 --- /dev/null +++ b/ui/qt/widgets/traffic_types_list.cpp @@ -0,0 +1,236 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <glib.h> + +#include <epan/conversation_table.h> + +#include <ui/qt/widgets/traffic_types_list.h> + +#include <QStringList> + +TrafficTypesRowData::TrafficTypesRowData(int protocol, QString name) : + _protocol(protocol), + _name(name), + _checked(false) +{} + +int TrafficTypesRowData::protocol() const +{ + return _protocol; +} + +QString TrafficTypesRowData::name() const +{ + return _name; +} + +bool TrafficTypesRowData::checked() const +{ + return _checked; +} + +void TrafficTypesRowData::setChecked(bool checked) +{ + _checked = checked; +} + +static gboolean iterateProtocols(const void *key, void *value, void *userdata) +{ + QList<TrafficTypesRowData> * protocols = (QList<TrafficTypesRowData> *)userdata; + + register_ct_t* ct = (register_ct_t*)value; + const QString title = (const gchar*)key; + int proto_id = get_conversation_proto_id(ct); + TrafficTypesRowData entry(proto_id, title); + protocols->append(entry); + + return FALSE; +} + +TrafficTypesModel::TrafficTypesModel(GList ** recentList, QObject *parent) : + QAbstractListModel(parent), + _recentList(recentList) +{ + conversation_table_iterate_tables(iterateProtocols, &_allTaps); + + QList<int> _protocols; + + for (GList * endTab = *_recentList; endTab; endTab = endTab->next) { + int protoId = proto_get_id_by_short_name((const char *)endTab->data); + if (protoId > -1 && ! _protocols.contains(protoId)) + _protocols.append(protoId); + } + + if (_protocols.isEmpty()) { + QStringList protoNames = QStringList() << "eth" << "ip" << "ipv6" << "tcp" << "udp"; + foreach(QString name, protoNames) + _protocols << proto_get_id_by_filter_name(name.toStdString().c_str()); + } + + for(int cnt = 0; cnt < _allTaps.count(); cnt++) + { + _allTaps[cnt].setChecked(false); + if (_protocols.contains(_allTaps[cnt].protocol())) + _allTaps[cnt].setChecked(true); + } + +} + +int TrafficTypesModel::rowCount(const QModelIndex &) const +{ + return _allTaps.count(); +} + +int TrafficTypesModel::columnCount(const QModelIndex &) const +{ + return TrafficTypesModel::COL_NUM; +} + +QVariant TrafficTypesModel::data(const QModelIndex &idx, int role) const +{ + if (!idx.isValid()) + return QVariant(); + + TrafficTypesRowData data = _allTaps[idx.row()]; + if (role == Qt::DisplayRole) + { + switch(idx.column()) + { + case(TrafficTypesModel::COL_NAME): + return data.name(); + case(TrafficTypesModel::COL_PROTOCOL): + return data.protocol(); + } + } else if (role == Qt::CheckStateRole && idx.column() == TrafficTypesModel::COL_CHECKED) { + return data.checked() ? Qt::Checked : Qt::Unchecked; + } else if (role == TrafficTypesModel::TRAFFIC_PROTOCOL) { + return data.protocol(); + } else if (role == TrafficTypesModel::TRAFFIC_IS_CHECKED) { + return (bool)data.checked(); + } + + return QVariant(); +} + +QVariant TrafficTypesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (section < 0 || role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + if (section == TrafficTypesModel::COL_NAME) + return tr("Protocol"); + return QVariant(); +} + +Qt::ItemFlags TrafficTypesModel::flags (const QModelIndex & idx) const +{ + Qt::ItemFlags defaultFlags = QAbstractListModel::flags(idx); + if (idx.isValid()) + return defaultFlags | Qt::ItemIsUserCheckable; + + return defaultFlags; +} + +bool TrafficTypesModel::setData(const QModelIndex &idx, const QVariant &value, int role) +{ + if(!idx.isValid() || role != Qt::CheckStateRole) + return false; + + if (_allTaps.count() <= idx.row()) + return false; + + _allTaps[idx.row()].setChecked(value == Qt::Checked); + + QList<int> selected; + prefs_clear_string_list(*_recentList); + *_recentList = NULL; + + for (int cnt = 0; cnt < _allTaps.count(); cnt++) { + if (_allTaps[cnt].checked()) { + int protoId = _allTaps[cnt].protocol(); + selected.append(protoId); + char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(protoId))); + *_recentList = g_list_append(*_recentList, title); + } + } + + emit protocolsChanged(selected); + + emit dataChanged(idx, idx); + return true; +} + +void TrafficTypesModel::selectProtocols(QList<int> protocols) +{ + beginResetModel(); + for (int cnt = 0; cnt < _allTaps.count(); cnt++) { + _allTaps[cnt].setChecked(false); + if (protocols.contains(_allTaps[cnt].protocol())) + _allTaps[cnt].setChecked(true); + } + endResetModel(); +} + +TrafficTypesList::TrafficTypesList(QWidget *parent) : + QTreeView(parent) +{ + _name = QString(); + _model = nullptr; + + setAlternatingRowColors(true); + setRootIsDecorated(false); +} + +void TrafficTypesList::setProtocolInfo(QString name, GList ** recentList) +{ + _name = name; + + _model = new TrafficTypesModel(recentList); + setModel(_model); + + connect(_model, &TrafficTypesModel::protocolsChanged, this, &TrafficTypesList::protocolsChanged); + + resizeColumnToContents(0); + resizeColumnToContents(1); +} + +void TrafficTypesList::selectProtocols(QList<int> protocols) +{ + if (_model) + _model->selectProtocols(protocols); +} + +QList<int> TrafficTypesList::protocols() const +{ + QList<int> entries; + for (int cnt = 0; cnt < _model->rowCount(); cnt++) { + QModelIndex idx = _model->index(cnt, TrafficTypesModel::COL_CHECKED); + int protoId = _model->data(idx, TrafficTypesModel::TRAFFIC_PROTOCOL).toInt(); + if (protoId > 0 && ! entries.contains(protoId)) + entries.append(protoId); + } + + return entries; +} + +QList<int> TrafficTypesList::selectedProtocols() const +{ + QList<int> entries; + for (int cnt = 0; cnt < _model->rowCount(); cnt++) { + QModelIndex idx = _model->index(cnt, TrafficTypesModel::COL_CHECKED); + int protoId = _model->data(idx, TrafficTypesModel::TRAFFIC_PROTOCOL).toInt(); + if (protoId > 0 && ! entries.contains(protoId) && _model->data(idx, TrafficTypesModel::TRAFFIC_IS_CHECKED).toBool()) + entries.append(protoId); + } + + return entries; +} diff --git a/ui/qt/widgets/traffic_types_list.h b/ui/qt/widgets/traffic_types_list.h new file mode 100644 index 0000000000..fece2ef86a --- /dev/null +++ b/ui/qt/widgets/traffic_types_list.h @@ -0,0 +1,100 @@ +/** @file + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TRAFFIC_TYPES_LIST_H +#define TRAFFIC_TYPES_LIST_H + +#include "config.h" + +#include <QTreeView> +#include <QAbstractListModel> +#include <QMap> +#include <QString> + +class TrafficTypesRowData +{ + +public: + TrafficTypesRowData(int protocol, QString name); + + int protocol() const; + QString name() const; + bool checked() const; + void setChecked(bool checked); + +private: + int _protocol; + QString _name; + bool _checked; +}; + +class TrafficTypesModel : public QAbstractListModel +{ + Q_OBJECT +public: + + enum { + TRAFFIC_PROTOCOL = Qt::UserRole, + TRAFFIC_IS_CHECKED, + } eTrafficUserData; + + enum { + COL_CHECKED, + COL_NAME, + COL_NUM, + COL_PROTOCOL, + } eTrafficColumnNames; + + TrafficTypesModel(GList ** recentList, QObject *parent = nullptr); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + virtual bool setData(const QModelIndex &idx, const QVariant &value, int role) override; + virtual Qt::ItemFlags flags (const QModelIndex & idx) const override; + + QList<int> protocols() const; + +public slots: + void selectProtocols(QList<int> protocols); + +signals: + void protocolsChanged(QList<int> protocols); + +private: + QList<TrafficTypesRowData> _allTaps; + GList ** _recentList; + +}; + +class TrafficTypesList : public QTreeView +{ + Q_OBJECT +public: + + TrafficTypesList(QWidget *parent = nullptr); + + void setProtocolInfo(QString name, GList ** recentList); + QList<int> protocols() const; + QList<int> selectedProtocols() const; + +public slots: + void selectProtocols(QList<int> protocols); + +signals: + void protocolsChanged(QList<int> protocols); + +private: + QString _name; + TrafficTypesModel * _model; +}; + +#endif // TRAFFIC_TYPES_LIST_H
\ No newline at end of file |