summaryrefslogtreecommitdiffstats
path: root/ui/qt/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'ui/qt/widgets')
-rw-r--r--ui/qt/widgets/additional_toolbar.cpp24
-rw-r--r--ui/qt/widgets/byte_view_text.cpp5
-rw-r--r--ui/qt/widgets/capture_filter_combo.cpp2
-rw-r--r--ui/qt/widgets/capture_filter_edit.cpp4
-rw-r--r--ui/qt/widgets/compression_group_box.cpp69
-rw-r--r--ui/qt/widgets/compression_group_box.h41
-rw-r--r--ui/qt/widgets/copy_from_profile_button.h1
-rw-r--r--ui/qt/widgets/display_filter_combo.cpp89
-rw-r--r--ui/qt/widgets/display_filter_combo.h7
-rw-r--r--ui/qt/widgets/display_filter_edit.cpp73
-rw-r--r--ui/qt/widgets/dissector_syntax_line_edit.cpp15
-rw-r--r--ui/qt/widgets/dissector_syntax_line_edit.h7
-rw-r--r--ui/qt/widgets/field_filter_edit.cpp6
-rw-r--r--ui/qt/widgets/filter_expression_toolbar.cpp34
-rw-r--r--ui/qt/widgets/filter_expression_toolbar.h2
-rw-r--r--ui/qt/widgets/follow_stream_text.cpp118
-rw-r--r--ui/qt/widgets/follow_stream_text.h18
-rw-r--r--ui/qt/widgets/label_stack.cpp4
-rw-r--r--ui/qt/widgets/label_stack.h3
-rw-r--r--ui/qt/widgets/path_selection_edit.cpp12
-rw-r--r--ui/qt/widgets/profile_tree_view.cpp63
-rw-r--r--ui/qt/widgets/profile_tree_view.h13
-rw-r--r--ui/qt/widgets/qcp_axis_ticker_elided.cpp74
-rw-r--r--ui/qt/widgets/qcp_axis_ticker_elided.h38
-rw-r--r--ui/qt/widgets/qcp_axis_ticker_si.cpp74
-rw-r--r--ui/qt/widgets/qcp_axis_ticker_si.h42
-rw-r--r--ui/qt/widgets/qcp_string_legend_item.cpp46
-rw-r--r--ui/qt/widgets/qcp_string_legend_item.h35
-rw-r--r--ui/qt/widgets/resize_header_view.cpp45
-rw-r--r--ui/qt/widgets/resize_header_view.h31
-rw-r--r--ui/qt/widgets/resolved_addresses_view.cpp261
-rw-r--r--ui/qt/widgets/resolved_addresses_view.h50
-rw-r--r--ui/qt/widgets/rowmove_tree_view.cpp98
-rw-r--r--ui/qt/widgets/rowmove_tree_view.h35
-rw-r--r--ui/qt/widgets/rtp_audio_graph.cpp2
-rw-r--r--ui/qt/widgets/splash_overlay.h2
-rw-r--r--ui/qt/widgets/syntax_line_edit.cpp30
-rw-r--r--ui/qt/widgets/traffic_tab.cpp77
-rw-r--r--ui/qt/widgets/traffic_tab.h14
-rw-r--r--ui/qt/widgets/traffic_tree.cpp56
-rw-r--r--ui/qt/widgets/traffic_tree.h2
-rw-r--r--ui/qt/widgets/traffic_types_list.cpp42
-rw-r--r--ui/qt/widgets/traffic_types_list.h4
-rw-r--r--ui/qt/widgets/wireless_timeline.cpp78
-rw-r--r--ui/qt/widgets/wireless_timeline.h14
-rw-r--r--ui/qt/widgets/wireshark_file_dialog.cpp15
-rw-r--r--ui/qt/widgets/wireshark_file_dialog.h9
47 files changed, 1513 insertions, 271 deletions
diff --git a/ui/qt/widgets/additional_toolbar.cpp b/ui/qt/widgets/additional_toolbar.cpp
index c023d149..43eab273 100644
--- a/ui/qt/widgets/additional_toolbar.cpp
+++ b/ui/qt/widgets/additional_toolbar.cpp
@@ -9,8 +9,6 @@
#include <config.h>
-#include <glib.h>
-
#include <ui/qt/widgets/additional_toolbar.h>
#include <ui/qt/widgets/apply_line_edit.h>
#include <ui/qt/utils/qt_ui_utils.h>
@@ -159,7 +157,7 @@ QWidget * AdditionalToolbarWidgetAction::createWidget(QWidget * parent)
}
static void
-toolbar_button_cb(gpointer item, gpointer item_data, gpointer user_data)
+toolbar_button_cb(void *item, void *item_data, void *user_data)
{
if (! item || ! item_data || ! user_data)
return;
@@ -170,7 +168,7 @@ toolbar_button_cb(gpointer item, gpointer item_data, gpointer user_data)
if (widget)
{
if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE)
- widget->setText((gchar *)update_entry->user_data);
+ widget->setText((char *)update_entry->user_data);
else if (update_entry->type == EXT_TOOLBAR_SET_ACTIVE)
{
bool enableState = GPOINTER_TO_INT(update_entry->user_data) == 1;
@@ -195,7 +193,7 @@ QWidget * AdditionalToolbarWidgetAction::createButton(ext_toolbar_t * item, QWid
}
static void
-toolbar_boolean_cb(gpointer item, gpointer item_data, gpointer user_data)
+toolbar_boolean_cb(void *item, void *item_data, void *user_data)
{
if (! item || ! item_data || ! user_data)
return;
@@ -267,7 +265,7 @@ QWidget * AdditionalToolbarWidgetAction::createLabelFrame(ext_toolbar_t * item,
}
static void
-toolbar_string_cb(gpointer item, gpointer item_data, gpointer user_data)
+toolbar_string_cb(void *item, void *item_data, void *user_data)
{
if (! item || ! item_data || ! user_data)
return;
@@ -282,7 +280,7 @@ toolbar_string_cb(gpointer item, gpointer item_data, gpointer user_data)
if (update_entry->silent)
oldState = edit->blockSignals(true);
- edit->setText((gchar *)update_entry->user_data);
+ edit->setText((char *)update_entry->user_data);
if (update_entry->silent)
edit->blockSignals(oldState);
@@ -321,7 +319,7 @@ QWidget * AdditionalToolbarWidgetAction::createTextEditor(ext_toolbar_t * item,
}
static void
-toolbar_selector_cb(gpointer item, gpointer item_data, gpointer user_data)
+toolbar_selector_cb(void *item, void *item_data, void *user_data)
{
if (! item || ! item_data || ! user_data)
return;
@@ -346,7 +344,7 @@ toolbar_selector_cb(gpointer item, gpointer item_data, gpointer user_data)
if (update_entry->type == EXT_TOOLBAR_UPDATE_VALUE)
{
- QString data = QString((gchar *)update_entry->user_data);
+ QString data = QString((char *)update_entry->user_data);
for (int i = 0; i < sourceModel->rowCount(); i++)
{
@@ -385,8 +383,8 @@ toolbar_selector_cb(gpointer item, gpointer item_data, gpointer user_data)
if (! update_entry->data_index)
return;
- gchar * idx = (gchar *)update_entry->data_index;
- gchar * display = (gchar *)update_entry->user_data;
+ char * idx = (char *)update_entry->data_index;
+ char * display = (char *)update_entry->user_data;
if (update_entry->type == EXT_TOOLBAR_UPDATE_DATABYINDEX)
{
@@ -522,7 +520,7 @@ void AdditionalToolbarWidgetAction::onCheckBoxChecked(int checkState)
if (! item)
return;
- gboolean value = checkState == Qt::Checked ? true : false;
+ bool value = checkState == Qt::Checked ? true : false;
item->callback(item, &value, item->user_data);
}
@@ -539,7 +537,7 @@ void AdditionalToolbarWidgetAction::sendTextToCallback()
ApplyLineEdit * editor = dynamic_cast<ApplyLineEdit *>(sender());
if (! editor)
{
- /* Called from button, searching for acompanying line edit */
+ /* Called from button, searching for accompanying line edit */
QWidget * parent = dynamic_cast<QWidget *>(sender()->parent());
if (parent)
{
diff --git a/ui/qt/widgets/byte_view_text.cpp b/ui/qt/widgets/byte_view_text.cpp
index e81dc0ac..ee1e0195 100644
--- a/ui/qt/widgets/byte_view_text.cpp
+++ b/ui/qt/widgets/byte_view_text.cpp
@@ -70,6 +70,7 @@ ByteViewText::ByteViewText(const QByteArray &data, packet_char_enc encoding, QWi
offset_normal_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35);
offset_field_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.65);
+ ctx_menu_.setToolTipsVisible(true);
window()->winId(); // Required for screenChanged? https://phabricator.kde.org/D20171
connect(window()->windowHandle(), &QWindow::screenChanged, viewport(), [=](const QScreen *) { viewport()->update(); });
@@ -407,11 +408,7 @@ void ByteViewText::updateLayoutMetrics()
int ByteViewText::stringWidth(const QString &line)
{
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
return viewport()->fontMetrics().horizontalAdvance(line);
-#else
- return viewport()->fontMetrics().boundingRect(line).width();
-#endif
}
// Draw a line of byte view text for a given offset.
diff --git a/ui/qt/widgets/capture_filter_combo.cpp b/ui/qt/widgets/capture_filter_combo.cpp
index 70635cef..95766310 100644
--- a/ui/qt/widgets/capture_filter_combo.cpp
+++ b/ui/qt/widgets/capture_filter_combo.cpp
@@ -126,7 +126,7 @@ void CaptureFilterCombo::rebuildFilterList()
QString cur_filter = currentText();
clear();
for (GList *li = g_list_first(cfilter_list); li != NULL; li = gxx_list_next(li)) {
- addItem(gxx_list_data(const gchar *, li));
+ addItem(gxx_list_data(const char *, li));
}
lineEdit()->setText(cur_filter);
lineEdit()->blockSignals(false);
diff --git a/ui/qt/widgets/capture_filter_edit.cpp b/ui/qt/widgets/capture_filter_edit.cpp
index 3c72fe41..50793143 100644
--- a/ui/qt/widgets/capture_filter_edit.cpp
+++ b/ui/qt/widgets/capture_filter_edit.cpp
@@ -9,8 +9,6 @@
#include "config.h"
-#include <glib.h>
-
#include <epan/proto.h>
#include "capture_opts.h"
@@ -308,7 +306,7 @@ QPair<const QString, bool> CaptureFilterEdit::getSelectedFilter()
#ifdef HAVE_LIBPCAP
int selected_devices = 0;
- for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) {
+ for (unsigned i = 0; i < global_capture_opts.all_ifaces->len; i++) {
interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, i);
if (device->selected) {
selected_devices++;
diff --git a/ui/qt/widgets/compression_group_box.cpp b/ui/qt/widgets/compression_group_box.cpp
new file mode 100644
index 00000000..08a74dae
--- /dev/null
+++ b/ui/qt/widgets/compression_group_box.cpp
@@ -0,0 +1,69 @@
+/* @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "compression_group_box.h"
+
+#include <QRadioButton>
+#include <QButtonGroup>
+#include <QVBoxLayout>
+
+CompressionGroupBox::CompressionGroupBox(QWidget *parent) :
+ QGroupBox(parent)
+{
+ setTitle(tr("Compression options"));
+ setFlat(true);
+
+
+ bg_ = new QButtonGroup(this);
+ QVBoxLayout *vbox = new QVBoxLayout();
+
+ QRadioButton *radio1 = new QRadioButton(tr("&Uncompressed"));
+ bg_->addButton(radio1, WTAP_UNCOMPRESSED);
+ vbox->addWidget(radio1);
+
+#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
+ QRadioButton *radio2 = new QRadioButton(tr("Compress with g&zip"));
+ bg_->addButton(radio2, WTAP_GZIP_COMPRESSED);
+ vbox->addWidget(radio2);
+#endif
+#ifdef HAVE_LZ4FRAME_H
+ QRadioButton *radio3 = new QRadioButton(tr("Compress with &LZ4"));
+ bg_->addButton(radio3, WTAP_LZ4_COMPRESSED);
+ vbox->addWidget(radio3);
+#endif
+
+ radio1->setChecked(true);
+
+ setLayout(vbox);
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ connect(bg_, &QButtonGroup::idToggled, [=] { emit stateChanged(); });
+#else
+ connect(bg_, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), [=] { emit stateChanged(); });
+#endif
+
+}
+
+CompressionGroupBox::~CompressionGroupBox()
+{
+}
+
+wtap_compression_type CompressionGroupBox::compressionType() const
+{
+ return static_cast<wtap_compression_type>(bg_->checkedId());
+}
+
+void CompressionGroupBox::setCompressionType(wtap_compression_type type)
+{
+ QAbstractButton *button = bg_->button(type);
+ if (button != nullptr) {
+ button->setChecked(true);
+ }
+}
+
diff --git a/ui/qt/widgets/compression_group_box.h b/ui/qt/widgets/compression_group_box.h
new file mode 100644
index 00000000..4e70ca2d
--- /dev/null
+++ b/ui/qt/widgets/compression_group_box.h
@@ -0,0 +1,41 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef COMPRESSION_GROUP_BOX_H
+#define COMPRESSION_GROUP_BOX_H
+
+#include <config.h>
+
+#include <QGroupBox>
+
+#include <wiretap/wtap.h>
+
+class QButtonGroup;
+
+/**
+ * UI element for selecting compression type from among those supported.
+ */
+class CompressionGroupBox : public QGroupBox
+{
+ Q_OBJECT
+
+public:
+ explicit CompressionGroupBox(QWidget *parent = 0);
+ ~CompressionGroupBox();
+ wtap_compression_type compressionType() const;
+ void setCompressionType(wtap_compression_type type);
+
+signals:
+ void stateChanged();
+
+private:
+ QButtonGroup *bg_;
+};
+
+#endif // COMPRESSION_GROUP_BOX_H
diff --git a/ui/qt/widgets/copy_from_profile_button.h b/ui/qt/widgets/copy_from_profile_button.h
index f074bdb8..ea6df816 100644
--- a/ui/qt/widgets/copy_from_profile_button.h
+++ b/ui/qt/widgets/copy_from_profile_button.h
@@ -11,7 +11,6 @@
#define COPY_FROM_PROFILE_BUTTON_H
#include <config.h>
-#include <glib.h>
#include <QMenu>
#include <QPushButton>
diff --git a/ui/qt/widgets/display_filter_combo.cpp b/ui/qt/widgets/display_filter_combo.cpp
index c28afb91..674da765 100644
--- a/ui/qt/widgets/display_filter_combo.cpp
+++ b/ui/qt/widgets/display_filter_combo.cpp
@@ -25,8 +25,31 @@
#include <ui/qt/utils/color_utils.h>
#include "main_application.h"
-// If we ever add support for multiple windows this will need to be replaced.
-static DisplayFilterCombo *cur_display_filter_combo = NULL;
+static QStandardItemModel *cur_model;
+
+extern "C" void dfilter_recent_combo_write_all(FILE *rf) {
+ if (cur_model == nullptr)
+ return;
+
+ for (int i = 0; i < cur_model->rowCount(); i++ ) {
+ const QByteArray& filter = cur_model->item(i)->text().toUtf8();
+ if (!filter.isEmpty()) {
+ fprintf(rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", filter.constData());
+ }
+ }
+}
+
+extern "C" bool dfilter_combo_add_recent(const char *filter) {
+ if (cur_model == nullptr) {
+ cur_model = new QStandardItemModel();
+ cur_model->setSortRole(Qt::UserRole);
+ }
+
+ QStandardItem *new_item = new QStandardItem(filter);
+ new_item->setData(QVariant(QDateTime::currentMSecsSinceEpoch()), Qt::UserRole);
+ cur_model->appendRow(new_item);
+ return true;
+}
DisplayFilterCombo::DisplayFilterCombo(QWidget *parent) :
QComboBox(parent)
@@ -46,36 +69,51 @@ DisplayFilterCombo::DisplayFilterCombo(QWidget *parent) :
// Default is Preferred.
setSizePolicy(QSizePolicy::MinimumExpanding, sizePolicy().verticalPolicy());
setAccessibleName(tr("Display filter selector"));
- cur_display_filter_combo = this;
updateStyleSheet();
setToolTip(tr("Select from previously used filters."));
- QStandardItemModel *model = qobject_cast<QStandardItemModel*>(this->model());
- model->setSortRole(Qt::UserRole);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ // Setting the placeholderText keeps newly added items from being the
+ // current item. It only works for the placeholderText of the QComboBox,
+ // not the lineEdit (even though the lineEdit's placeholderText is shown
+ // instead.) This only matters for any combobox created before the recent
+ // display filter list is read (i.e., the main window one.)
+ setPlaceholderText(lineEdit()->placeholderText());
+#endif
+
+ if (cur_model == nullptr) {
+ cur_model = new QStandardItemModel();
+ cur_model->setSortRole(Qt::UserRole);
+ }
+ setModel(cur_model);
connect(mainApp, &MainApplication::preferencesChanged, this, &DisplayFilterCombo::updateMaxCount);
// Ugly cast required (?)
// https://stackoverflow.com/questions/16794695/connecting-overloaded-signals-and-slots-in-qt-5
connect(this, static_cast<void (DisplayFilterCombo::*)(int)>(&DisplayFilterCombo::activated), this, &DisplayFilterCombo::onActivated);
-}
-
-extern "C" void dfilter_recent_combo_write_all(FILE *rf) {
- if (!cur_display_filter_combo)
- return;
- cur_display_filter_combo->writeRecent(rf);
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ connect(cur_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &DisplayFilterCombo::rowsAboutToBeInserted);
+ connect(cur_model, &QAbstractItemModel::rowsInserted, this, &DisplayFilterCombo::rowsInserted);
+#endif
}
-void DisplayFilterCombo::writeRecent(FILE *rf) {
- int i;
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+void DisplayFilterCombo::rowsAboutToBeInserted(const QModelIndex&, int, int)
+{
+ // If the current text is blank but we're inserting a row, that means
+ // it is being added programmatically from the model, and we want to
+ // clear it afterwards and show the placeholder text instead.
+ clear_state_ = (currentText() == QString());
+}
- for (i = 0; i < count(); i++) {
- const QByteArray& filter = itemText(i).toUtf8();
- if (!filter.isEmpty()) {
- fprintf(rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", filter.constData());
- }
+void DisplayFilterCombo::rowsInserted(const QModelIndex&, int, int)
+{
+ if (clear_state_) {
+ clearEditText();
}
}
+#endif
void DisplayFilterCombo::onActivated(int row)
{
@@ -172,18 +210,3 @@ void DisplayFilterCombo::updateMaxCount()
{
setMaxCount(prefs.gui_recent_df_entries_max);
}
-
-extern "C" gboolean dfilter_combo_add_recent(const gchar *filter) {
- if (!cur_display_filter_combo)
- return FALSE;
-
- // Adding an item to a QComboBox also sets its lineEdit. In our case
- // that means we might trigger a temporary status message so we block
- // the lineEdit's signals.
- // Another approach would be to update QComboBox->model directly.
- bool block_state = cur_display_filter_combo->lineEdit()->blockSignals(true);
- cur_display_filter_combo->addItem(filter, QVariant(QDateTime::currentMSecsSinceEpoch()));
- cur_display_filter_combo->clearEditText();
- cur_display_filter_combo->lineEdit()->blockSignals(block_state);
- return TRUE;
-}
diff --git a/ui/qt/widgets/display_filter_combo.h b/ui/qt/widgets/display_filter_combo.h
index 81fa11ba..d53bfca4 100644
--- a/ui/qt/widgets/display_filter_combo.h
+++ b/ui/qt/widgets/display_filter_combo.h
@@ -23,9 +23,16 @@ public:
void updateStyleSheet();
protected:
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ void rowsAboutToBeInserted(const QModelIndex&, int, int);
+ void rowsInserted(const QModelIndex&, int, int);
+#endif
virtual bool event(QEvent *event);
private:
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ bool clear_state_;
+#endif
public slots:
bool checkDisplayFilter();
diff --git a/ui/qt/widgets/display_filter_edit.cpp b/ui/qt/widgets/display_filter_edit.cpp
index 839b14fd..09113da7 100644
--- a/ui/qt/widgets/display_filter_edit.cpp
+++ b/ui/qt/widgets/display_filter_edit.cpp
@@ -9,9 +9,8 @@
#include "config.h"
-#include <glib.h>
-
#include <epan/dfilter/dfilter.h>
+#include <epan/dfilter/dfunctions.h>
#include <ui/recent.h>
@@ -58,8 +57,16 @@
#define DEFAULT_MODIFIER "Ctrl-"
#endif
-// proto.c:fld_abbrev_chars
-static const QString fld_abbrev_chars_ = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+// ':' is not a legal field character, but it appears inside literals and
+// having it as a token character will keep field completion from being
+// offered in a place where it is syntactically impossible.
+//
+// The other place ':' is used in the grammar is to separate display filter
+// macros from their argument lists in the ${macro:arg;arg} format. Adding
+// ':' here means that the first argument of the list won't have a completion
+// pop-up. (We don't do completion for the macro names, maybe we should?)
+// ${macro;arg;arg} is allowed now, though.
+static const QString fld_abbrev_chars_ = ":-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
DisplayFilterEdit::DisplayFilterEdit(QWidget *parent, DisplayFilterEditType type) :
SyntaxLineEdit(parent),
@@ -121,6 +128,8 @@ DisplayFilterEdit::DisplayFilterEdit(QWidget *parent, DisplayFilterEditType type
connect(this, &DisplayFilterEdit::returnPressed, this, &DisplayFilterEdit::applyDisplayFilter);
}
+ setDefaultPlaceholderText();
+
connect(this, &DisplayFilterEdit::textChanged, this,
static_cast<void (DisplayFilterEdit::*)(const QString &)>(&DisplayFilterEdit::checkFilter));
@@ -578,16 +587,24 @@ void DisplayFilterEdit::buildCompletionList(const QString &field_word, const QSt
void *field_cookie;
const QByteArray fw_ba = field_word.toUtf8(); // or toLatin1 or toStdString?
const char *fw_utf8 = fw_ba.constData();
- gsize fw_len = (gsize) strlen(fw_utf8);
+ size_t fw_len = (size_t) strlen(fw_utf8);
for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo; hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) {
if (hfinfo->same_name_prev_id != -1) continue; // Ignore duplicate names.
if (!g_ascii_strncasecmp(fw_utf8, hfinfo->abbrev, fw_len)) {
- if ((gsize) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev;
+ if ((size_t) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev;
}
}
}
}
+
+ // Add display filter functions to the completion list
+ GPtrArray *func_list = df_func_name_list();
+ for (unsigned i = 0; i < func_list->len; i++) {
+ field_list << QString::fromUtf8(static_cast<const char *>(func_list->pdata[i])).append("(");
+ }
+ g_ptr_array_unref(func_list);
+
field_list.sort();
}
@@ -614,19 +631,42 @@ void DisplayFilterEdit::clearFilter()
void DisplayFilterEdit::applyDisplayFilter()
{
if (completer()->popup()->currentIndex().isValid()) {
- // If the popup (not the QCompleter itself) has a currently
- // valid QModelIndex, that means that the popup's
- // QAbstractItemView::activated() signal has not yet
- // been handled, which means that text() has the old value,
- // not the one from the completer.
- return;
+ // If the popup (not the QCompleter itself) has a currently valid
+ // QModelIndex, check to see if text() matches the text from the popup.
+ // If it does, then all is well, go ahead and filter (this happens
+ // if the popup entry is selected via mouse.)
+ //
+ // If it doesn't match, then it has the old value. There are two
+ // possibilities:
+ // 1) The user clicked away from the popup *without* selecting
+ // anything (making the popup disappear), and then hit Enter, in
+ // which case the user wants to filter with text() and doesn't care
+ // about what's in the popup. However, the QModelIndex for the popup
+ // is still valid until some time after this signal is handled.
+ //
+ // 2) The user pressed Return on an entry in the popup, in which
+ // case the user wants to filter with the new value in the popup,
+ // not the value in text(), but for some reason the popup's
+ // activated() signal gets handled *after* returnPressed on the
+ // LineEdit, unfortunately (#19323).
+ //
+ // We haven't figured out how to distinguish case 1 from case 2 yet,
+ // so ignore this force the user to press Enter again, and which
+ // point everything will have reconciled.
+ //
+ // Note that the currentCompletion() / currentIndex.data() of
+ // the completer() itself is "what would be the first completion
+ // of the text currently displayed in the line edit" and has naught
+ // to do with what was selected in the popup.
+ if (text() != completer()->popup()->currentIndex().data()) {
+ return;
+ }
}
if (syntaxState() == Invalid)
return;
- if (text().length() > 0)
- last_applied_ = text();
+ last_applied_ = text();
updateClearButton();
@@ -712,9 +752,7 @@ void DisplayFilterEdit::applyOrPrepareFilter()
if (! pa || pa->property("display_filter").toString().isEmpty())
return;
- QString filterText = pa->property("display_filter").toString();
- last_applied_ = filterText;
- setText(filterText);
+ setText(pa->property("display_filter").toString());
// Holding down the Shift key will only prepare filter.
if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
@@ -802,7 +840,6 @@ void DisplayFilterEdit::dropEvent(QDropEvent *event)
return;
}
- last_applied_ = filterText;
setText(filterText);
// Holding down the Shift key will only prepare filter.
diff --git a/ui/qt/widgets/dissector_syntax_line_edit.cpp b/ui/qt/widgets/dissector_syntax_line_edit.cpp
index ae8a2247..4f228ce4 100644
--- a/ui/qt/widgets/dissector_syntax_line_edit.cpp
+++ b/ui/qt/widgets/dissector_syntax_line_edit.cpp
@@ -9,8 +9,6 @@
#include "config.h"
-#include <glib.h>
-
#include <epan/packet.h>
#include <wsutil/utf8_entities.h>
@@ -38,6 +36,15 @@ DissectorSyntaxLineEdit::DissectorSyntaxLineEdit(QWidget *parent) :
setCompleter(new QCompleter(completion_model_, this));
setCompletionTokenChars(fld_abbrev_chars_);
+ updateDissectorNames();
+ setDefaultPlaceholderText();
+
+ connect(this, &DissectorSyntaxLineEdit::textChanged, this,
+ static_cast<void (DissectorSyntaxLineEdit::*)(const QString &)>(&DissectorSyntaxLineEdit::checkDissectorName));
+}
+
+void DissectorSyntaxLineEdit::updateDissectorNames()
+{
GList *dissector_names = get_dissector_names();
QStringList dissector_list;
for (GList* l = dissector_names; l != NULL; l = l->next) {
@@ -46,10 +53,6 @@ DissectorSyntaxLineEdit::DissectorSyntaxLineEdit(QWidget *parent) :
g_list_free(dissector_names);
dissector_list.sort();
completion_model_->setStringList(dissector_list);
- setDefaultPlaceholderText();
-
- connect(this, &DissectorSyntaxLineEdit::textChanged, this,
- static_cast<void (DissectorSyntaxLineEdit::*)(const QString &)>(&DissectorSyntaxLineEdit::checkDissectorName));
}
void DissectorSyntaxLineEdit::setDefaultPlaceholderText()
diff --git a/ui/qt/widgets/dissector_syntax_line_edit.h b/ui/qt/widgets/dissector_syntax_line_edit.h
index adcee8c7..a99442b7 100644
--- a/ui/qt/widgets/dissector_syntax_line_edit.h
+++ b/ui/qt/widgets/dissector_syntax_line_edit.h
@@ -20,19 +20,22 @@ class DissectorSyntaxLineEdit : public SyntaxLineEdit
Q_OBJECT
public:
explicit DissectorSyntaxLineEdit(QWidget *parent = 0);
+ void updateDissectorNames();
+ void setDefaultPlaceholderText();
protected:
void keyPressEvent(QKeyEvent *event) { completionKeyPressEvent(event); }
void focusInEvent(QFocusEvent *event) { completionFocusInEvent(event); }
-private slots:
+public slots:
void checkDissectorName(const QString &dissector);
+
+private slots:
void changeEvent(QEvent* event);
private:
QString placeholder_text_;
- void setDefaultPlaceholderText();
void buildCompletionList(const QString &field_word, const QString &preamble);
};
diff --git a/ui/qt/widgets/field_filter_edit.cpp b/ui/qt/widgets/field_filter_edit.cpp
index 7aebf051..b2452c30 100644
--- a/ui/qt/widgets/field_filter_edit.cpp
+++ b/ui/qt/widgets/field_filter_edit.cpp
@@ -9,8 +9,6 @@
#include "config.h"
-#include <glib.h>
-
#include <epan/dfilter/dfilter.h>
#include <wsutil/filter_files.h>
@@ -160,12 +158,12 @@ void FieldFilterEdit::buildCompletionList(const QString &field_word, const QStri
void *field_cookie;
const QByteArray fw_ba = field_word.toUtf8(); // or toLatin1 or toStdString?
const char *fw_utf8 = fw_ba.constData();
- gsize fw_len = (gsize) strlen(fw_utf8);
+ size_t fw_len = (size_t) strlen(fw_utf8);
for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo; hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) {
if (hfinfo->same_name_prev_id != -1) continue; // Ignore duplicate names.
if (!g_ascii_strncasecmp(fw_utf8, hfinfo->abbrev, fw_len)) {
- if ((gsize) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev;
+ if ((size_t) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev;
}
}
}
diff --git a/ui/qt/widgets/filter_expression_toolbar.cpp b/ui/qt/widgets/filter_expression_toolbar.cpp
index 84dabc47..c17f7aee 100644
--- a/ui/qt/widgets/filter_expression_toolbar.cpp
+++ b/ui/qt/widgets/filter_expression_toolbar.cpp
@@ -181,7 +181,7 @@ WiresharkMimeData * FilterExpressionToolBar::createMimeData(QString name, int po
void FilterExpressionToolBar::onActionMoved(QAction* action, int oldPos, int newPos)
{
- gchar* err = NULL;
+ char* err = NULL;
if (oldPos == newPos)
return;
@@ -237,7 +237,7 @@ void FilterExpressionToolBar::onFilterDropped(QString description, QString filte
return;
filter_expression_new(qUtf8Printable(description),
- qUtf8Printable(filter), qUtf8Printable(description), TRUE);
+ qUtf8Printable(filter), qUtf8Printable(description), true);
save_migrated_uat("Display expressions", &prefs.filter_expressions_old);
filterExpressionsChanged();
@@ -356,16 +356,14 @@ QMenu * FilterExpressionToolBar::findParentMenu(const QStringList tree, void *fe
/* Searching existing main menus */
foreach(QAction * entry, data->toolbar->actions())
{
- QWidget * widget = data->toolbar->widgetForAction(entry);
- QToolButton * tb = qobject_cast<QToolButton *>(widget);
- if (tb && tb->menu() && tb->text().compare(tree.at(0).trimmed()) == 0)
- return findParentMenu(tree.mid(1), fed_data, tb->menu());
+ if (entry->text().compare(tree.at(0).trimmed()) == 0)
+ return findParentMenu(tree.mid(1), fed_data, entry->menu());
}
}
else if (parent)
{
QString menuName = tree.at(0).trimmed();
- /* Iterate to see, if we next have to jump into another submenu */
+ /* Iterate to see if we next have to jump into another submenu */
foreach(QAction *entry, parent->actions())
{
if (entry->menu() && entry->text().compare(menuName) == 0)
@@ -382,16 +380,18 @@ QMenu * FilterExpressionToolBar::findParentMenu(const QStringList tree, void *fe
/* No menu has been found, create one */
QString parentName = tree.at(0).trimmed();
- QToolButton * menuButton = new QToolButton();
- menuButton->setText(parentName);
- menuButton->setPopupMode(QToolButton::MenuButtonPopup);
- QMenu * parentMenu = new QMenu(menuButton);
+ QMenu * parentMenu = new QMenu(data->toolbar);
parentMenu->installEventFilter(data->toolbar);
parentMenu->setProperty(dfe_menu_, QVariant::fromValue(true));
- menuButton->setMenu(parentMenu);
- // Required for QToolButton::MenuButtonPopup.
- connect(menuButton, &QToolButton::pressed, menuButton, &QToolButton::showMenu);
- data->toolbar->addWidget(menuButton);
+ QAction *menuAction = new QAction(data->toolbar);
+ menuAction->setText(parentName);
+ menuAction->setMenu(parentMenu);
+ // QToolButton::MenuButtonPopup means that pressing the button text
+ // itself doesn't open the menu, only pressing the downwards pointing
+ // triangle does. This is difficult to change for the auto created
+ // QToolButton inside the QToolBar. But only auto created tool buttons
+ // will show up in the extension menu at narrow widths (#19887.)
+ data->toolbar->addAction(menuAction);
return findParentMenu(tree.mid(1), fed_data, parentMenu);
}
@@ -407,7 +407,7 @@ bool FilterExpressionToolBar::filter_expression_add_action(const void *key _U_,
struct filter_expression_data* data = (filter_expression_data*)user_data;
if (!fe->enabled)
- return FALSE;
+ return false;
QString label = QString(fe->label);
@@ -449,7 +449,7 @@ bool FilterExpressionToolBar::filter_expression_add_action(const void *key _U_,
connect(dfb_action, &QAction::triggered, data->toolbar, &FilterExpressionToolBar::filterClicked);
data->actions_added = true;
- return FALSE;
+ return false;
}
void FilterExpressionToolBar::filterClicked()
diff --git a/ui/qt/widgets/filter_expression_toolbar.h b/ui/qt/widgets/filter_expression_toolbar.h
index 80724a27..174d927b 100644
--- a/ui/qt/widgets/filter_expression_toolbar.h
+++ b/ui/qt/widgets/filter_expression_toolbar.h
@@ -9,8 +9,6 @@
#include <ui/qt/widgets/drag_drop_toolbar.h>
-#include <glib.h>
-
#include <QMenu>
#ifndef FILTER_EXPRESSION_TOOLBAR_H
diff --git a/ui/qt/widgets/follow_stream_text.cpp b/ui/qt/widgets/follow_stream_text.cpp
index a8e0f2b9..b2492c0b 100644
--- a/ui/qt/widgets/follow_stream_text.cpp
+++ b/ui/qt/widgets/follow_stream_text.cpp
@@ -9,10 +9,16 @@
#include <ui/qt/widgets/follow_stream_text.h>
+#include "epan/prefs.h"
+
+#include <ui/qt/utils/color_utils.h>
+
#include <main_application.h>
+#include <QMap>
#include <QMouseEvent>
#include <QTextCursor>
+#include <QScrollBar>
// To do:
// - Draw text by hand similar to ByteViewText. This would let us add
@@ -20,17 +26,96 @@
// max_document_length_ in FollowStreamDialog.
FollowStreamText::FollowStreamText(QWidget *parent) :
- QPlainTextEdit(parent)
+ QPlainTextEdit(parent), truncated_(false)
{
setMouseTracking(true);
// setMaximumBlockCount(1);
QTextDocument *text_doc = document();
text_doc->setDefaultFont(mainApp->monospaceFont());
+
+ metainfo_fg_ = ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35);
+}
+
+const int FollowStreamText::max_document_length_ = 500 * 1000 * 1000; // Just a guess
+
+void FollowStreamText::addTruncated(int cur_pos)
+{
+ if (truncated_) {
+ QTextCharFormat tcf = currentCharFormat();
+ tcf.setBackground(palette().base().color());
+ tcf.setForeground(metainfo_fg_);
+ insertPlainText("\n" + tr("[Stream output truncated]"));
+ moveCursor(QTextCursor::End);
+ } else {
+ verticalScrollBar()->setValue(cur_pos);
+ }
+}
+
+void FollowStreamText::addText(QString text, bool is_from_server, uint32_t packet_num, bool colorize)
+{
+ if (truncated_) {
+ return;
+ }
+
+ int char_count = document()->characterCount();
+ if (char_count + text.length() > max_document_length_) {
+ text.truncate(max_document_length_ - char_count);
+ truncated_ = true;
+ }
+
+ setUpdatesEnabled(false);
+ int cur_pos = verticalScrollBar()->value();
+ moveCursor(QTextCursor::End);
+
+ QTextCharFormat tcf = currentCharFormat();
+ if (!colorize) {
+ tcf.setBackground(palette().base().color());
+ tcf.setForeground(palette().text().color());
+ } else if (is_from_server) {
+ tcf.setForeground(ColorUtils::fromColorT(prefs.st_server_fg));
+ tcf.setBackground(ColorUtils::fromColorT(prefs.st_server_bg));
+ } else {
+ tcf.setForeground(ColorUtils::fromColorT(prefs.st_client_fg));
+ tcf.setBackground(ColorUtils::fromColorT(prefs.st_client_bg));
+ }
+ setCurrentCharFormat(tcf);
+
+ insertPlainText(text);
+ text_pos_to_packet_[textCursor().anchor()] = packet_num;
+
+ addTruncated(cur_pos);
+ setUpdatesEnabled(true);
+}
+
+void FollowStreamText::addDeltaTime(double delta)
+{
+ QString delta_str = QString("\n%1s").arg(QString::number(delta, 'f', 6));
+ if (truncated_) {
+ return;
+ }
+
+ if (document()->characterCount() + delta_str.length() > max_document_length_) {
+ truncated_ = true;
+ }
+
+ setUpdatesEnabled(false);
+ int cur_pos = verticalScrollBar()->value();
+ moveCursor(QTextCursor::End);
+
+ QTextCharFormat tcf = currentCharFormat();
+ tcf.setBackground(palette().base().color());
+ tcf.setForeground(metainfo_fg_);
+ setCurrentCharFormat(tcf);
+
+ insertPlainText(delta_str);
+
+ addTruncated(cur_pos);
+ setUpdatesEnabled(true);
}
void FollowStreamText::mouseMoveEvent(QMouseEvent *event)
{
- emit mouseMovedToTextCursorPosition(cursorForPosition(event->pos()).position());
+ emit mouseMovedToPacket(textPosToPacket(cursorForPosition(event->pos()).position()));
// Don't send the mouseMoveEvents with no buttons pushed to the base
// class, effectively turning off mouse tracking for the base class.
// It causes a lot of useless calculations that hurt scroll performance.
@@ -41,12 +126,37 @@ void FollowStreamText::mouseMoveEvent(QMouseEvent *event)
void FollowStreamText::mousePressEvent(QMouseEvent *event)
{
- emit mouseClickedOnTextCursorPosition(cursorForPosition(event->pos()).position());
+ emit mouseClickedOnPacket(textPosToPacket(cursorForPosition(event->pos()).position()));
QPlainTextEdit::mousePressEvent(event);
}
void FollowStreamText::leaveEvent(QEvent *event)
{
- emit mouseMovedToTextCursorPosition(-1);
+ emit mouseMovedToPacket(0);
QPlainTextEdit::leaveEvent(event);
}
+
+void FollowStreamText::clear()
+{
+ truncated_ = false;
+ text_pos_to_packet_.clear();
+ QPlainTextEdit::clear();
+}
+
+int FollowStreamText::currentPacket() const
+{
+ return textPosToPacket(textCursor().position());
+}
+
+int FollowStreamText::textPosToPacket(int text_pos) const
+{
+ int pkt = 0;
+ if (text_pos >= 0) {
+ QMap<int, uint32_t>::const_iterator it = text_pos_to_packet_.upperBound(text_pos);
+ if (it != text_pos_to_packet_.end()) {
+ pkt = it.value();
+ }
+ }
+
+ return pkt;
+}
diff --git a/ui/qt/widgets/follow_stream_text.h b/ui/qt/widgets/follow_stream_text.h
index 0dd8dca5..0276c872 100644
--- a/ui/qt/widgets/follow_stream_text.h
+++ b/ui/qt/widgets/follow_stream_text.h
@@ -17,6 +17,10 @@ class FollowStreamText : public QPlainTextEdit
Q_OBJECT
public:
explicit FollowStreamText(QWidget *parent = 0);
+ bool isTruncated() const { return truncated_; }
+ void addText(QString text, bool is_from_server, uint32_t packet_num, bool colorize);
+ void addDeltaTime(double delta);
+ int currentPacket() const;
protected:
void mouseMoveEvent(QMouseEvent *event);
@@ -24,12 +28,20 @@ protected:
void leaveEvent(QEvent *event);
signals:
- // Perhaps this is not descriptive enough. We should add more words.
- void mouseMovedToTextCursorPosition(int);
- void mouseClickedOnTextCursorPosition(int);
+ void mouseMovedToPacket(int);
+ void mouseClickedOnPacket(int);
public slots:
+ void clear();
+private:
+ int textPosToPacket(int text_pos) const;
+ void addTruncated(int cur_pos);
+
+ static const int max_document_length_;
+ bool truncated_;
+ QMap<int, uint32_t> text_pos_to_packet_;
+ QColor metainfo_fg_;
};
#endif // FOLLOW_STREAM_TEXT_H
diff --git a/ui/qt/widgets/label_stack.cpp b/ui/qt/widgets/label_stack.cpp
index 4cac15b7..8deedaec 100644
--- a/ui/qt/widgets/label_stack.cpp
+++ b/ui/qt/widgets/label_stack.cpp
@@ -68,9 +68,10 @@ void LabelStack::fillLabel() {
setStyleSheet(style_sheet);
}
setText(si.text);
+ setToolTip(si.tooltip);
}
-void LabelStack::pushText(const QString &text, int ctx) {
+void LabelStack::pushText(const QString &text, int ctx, const QString &tooltip) {
popText(ctx);
if (ctx == temporary_ctx_) {
@@ -83,6 +84,7 @@ void LabelStack::pushText(const QString &text, int ctx) {
StackItem si;
si.text = text;
+ si.tooltip = tooltip;
si.ctx = ctx;
labels_.prepend(si);
fillLabel();
diff --git a/ui/qt/widgets/label_stack.h b/ui/qt/widgets/label_stack.h
index 63657b74..27e41be1 100644
--- a/ui/qt/widgets/label_stack.h
+++ b/ui/qt/widgets/label_stack.h
@@ -21,7 +21,7 @@ class LabelStack : public QLabel
public:
explicit LabelStack(QWidget *parent = 0);
void setTemporaryContext(const int ctx);
- void pushText(const QString &text, int ctx);
+ void pushText(const QString &text, int ctx, const QString &tooltip = QString());
void setShrinkable(bool shrinkable = true);
protected:
@@ -35,6 +35,7 @@ protected:
private:
typedef struct _StackItem {
QString text;
+ QString tooltip;
int ctx;
} StackItem;
diff --git a/ui/qt/widgets/path_selection_edit.cpp b/ui/qt/widgets/path_selection_edit.cpp
index eb30f1d0..660a6f53 100644
--- a/ui/qt/widgets/path_selection_edit.cpp
+++ b/ui/qt/widgets/path_selection_edit.cpp
@@ -10,18 +10,18 @@
#include "config.h"
-#include "epan/prefs.h"
#include "ui/util.h"
#include <ui/qt/widgets/path_selection_edit.h>
#include "ui/qt/widgets/wireshark_file_dialog.h"
+#include "ui/qt/utils/qt_ui_utils.h"
#include <QHBoxLayout>
#include <QToolButton>
#include <QWidget>
#include <QLineEdit>
-PathSelectionEdit::PathSelectionEdit(QString title, QString path, bool selectFile, QWidget *parent) :
+PathSelectionEdit::PathSelectionEdit(QString title, QString path, bool selectFile, QWidget *parent) :
QWidget(parent)
{
_title = title;
@@ -49,7 +49,7 @@ PathSelectionEdit::PathSelectionEdit(QString title, QString path, bool selectFil
setFocusPolicy(_edit->focusPolicy());
}
-PathSelectionEdit::PathSelectionEdit(QWidget *parent) :
+PathSelectionEdit::PathSelectionEdit(QWidget *parent) :
PathSelectionEdit(tr("Select a path"), QString(), true, parent)
{}
@@ -75,11 +75,7 @@ void PathSelectionEdit::browseForPath()
QString openDir = _path;
if (openDir.isEmpty()) {
- if (prefs.gui_fileopen_style == FO_STYLE_LAST_OPENED) {
- openDir = QString(get_open_dialog_initial_dir());
- } else if (prefs.gui_fileopen_style == FO_STYLE_SPECIFIED) {
- openDir = QString(prefs.gui_fileopen_dir);
- }
+ openDir = openDialogInitialDir();
}
QString newPath;
diff --git a/ui/qt/widgets/profile_tree_view.cpp b/ui/qt/widgets/profile_tree_view.cpp
index afa86d71..ebac67c7 100644
--- a/ui/qt/widgets/profile_tree_view.cpp
+++ b/ui/qt/widgets/profile_tree_view.cpp
@@ -7,31 +7,28 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
-#include <ui/qt/models/url_link_delegate.h>
#include <ui/qt/models/profile_model.h>
#include <ui/qt/utils/qt_ui_utils.h>
+#include <ui/qt/widgets/display_filter_edit.h>
#include <ui/qt/widgets/profile_tree_view.h>
#include <QDesktopServices>
#include <QDir>
+#include <QHeaderView>
#include <QItemDelegate>
#include <QLineEdit>
-#include <QUrl>
-ProfileUrlLinkDelegate::ProfileUrlLinkDelegate(QObject *parent) : UrlLinkDelegate (parent) {}
+ProfileTreeEditDelegate::ProfileTreeEditDelegate(QWidget *parent) : QItemDelegate(parent), editor_(Q_NULLPTR) {}
-void ProfileUrlLinkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+QWidget *ProfileTreeEditDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
{
- /* Only paint links for valid paths */
- if (index.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool())
- UrlLinkDelegate::paint(painter, option, index);
- else
- QStyledItemDelegate::paint(painter, option, index);
-
+ if (index.column() == ProfileModel::COL_AUTO_SWITCH_FILTER) {
+ return new DisplayFilterEdit(parent);
+ }
+ return QItemDelegate::createEditor(parent, option, index);
}
-ProfileTreeEditDelegate::ProfileTreeEditDelegate(QWidget *parent) : QItemDelegate(parent), editor_(Q_NULLPTR) {}
-
void ProfileTreeEditDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
if (qobject_cast<QLineEdit *>(editor))
@@ -46,8 +43,8 @@ ProfileTreeView::ProfileTreeView(QWidget *parent) :
{
delegate_ = new ProfileTreeEditDelegate();
setItemDelegateForColumn(ProfileModel::COL_NAME, delegate_);
+ setItemDelegateForColumn(ProfileModel::COL_AUTO_SWITCH_FILTER, delegate_);
- connect(this, &QAbstractItemView::clicked, this, &ProfileTreeView::clicked);
connect(delegate_, &ProfileTreeEditDelegate::commitData, this, &ProfileTreeView::itemUpdated);
}
@@ -80,19 +77,6 @@ void ProfileTreeView::selectionChanged(const QItemSelection &selected, const QIt
}
}
-void ProfileTreeView::clicked(const QModelIndex &index)
-{
- if (!index.isValid())
- return;
-
- /* Only paint links for valid paths */
- if (index.data(ProfileModel::DATA_INDEX_VALUE_IS_URL).toBool())
- {
- QString path = QDir::toNativeSeparators(index.data().toString());
- QDesktopServices::openUrl(QUrl::fromLocalFile(path));
- }
-}
-
void ProfileTreeView::selectRow(int row)
{
if (row < 0)
@@ -117,3 +101,30 @@ bool ProfileTreeView::activeEdit()
{
return (state() == QAbstractItemView::EditingState);
}
+
+// If our auto switch filters are shorter than the filter column title,
+// stretch the name column.
+void ProfileTreeView::showEvent(QShowEvent *)
+{
+ bool have_wide_filter = false;
+ int auto_switch_title_width = fontMetrics().horizontalAdvance(model()->headerData(ProfileModel::COL_AUTO_SWITCH_FILTER, Qt::Horizontal).toString());
+ for (int row = 0; row < model()->rowCount(); row++) {
+ QString filter = model()->data(model()->index(row, ProfileModel::COL_AUTO_SWITCH_FILTER)).toString();
+ if (fontMetrics().horizontalAdvance(filter) > auto_switch_title_width) {
+ have_wide_filter = true;
+ break;
+ }
+ }
+
+ if (have_wide_filter) {
+ return;
+ }
+
+ int col_name_size_hint = sizeHintForColumn(ProfileModel::COL_NAME);
+ int col_asf_size_hint = qMax(header()->sectionSizeHint(ProfileModel::COL_AUTO_SWITCH_FILTER), sizeHintForColumn(ProfileModel::COL_AUTO_SWITCH_FILTER));
+ int extra = columnWidth(ProfileModel::COL_AUTO_SWITCH_FILTER) - col_asf_size_hint;
+ if (extra > 0) {
+ setColumnWidth(ProfileModel::COL_NAME, col_name_size_hint + extra);
+ }
+
+}
diff --git a/ui/qt/widgets/profile_tree_view.h b/ui/qt/widgets/profile_tree_view.h
index 9684811e..02492e4b 100644
--- a/ui/qt/widgets/profile_tree_view.h
+++ b/ui/qt/widgets/profile_tree_view.h
@@ -15,16 +15,6 @@
#include <QTreeView>
#include <QItemDelegate>
-class ProfileUrlLinkDelegate : public UrlLinkDelegate
-{
- Q_OBJECT
-
-public:
- explicit ProfileUrlLinkDelegate(QObject *parent = Q_NULLPTR);
-
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
-};
-
class ProfileTreeEditDelegate : public QItemDelegate
{
Q_OBJECT
@@ -32,6 +22,7 @@ public:
ProfileTreeEditDelegate(QWidget *parent = Q_NULLPTR);
// QAbstractItemDelegate interface
+ QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
private:
@@ -54,12 +45,12 @@ signals:
// QWidget interface
protected:
+ virtual void showEvent(QShowEvent *);
virtual void mouseDoubleClickEvent(QMouseEvent *event);
// QAbstractItemView interface
protected slots:
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
- virtual void clicked(const QModelIndex &index);
private:
ProfileTreeEditDelegate *delegate_;
diff --git a/ui/qt/widgets/qcp_axis_ticker_elided.cpp b/ui/qt/widgets/qcp_axis_ticker_elided.cpp
new file mode 100644
index 00000000..154a750a
--- /dev/null
+++ b/ui/qt/widgets/qcp_axis_ticker_elided.cpp
@@ -0,0 +1,74 @@
+/** @file
+ *
+ * QCustomPlot QCPAxisTickerText subclass that elides labels to the
+ * width of the parent QCPAxis's QCPAxisRect's margin for the appropriate
+ * side, for use when the margin is fixed.
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/widgets/qcp_axis_ticker_elided.h>
+
+#include <QFontMetrics>
+
+QCPAxisTickerElided::QCPAxisTickerElided(QCPAxis *parent) :
+ mParent(parent)
+{
+}
+
+QCP::MarginSide QCPAxisTickerElided::axisTypeToMarginSide(const QCPAxis::AxisType axis)
+{
+ switch (axis) {
+ case QCPAxis::atLeft:
+ return QCP::msLeft;
+ case QCPAxis::atRight:
+ return QCP::msRight;
+ case QCPAxis::atTop:
+ return QCP::msTop;
+ case QCPAxis::atBottom:
+ return QCP::msBottom;
+ }
+ return QCP::msNone;
+}
+
+QString QCPAxisTickerElided::elidedText(const QString& text)
+{
+ QCP::MarginSides autoMargins = mParent->axisRect()->autoMargins();
+ if (autoMargins & axisTypeToMarginSide(mParent->axisType())) {
+ return text;
+ }
+ int elide_w;
+ QMargins margins = mParent->axisRect()->margins();
+ switch (mParent->axisType()) {
+ case QCPAxis::atLeft:
+ elide_w = margins.left();
+ break;
+ case QCPAxis::atRight:
+ elide_w = margins.right();
+ break;
+ case QCPAxis::atTop:
+ elide_w = margins.top();
+ break;
+ case QCPAxis::atBottom:
+ elide_w = margins.bottom();
+ break;
+ default:
+ // ??
+ elide_w = margins.left();
+ }
+
+ return QFontMetrics(mParent->tickLabelFont()).elidedText(text,
+ Qt::ElideRight,
+ elide_w);
+}
+
+QString QCPAxisTickerElided::getTickLabel(double tick, const QLocale& , QChar , int)
+{
+ return elidedText(mTicks.value(tick));
+}
diff --git a/ui/qt/widgets/qcp_axis_ticker_elided.h b/ui/qt/widgets/qcp_axis_ticker_elided.h
new file mode 100644
index 00000000..b542d9a9
--- /dev/null
+++ b/ui/qt/widgets/qcp_axis_ticker_elided.h
@@ -0,0 +1,38 @@
+/** @file
+ *
+ * QCustomPlot QCPAxisTickerText subclass that elides labels to the
+ * width of the parent QCPAxis's QCPAxisRect's margin for the appropriate
+ * side, for use when the margin is fixed.
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QCP_AXIS_TICKER_ELIDED_H
+#define QCP_AXIS_TICKER_ELIDED_H
+
+#include <ui/qt/widgets/qcustomplot.h>
+
+class QCPAxisTickerElided : public QCPAxisTickerText
+{
+public:
+ explicit QCPAxisTickerElided(QCPAxis *parent);
+
+ // QCP has marginSideToAxisType but not the inverse
+ static QCP::MarginSide axisTypeToMarginSide(const QCPAxis::AxisType);
+
+ QString elidedText(const QString& text);
+
+protected:
+ virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) override;
+
+private:
+ QCPAxis *mParent;
+};
+
+#endif
diff --git a/ui/qt/widgets/qcp_axis_ticker_si.cpp b/ui/qt/widgets/qcp_axis_ticker_si.cpp
new file mode 100644
index 00000000..be6d35a6
--- /dev/null
+++ b/ui/qt/widgets/qcp_axis_ticker_si.cpp
@@ -0,0 +1,74 @@
+/** @file
+ *
+ * QCustomPlot QCPAxisTicker subclass that creates human-readable
+ * SI unit labels, optionally supporting log scale.
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <cmath>
+
+#include <ui/qt/widgets/qcp_axis_ticker_si.h>
+#include <ui/qt/utils/qt_ui_utils.h>
+
+#include <wsutil/str_util.h>
+
+QCPAxisTickerSi::QCPAxisTickerSi(format_size_units_e unit, QString customUnit, bool log) :
+ mUnit(unit), mCustomUnit(customUnit), mLog(log)
+{
+}
+
+QString QCPAxisTickerSi::getTickLabel(double tick, const QLocale& , QChar , int precision)
+{
+ QString label = gchar_free_to_qstring(format_units(nullptr, tick, mUnit, FORMAT_SIZE_PREFIX_SI, precision));
+
+ // XXX - format_units isn't consistent about whether we need to
+ // add a space or not
+ if (mUnit == FORMAT_SIZE_UNIT_NONE && !mCustomUnit.isEmpty()) {
+ label += mCustomUnit;
+ }
+ // XXX - "Beautiful typeset powers" for exponentials is handled by QCPAxis,
+ // not QCPAxisTicker and its subclasses, and its detection of exponentials
+ // doesn't handle having a unit or other suffix, so that won't work.
+ // In practical use we'll be within our prefix range, though.
+ return label;
+}
+
+int QCPAxisTickerSi::getSubTickCount(double tickStep)
+{
+ if (mLog) {
+ return QCPAxisTickerLog::getSubTickCount(tickStep);
+ } else {
+ return QCPAxisTicker::getSubTickCount(tickStep);
+ }
+}
+
+QVector<double> QCPAxisTickerSi::createTickVector(double tickStep, const QCPRange &range)
+{
+ if (mLog) {
+ return QCPAxisTickerLog::createTickVector(tickStep, range);
+ } else {
+ return QCPAxisTicker::createTickVector(tickStep, range);
+ }
+}
+
+void QCPAxisTickerSi::setUnit(format_size_units_e unit)
+{
+ mUnit = unit;
+}
+
+void QCPAxisTickerSi::setCustomUnit(QString unit)
+{
+ mCustomUnit = unit;
+}
+
+void QCPAxisTickerSi::setLog(bool log)
+{
+ mLog = log;
+}
diff --git a/ui/qt/widgets/qcp_axis_ticker_si.h b/ui/qt/widgets/qcp_axis_ticker_si.h
new file mode 100644
index 00000000..a9fa51cc
--- /dev/null
+++ b/ui/qt/widgets/qcp_axis_ticker_si.h
@@ -0,0 +1,42 @@
+/** @file
+ *
+ * QCustomPlot QCPAxisTicker subclass that creates human-readable
+ * SI unit labels, optionally supporting log scale.
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef QCP_AXIS_TICKER_SI_H
+#define QCP_AXIS_TICKER_SI_H
+
+#include <ui/qt/widgets/qcustomplot.h>
+
+#include <wsutil/str_util.h>
+
+class QCPAxisTickerSi : public QCPAxisTickerLog
+{
+public:
+ explicit QCPAxisTickerSi(format_size_units_e unit = FORMAT_SIZE_UNIT_PACKETS, QString customUnit = QString(), bool log = false);
+
+ format_size_units_e getUnit() const { return mUnit; }
+ void setUnit(format_size_units_e unit);
+ void setCustomUnit(QString unit);
+ void setLog(bool log);
+
+protected:
+ virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) override;
+ virtual int getSubTickCount(double tickStep) override;
+ virtual QVector<double> createTickVector(double tickStep, const QCPRange &range) override;
+
+ format_size_units_e mUnit;
+ QString mCustomUnit;
+ bool mLog;
+};
+
+#endif
diff --git a/ui/qt/widgets/qcp_string_legend_item.cpp b/ui/qt/widgets/qcp_string_legend_item.cpp
new file mode 100644
index 00000000..62596895
--- /dev/null
+++ b/ui/qt/widgets/qcp_string_legend_item.cpp
@@ -0,0 +1,46 @@
+/** @file
+ *
+ * QCustomPlot QCPAbstractLegendItem subclass containing a string.
+ * This is used to add a title to a QCPLegend.
+ *
+ * This file is from https://www.qcustomplot.com/index.php/support/forum/443
+ * where the author David said "I thought I would share in case any one else
+ * is needing the same functionality." Accordingly, this file is in the
+ * public domain.
+ */
+
+#include <ui/qt/widgets/qcp_string_legend_item.h>
+
+QCPStringLegendItem::QCPStringLegendItem(QCPLegend *pParent, const QString& strText)
+ : QCPAbstractLegendItem(pParent)
+ , m_strText(strText)
+{
+}
+
+QString QCPStringLegendItem::text() const
+{
+ return m_strText;
+}
+
+void QCPStringLegendItem::setText(const QString& strText)
+{
+ m_strText = strText;
+}
+
+void QCPStringLegendItem::draw(QCPPainter *pPainter)
+{
+ pPainter->setFont(mFont);
+ pPainter->setPen(QPen(mTextColor));
+ QRectF textRect = pPainter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, m_strText);
+ pPainter->drawText(mRect.x() + mMargins.left(), mRect.y(), textRect.width(), textRect.height(), Qt::TextDontClip | Qt::AlignHCenter, m_strText);
+}
+
+QSize QCPStringLegendItem::minimumOuterSizeHint() const
+{
+ QSize cSize(0, 0);
+ QFontMetrics fontMetrics(mFont);
+ QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, m_strText);
+ cSize.setWidth(textRect.width() + mMargins.left() + mMargins.right());
+ cSize.setHeight(textRect.height() + mMargins.top() + mMargins.bottom());
+ return cSize;
+}
diff --git a/ui/qt/widgets/qcp_string_legend_item.h b/ui/qt/widgets/qcp_string_legend_item.h
new file mode 100644
index 00000000..d32631ad
--- /dev/null
+++ b/ui/qt/widgets/qcp_string_legend_item.h
@@ -0,0 +1,35 @@
+/** @file
+ *
+ * QCustomPlot QCPAbstractLegendItem subclass containing a string.
+ * This is used to add a title to a QCPLegend.
+ *
+ * This file is from https://www.qcustomplot.com/index.php/support/forum/443
+ * where the author David said "I thought I would share in case any one else
+ * is needing the same functionality." Accordingly, this file is in the
+ * public domain.
+ */
+
+#ifndef QCP_STRING_LEGEND_ITEM_H
+#define QCP_STRING_LEGEND_ITEM_H
+
+#include <ui/qt/widgets/qcustomplot.h>
+
+class QCPStringLegendItem : public QCPAbstractLegendItem
+{
+ Q_OBJECT
+
+public:
+ explicit QCPStringLegendItem(QCPLegend *pParent, const QString& strText);
+
+ QString text() const;
+ void setText(const QString& strText);
+
+protected:
+ virtual void draw(QCPPainter *painter) override;
+ virtual QSize minimumOuterSizeHint() const override;
+
+private:
+ QString m_strText;
+};
+
+#endif
diff --git a/ui/qt/widgets/resize_header_view.cpp b/ui/qt/widgets/resize_header_view.cpp
new file mode 100644
index 00000000..7335265f
--- /dev/null
+++ b/ui/qt/widgets/resize_header_view.cpp
@@ -0,0 +1,45 @@
+/** @file
+ *
+ * Header view with a context menu to resize all sections to contents
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "resize_header_view.h"
+
+#include <QMenu>
+#include <QContextMenuEvent>
+
+ResizeHeaderView::ResizeHeaderView(Qt::Orientation orientation, QWidget *parent) : QHeaderView(orientation, parent)
+{
+ setStretchLastSection(true);
+ setSectionsMovable(true);
+ // setFirstSectionMovable(true) ?
+}
+
+/*!
+ \fn void ResizeHeaderView::contextMenuEvent(QContextMenuEvent *e)
+
+ Shows a context menu which resizes all sections to their contents.
+ */
+
+void ResizeHeaderView::contextMenuEvent(QContextMenuEvent *e)
+{
+ if (e == nullptr)
+ return;
+
+ QMenu *ctxMenu = new QMenu(this);
+ ctxMenu->setAttribute(Qt::WA_DeleteOnClose);
+
+ QString text = tr("Resize all %1 to contents").arg((orientation() == Qt::Horizontal) ? "columns" : "rows");
+ QAction *act = ctxMenu->addAction(std::move(text));
+ connect(act, &QAction::triggered, this, [&]() { resizeSections(QHeaderView::ResizeToContents); });
+
+ ctxMenu->popup(e->globalPos());
+}
diff --git a/ui/qt/widgets/resize_header_view.h b/ui/qt/widgets/resize_header_view.h
new file mode 100644
index 00000000..a8aa57f3
--- /dev/null
+++ b/ui/qt/widgets/resize_header_view.h
@@ -0,0 +1,31 @@
+/** @file
+ *
+ * Header view with a context menu to resize all sections to contents
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RESIZE_HEADER_VIEW_H
+#define RESIZE_HEADER_VIEW_H
+
+#include <config.h>
+#include <QHeaderView>
+
+class ResizeHeaderView : public QHeaderView
+{
+ Q_OBJECT
+
+public:
+ ResizeHeaderView(Qt::Orientation orientation, QWidget *parent = nullptr);
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *e) override;
+
+};
+#endif // RESIZE_HEADER_VIEW_H
diff --git a/ui/qt/widgets/resolved_addresses_view.cpp b/ui/qt/widgets/resolved_addresses_view.cpp
new file mode 100644
index 00000000..82b2a299
--- /dev/null
+++ b/ui/qt/widgets/resolved_addresses_view.cpp
@@ -0,0 +1,261 @@
+/** @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>
+#define WS_LOG_DOMAIN LOG_DOMAIN_QTUI
+
+#include <ui/qt/widgets/resolved_addresses_view.h>
+#include <ui/qt/models/resolved_addresses_models.h>
+#include <ui/qt/widgets/wireshark_file_dialog.h>
+
+#include <QHeaderView>
+#include <QMessageBox>
+#include <QClipboard>
+#include <QTextStream>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+#include <QContextMenuEvent>
+
+#include "main_application.h"
+
+#include <wsutil/wslog.h>
+
+ResolvedAddressesView::ResolvedAddressesView(QWidget *parent) : QTableView(parent)
+{
+ setEditTriggers(QAbstractItemView::NoEditTriggers);
+ setSortingEnabled(true);
+ setSelectionBehavior(QAbstractItemView::SelectRows);
+ horizontalHeader()->setStretchLastSection(true);
+ verticalHeader()->setVisible(false);
+
+ // creating this action is mostly to override the default Ctrl-C handling
+ // (which could also be done by overriding KeyPressEvent) and to make the
+ // keyboard shortcut show up in the context menu.
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ clip_action_ = new QAction(tr("as Plain Text"), this);
+ clip_action_->setShortcut(QKeySequence(QKeySequence::Copy));
+ connect(clip_action_, &QAction::triggered, this, &ResolvedAddressesView::clipboardAction);
+ addAction(clip_action_);
+#else
+ clip_action_ = addAction(tr("as Plain Text"), QKeySequence(QKeySequence::Copy), this, &ResolvedAddressesView::clipboardAction);
+#endif
+ clip_action_->setProperty("copy_as", ResolvedAddressesView::EXPORT_TEXT);
+ clip_action_->setProperty("selected", true);
+}
+
+QMenu* ResolvedAddressesView::createCopyMenu(bool selected, QWidget *parent)
+{
+ QMenu *copy_menu;
+ if (selected) {
+ copy_menu = new QMenu(tr("Copy selected rows"), parent);
+ } else {
+ copy_menu = new QMenu(tr("Copy table"), parent);
+ }
+ copy_menu->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
+ QAction *ca;
+ if (selected) {
+ copy_menu->addAction(clip_action_);
+ } else {
+ ca = copy_menu->addAction(tr("as Plain Text"), this, &ResolvedAddressesView::clipboardAction);
+ ca->setProperty("copy_as", ResolvedAddressesView::EXPORT_TEXT);
+ ca->setProperty("selected", selected);
+ }
+ ca = copy_menu->addAction(tr("as CSV"), this, &ResolvedAddressesView::clipboardAction);
+ ca->setProperty("copy_as", ResolvedAddressesView::EXPORT_CSV);
+ ca->setProperty("selected", selected);
+ ca = copy_menu->addAction(tr("as JSON"), this, &ResolvedAddressesView::clipboardAction);
+ ca->setProperty("copy_as", ResolvedAddressesView::EXPORT_JSON);
+ ca->setProperty("selected", selected);
+
+ return copy_menu;
+}
+
+void ResolvedAddressesView::contextMenuEvent(QContextMenuEvent *e)
+{
+ if (!e)
+ return;
+
+ QMenu *ctxMenu = new QMenu(this);
+ ctxMenu->setAttribute(Qt::WA_DeleteOnClose);
+ ctxMenu->addMenu(createCopyMenu(true, ctxMenu));
+ QAction *act = ctxMenu->addAction(tr("Save selected rows as…"));
+ act->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
+ act->setProperty("selected", true);
+ connect(act, &QAction::triggered, this, &ResolvedAddressesView::saveAs);
+ ctxMenu->addSeparator();
+ ctxMenu->addMenu(createCopyMenu(false, ctxMenu));
+ act = ctxMenu->addAction(QIcon::fromTheme(QStringLiteral("document-save-as")), tr("Save table as…"), this, &ResolvedAddressesView::saveAs);
+ act->setProperty("selected", false);
+
+ ctxMenu->popup(e->globalPos());
+}
+
+AStringListListModel* ResolvedAddressesView::dataModel() const
+{
+ QSortFilterProxyModel *proxy = qobject_cast<QSortFilterProxyModel *>(model());
+
+ if (proxy) {
+ QAbstractItemModel *source = proxy->sourceModel();
+ while (qobject_cast<QSortFilterProxyModel *>(source) != nullptr) {
+ proxy = qobject_cast<QSortFilterProxyModel *>(source);
+ source = proxy->sourceModel();
+ }
+ return qobject_cast<AStringListListModel *>(source);
+ }
+ return nullptr;
+}
+
+void ResolvedAddressesView::clipboardAction()
+{
+ QAction *ca = qobject_cast<QAction *>(sender());
+ if (ca && ca->property("copy_as").isValid()) {
+ copyToClipboard(static_cast<eResolvedAddressesExport>(ca->property("copy_as").toInt()),
+ ca->property("selected").toBool());
+ }
+}
+
+void ResolvedAddressesView::copyToClipboard(eResolvedAddressesExport format, bool selected)
+{
+ QString clipText;
+ QTextStream stream(&clipText, QIODevice::Text);
+ toTextStream(stream, format, selected);
+ mainApp->clipboard()->setText(stream.readAll());
+}
+
+void ResolvedAddressesView::saveAs()
+{
+ bool selected = false;
+ QAction *ca = qobject_cast<QAction *>(sender());
+ if (ca && ca->property("selected").isValid()) {
+ selected = true;
+ }
+ QString caption(mainApp->windowTitleString(tr("Save Resolved Addresses As…")));
+ QString txtFilter = tr("Plain text (*.txt)");
+ QString csvFilter = tr("CSV Document (*.csv)");
+ QString jsonFilter = tr("JSON Document (*.json)");
+ QString selectedFilter;
+ QString fileName = WiresharkFileDialog::getSaveFileName(this, caption,
+ mainApp->openDialogInitialDir().canonicalPath(),
+ QString("%1;;%2;;%3").arg(txtFilter).arg(csvFilter).arg(jsonFilter),
+ &selectedFilter);
+ if (fileName.isEmpty()) {
+ return;
+ }
+
+ eResolvedAddressesExport format(EXPORT_TEXT);
+ if (selectedFilter.compare(csvFilter) == 0) {
+ format = EXPORT_CSV;
+ } else if (selectedFilter.compare(jsonFilter) == 0) {
+ format = EXPORT_JSON;
+ }
+
+ // macOS and Windows use the native file dialog, which enforces file
+ // extensions. UN*X dialogs generally don't. That's ok here, at
+ // least for the text format, because hosts and ethers and services
+ // files don't have an extension.
+ QFile saveFile(fileName);
+ if (saveFile.open(QFile::WriteOnly | QFile::Text)) {
+ QTextStream stream(&saveFile);
+ toTextStream(stream, format, selected);
+ saveFile.close();
+ } else {
+ QMessageBox::warning(this, tr("Warning").arg(saveFile.fileName()),
+ tr("Unable to save %1: %2").arg(saveFile.fileName().arg(saveFile.errorString())));
+ }
+}
+
+
+void ResolvedAddressesView::toTextStream(QTextStream& stream,
+ eResolvedAddressesExport format, bool selected) const
+{
+ if (model() == nullptr) {
+ return;
+ }
+
+ // XXX: TrafficTree and TapParameterDialog have similar
+ // "export a QAbstractItemModel to a QTextStream in TEXT, CSV or JSON"
+ // functions that could be made into common code.
+ QStringList rowText;
+ if (format == EXPORT_TEXT) {
+ if (qobject_cast<PortsModel*>(dataModel()) != nullptr) {
+ // Format of services(5)
+ if (!selected) {
+ stream << "# service-name\tport/protocol\n";
+ }
+ for (int row = 0; row < model()->rowCount(); row++) {
+ if (selected && !selectionModel()->isRowSelected(row, QModelIndex())) continue;
+ rowText.clear();
+ rowText << model()->data(model()->index(row, PORTS_COL_NAME)).toString();
+ rowText << QString("%1/%2")
+ .arg(model()->data(model()->index(row, PORTS_COL_PORT)).toString())
+ .arg(model()->data(model()->index(row, PORTS_COL_PROTOCOL)).toString());
+ stream << rowText.join("\t") << "\n";
+ }
+ } else {
+ // Format as hosts(5) and ethers(5)
+ if (!selected) {
+ for (int col = 0; col < model()->columnCount(); col++) {
+ rowText << model()->headerData(col, Qt::Horizontal).toString();
+ }
+ stream << "# " << rowText.join("\t") << "\n";
+ }
+ for (int row = 0; row < model()->rowCount(); row++) {
+ if (selected && !selectionModel()->isRowSelected(row, QModelIndex())) continue;
+ rowText.clear();
+ for (int col = 0; col < model()->columnCount(); col++) {
+ rowText << model()->data(model()->index(row, col)).toString();
+ }
+ stream << rowText.join("\t") << "\n";
+ }
+ }
+ } else if (format == EXPORT_CSV) {
+ for (int col = 0; col < model()->columnCount(); col++) {
+ rowText << model()->headerData(col, Qt::Horizontal).toString();
+ }
+ if (!selected) {
+ stream << rowText.join(",") << "\n";
+ }
+ for (int row = 0; row < model()->rowCount(); row++) {
+ if (selected && !selectionModel()->isRowSelected(row, QModelIndex())) continue;
+ rowText.clear();
+ for (int col = 0; col < model()->columnCount(); col++) {
+ QVariant v = model()->data(model()->index(row, col));
+ if (!v.isValid()) {
+ rowText << QStringLiteral("\"\"");
+ } else if (v.userType() == QMetaType::QString) {
+ rowText << QString("\"%1\"").arg(v.toString().replace('\"', "\"\""));
+ } else {
+ rowText << v.toString();
+ }
+ }
+ stream << rowText.join(",") << "\n";
+ }
+ } else if (format == EXPORT_JSON) {
+ QMap<int, QString> headers;
+ for (int col = 0; col < model()->columnCount(); col++)
+ headers.insert(col, model()->headerData(col, Qt::Horizontal, Qt::DisplayRole).toString());
+
+ QJsonArray records;
+
+ for (int row = 0; row < model()->rowCount(); row++) {
+ if (selected && !selectionModel()->isRowSelected(row, QModelIndex())) continue;
+ QJsonObject rowData;
+ foreach(int col, headers.keys()) {
+ QModelIndex idx = model()->index(row, col);
+ rowData.insert(headers[col], model()->data(idx).toString());
+ }
+ records.push_back(rowData);
+ }
+
+ QJsonDocument json;
+ json.setArray(records);
+ stream << json.toJson();
+ }
+}
diff --git a/ui/qt/widgets/resolved_addresses_view.h b/ui/qt/widgets/resolved_addresses_view.h
new file mode 100644
index 00000000..23fb707f
--- /dev/null
+++ b/ui/qt/widgets/resolved_addresses_view.h
@@ -0,0 +1,50 @@
+/** @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef RESOLVED_ADDRESSES_VIEW_H
+#define RESOLVED_ADDRESSES_VIEW_H
+
+#include <ui/qt/models/resolved_addresses_models.h>
+
+#include <QTableView>
+#include <QMenu>
+
+class ResolvedAddressesView : public QTableView
+{
+ Q_OBJECT
+
+public:
+ typedef enum {
+ EXPORT_TEXT,
+ EXPORT_CSV,
+ EXPORT_JSON
+ } eResolvedAddressesExport;
+
+ ResolvedAddressesView(QWidget *parent = nullptr);
+
+ QMenu* createCopyMenu(bool selected = false, QWidget *parent = nullptr);
+
+public slots:
+ void saveAs();
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *e) override;
+
+private:
+ QAction *clip_action_;
+
+ AStringListListModel* dataModel() const;
+ void copyToClipboard(eResolvedAddressesExport format, bool selected);
+
+private slots:
+ void clipboardAction();
+ void toTextStream(QTextStream &stream, eResolvedAddressesExport format, bool selected = false) const;
+};
+
+#endif // RESOLVED_ADDRESSES_VIEW_H
diff --git a/ui/qt/widgets/rowmove_tree_view.cpp b/ui/qt/widgets/rowmove_tree_view.cpp
new file mode 100644
index 00000000..fa91b28e
--- /dev/null
+++ b/ui/qt/widgets/rowmove_tree_view.cpp
@@ -0,0 +1,98 @@
+/* @file
+ * Tree view that uses the model's moveRows(), if implemented, to
+ * support internalMoves. The model must also have Qt::MoveAction
+ * among the supportedDropActions, and its item flags must allow drag
+ * and drop.
+ *
+ * The normal Qt Drag and Drop approach for moves involves inserting a
+ * new row and removing the original row. That has greater generality,
+ * but works poorly for views like the I/O Graphs Dialog where a newly
+ * inserted row would require an expensive retap.
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "rowmove_tree_view.h"
+
+#include <QDropEvent>
+
+RowMoveTreeView::RowMoveTreeView(QWidget *parent) : TabnavTreeView(parent)
+{
+ // QTreeViews default to row selection.
+ // setSelectionMode(QAbstractItemView::ContiguousSelection);
+ // ContiguousSelection works, but we probably want to make sure
+ // that the models we use this for can handle removing multiple
+ // rows (and that the dialogs support doing that.)
+ setDropIndicatorShown(true);
+ // We could override dragMoveEvent to have the dropIndicator cover
+ // the entire row.
+ setDragDropMode(QAbstractItemView::InternalMove);
+ // Classes can change this if they also support other drag and drop
+ // modes.
+}
+
+void RowMoveTreeView::dropEvent(QDropEvent *event)
+{
+ if (event->source() == this && (event->possibleActions() & Qt::MoveAction) && !event->isAccepted()) {
+
+ const QModelIndexList sourceIndices = selectionModel()->selectedRows();
+
+ if (sourceIndices.empty()) {
+ TabnavTreeView::dropEvent(event);
+ return;
+ }
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ QModelIndex destIndex = indexAt(event->position().toPoint());
+#else
+ QModelIndex destIndex = indexAt(event->pos());
+#endif
+ if (!destIndex.isValid() || destIndex.row() == -1) {
+ destIndex = model()->index(model()->rowCount() - 1, 0);
+ }
+ // dropIndicatorPosition() can be used to determine if we're slightly
+ // above the item, slightly below the item, on top, or elsewhere in
+ // the viewPort. We will just use the row number, table-like.
+ // Note that if we setDragDropOverwriteMode(true) then there wouldn't
+ // be graphical hints in between rows, but that could cause issues
+ // if we added non internalMove handling; overriding dragMoveEvent
+ // could also change it.
+
+ const auto minmaxIndex = std::minmax_element(sourceIndices.begin(), sourceIndices.end(),
+ [](const QModelIndex &a, const QModelIndex &b)
+ { return a.row() < b.row(); }
+ );
+
+ // Only allow a contiguous selection. (This check is unnecessary
+ // if the selectionMode is SingleSelection or ContiguousSelection.)
+ // We could handle multiple ranges with multiple moveRows() calls
+ // and QPersistentModelIndexes in place of the QModelIndexes, but
+ // it gets a little confusing, especially if some indices are above
+ // the target row and some are below (the default behavior would be
+ // to move all the indices above to immediately below, and vice versa.)
+ // Microsoft Excel doesn't allow row moves unless the selected
+ // rows are contiguous, and has an alert.
+ //
+ // Note that selectionModel()->selection()->size() is *not*
+ // guaranteed to be the minimal merged number of possible ranges
+ // if the selection order was unusual, so we can't just use it.
+ if ((minmaxIndex.second->row() - minmaxIndex.first->row() + 1) == sourceIndices.size()) {
+ if (model()->moveRows(QModelIndex(), minmaxIndex.first->row(), static_cast<int>(sourceIndices.size()), QModelIndex(), destIndex.row())) {
+ // Prevent QAbstractItemView from removing the sourceIndices
+ // There's an element in the private class (dropEventMoved)
+ // that QTreeWidget and QTableWidget use via the d-pointer,
+ // but as long as the action is no longer a MoveAction when
+ // it returns it won't get removed.
+ event->setDropAction(Qt::IgnoreAction);
+ event->accept();
+ }
+ }
+ }
+ TabnavTreeView::dropEvent(event);
+}
diff --git a/ui/qt/widgets/rowmove_tree_view.h b/ui/qt/widgets/rowmove_tree_view.h
new file mode 100644
index 00000000..ac415c41
--- /dev/null
+++ b/ui/qt/widgets/rowmove_tree_view.h
@@ -0,0 +1,35 @@
+/** @file
+ *
+ * Tree view that uses a model's moveRows(), if implemented, to support
+ * internalMoves.
+ *
+ * Copyright 2024 John Thacker <johnthacker@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ROWMOVE_TREE_VIEW_H
+#define ROWMOVE_TREE_VIEW_H
+
+#include <config.h>
+#include <ui/qt/widgets/tabnav_tree_view.h>
+
+/**
+ * Like QTreeView, but instead of changing to the next row (same column) when
+ * pressing Tab while editing, change to the next column (same row).
+ */
+class RowMoveTreeView : public TabnavTreeView
+{
+ Q_OBJECT
+
+public:
+ RowMoveTreeView(QWidget *parent = nullptr);
+
+protected:
+ void dropEvent(QDropEvent *event) override;
+};
+#endif // ROWMOVE_TREE_VIEW_H
diff --git a/ui/qt/widgets/rtp_audio_graph.cpp b/ui/qt/widgets/rtp_audio_graph.cpp
index 1340658b..58ff2f1f 100644
--- a/ui/qt/widgets/rtp_audio_graph.cpp
+++ b/ui/qt/widgets/rtp_audio_graph.cpp
@@ -9,8 +9,6 @@
#include "rtp_audio_graph.h"
-#include <glib.h>
-
#include <epan/prefs.h>
#include <ui/qt/utils/color_utils.h>
diff --git a/ui/qt/widgets/splash_overlay.h b/ui/qt/widgets/splash_overlay.h
index b15d98c4..3f5625fc 100644
--- a/ui/qt/widgets/splash_overlay.h
+++ b/ui/qt/widgets/splash_overlay.h
@@ -12,8 +12,6 @@
#include <config.h>
-#include <glib.h>
-
#include "epan/register.h"
#include <QWidget>
diff --git a/ui/qt/widgets/syntax_line_edit.cpp b/ui/qt/widgets/syntax_line_edit.cpp
index f2c32142..3e1abe96 100644
--- a/ui/qt/widgets/syntax_line_edit.cpp
+++ b/ui/qt/widgets/syntax_line_edit.cpp
@@ -9,8 +9,6 @@
#include "config.h"
-#include <glib.h>
-
#include <epan/prefs.h>
#include <epan/proto.h>
#include <epan/dfilter/dfilter.h>
@@ -86,7 +84,7 @@ void SyntaxLineEdit::setSyntaxState(SyntaxState state) {
QColor deprecated_bg = ColorUtils::fromColorT(&prefs.gui_text_deprecated);
QColor deprecated_fg = ColorUtils::contrastingTextColor(deprecated_bg);
- // Try to matche QLineEdit's placeholder text color (which sets the
+ // Try to match QLineEdit's placeholder text color (which sets the
// alpha channel to 50%, which doesn't work in style sheets).
// Setting the foreground color lets us avoid yet another background
// color preference and should hopefully make things easier to
@@ -212,7 +210,7 @@ bool SyntaxLineEdit::checkDisplayFilter(QString filter)
* We're being lazy and only printing the first warning.
* Would it be better to print all of them?
*/
- syntax_error_message_ = QString(static_cast<gchar *>(warn->data));
+ syntax_error_message_ = QString(static_cast<char *>(warn->data));
} else if (dfp != NULL && (depr = dfilter_deprecated_tokens(dfp)) != NULL) {
// You keep using that word. I do not think it means what you think it means.
// Possible alternatives: ::Troubled, or ::Problematic maybe?
@@ -222,7 +220,7 @@ bool SyntaxLineEdit::checkDisplayFilter(QString filter)
* Would it be better to print all of them?
*/
QString token((const char *)g_ptr_array_index(depr, 0));
- gchar *token_str = qstring_strdup(token.section('.', 0, 0));
+ char *token_str = qstring_strdup(token.section('.', 0, 0));
header_field_info *hfi = proto_registrar_get_byalias(token_str);
if (hfi)
syntax_error_message_ = tr("\"%1\" is deprecated in favour of \"%2\". "
@@ -267,10 +265,23 @@ void SyntaxLineEdit::checkCustomColumn(QString fields)
return;
}
- gchar **splitted_fields = g_regex_split_simple(COL_CUSTOM_PRIME_REGEX,
- fields.toUtf8().constData(), G_REGEX_ANCHORED, G_REGEX_MATCH_ANCHORED);
-
- for (guint i = 0; i < g_strv_length(splitted_fields); i++) {
+#if 0
+ // XXX - Eventually, if the operator we split on is something not supported
+ // in the filter expression syntax (so that we can distinguish multifield
+ // concatenation of column strings from a logical OR), we would split and
+ // then check each split result as a valid display filter.
+ // For now, any expression that is a valid display filter should work.
+ //
+ // We also, for the custom columns, want some of the extra completion
+ // information from DisplayFilterEdit (like the display filter functions),
+ // without all of its integration into the main app, but not every user
+ // of FieldFilterEdit wants that, so perhaps we eventually should have
+ // another class.
+ char **splitted_fields = g_regex_split_simple(COL_CUSTOM_PRIME_REGEX,
+ fields.toUtf8().constData(), (GRegexCompileFlags) G_REGEX_RAW,
+ (GRegexMatchFlags) 0);
+
+ for (unsigned i = 0; i < g_strv_length(splitted_fields); i++) {
if (splitted_fields[i] && *splitted_fields[i]) {
if (proto_check_field_name(splitted_fields[i]) != 0) {
setSyntaxState(SyntaxLineEdit::Invalid);
@@ -280,6 +291,7 @@ void SyntaxLineEdit::checkCustomColumn(QString fields)
}
}
g_strfreev(splitted_fields);
+#endif
checkDisplayFilter(fields);
}
diff --git a/ui/qt/widgets/traffic_tab.cpp b/ui/qt/widgets/traffic_tab.cpp
index 9913acaa..6233d60d 100644
--- a/ui/qt/widgets/traffic_tab.cpp
+++ b/ui/qt/widgets/traffic_tab.cpp
@@ -78,7 +78,7 @@ TrafficTab::TrafficTab(QWidget * parent) :
TrafficTab::~TrafficTab()
{}
-void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel)
+void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentList, GList ** recentColumnList, ATapModelCallback createModel)
{
setTabBasename(tableName);
@@ -86,6 +86,7 @@ void TrafficTab::setProtocolInfo(QString tableName, TrafficTypesList * trafficLi
if (createModel)
_createModel = createModel;
+ _recentList = recentList;
_recentColumnList = recentColumnList;
setOpenTabs(trafficList->protocols(true));
@@ -159,7 +160,7 @@ void TrafficTab::useAbsoluteTime(bool absolute)
{
for(int idx = 0; idx < count(); idx++)
{
- ATapDataModel * atdm = modelForTabIndex(idx);
+ ATapDataModel * atdm = dataModelForTabIndex(idx);
if (atdm)
atdm->useAbsoluteTime(absolute);
}
@@ -169,7 +170,7 @@ void TrafficTab::useNanosecondTimestamps(bool nanoseconds)
{
for(int idx = 0; idx < count(); idx++)
{
- ATapDataModel * atdm = modelForTabIndex(idx);
+ ATapDataModel * atdm = dataModelForTabIndex(idx);
if (atdm)
atdm->useNanosecondTimestamps(nanoseconds);
}
@@ -179,7 +180,7 @@ void TrafficTab::disableTap()
{
for(int idx = 0; idx < count(); idx++)
{
- ATapDataModel * atdm = modelForTabIndex(idx);
+ ATapDataModel * atdm = dataModelForTabIndex(idx);
if (atdm)
atdm->disableTap();
}
@@ -260,6 +261,12 @@ void TrafficTab::insertProtoTab(int protoId, bool emitSignals)
if (tabId >= 0)
tabBar()->setTabData(tabId, storage);
+ // Identify the last known opened tab
+ int lastOpened_protoId = -1;
+ GList *selected_tab = g_list_first(*_recentList);
+ if (selected_tab != nullptr) {
+ lastOpened_protoId = proto_get_id_by_short_name((const char *)selected_tab->data);
+ }
/* 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
@@ -270,6 +277,11 @@ void TrafficTab::insertProtoTab(int protoId, bool emitSignals)
_tabs.insert(tabData.protoId(), idx);
}
+ // Restore the last known opened tab
+ if(lastOpened_protoId == protoId) {
+ setCurrentIndex(tabId);
+ }
+
if (emitSignals) {
emit tabsChanged(_tabs.keys());
emit retapRequired();
@@ -329,7 +341,7 @@ QVariant TrafficTab::currentItemData(int role)
* to ensure proper handling. Especially ConversationDialog depends on this
* method always returning data */
if (!idx.isValid()) {
- ATapDataModel * model = modelForTabIndex(currentIndex());
+ TrafficDataFilterProxy * model = modelForTabIndex(currentIndex());
idx = model->index(0, 0);
}
return idx.data(role);
@@ -365,7 +377,7 @@ void TrafficTab::modelReset()
emit tabDataChanged(tabIdx);
}
-ATapDataModel * TrafficTab::modelForTabIndex(int tabIdx)
+TrafficDataFilterProxy * TrafficTab::modelForTabIndex(int tabIdx)
{
if (tabIdx == -1)
tabIdx = currentIndex();
@@ -373,26 +385,40 @@ ATapDataModel * TrafficTab::modelForTabIndex(int tabIdx)
return modelForWidget(widget(tabIdx));
}
-ATapDataModel * TrafficTab::modelForWidget(QWidget * searchWidget)
+TrafficDataFilterProxy * TrafficTab::modelForWidget(QWidget * searchWidget)
{
if (qobject_cast<QTreeView *>(searchWidget)) {
QTreeView * tree = qobject_cast<QTreeView *>(searchWidget);
if (qobject_cast<TrafficDataFilterProxy *>(tree->model())) {
- TrafficDataFilterProxy * qsfpm = qobject_cast<TrafficDataFilterProxy *>(tree->model());
- if (qsfpm && qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) {
- return qobject_cast<ATapDataModel *>(qsfpm->sourceModel());
- }
+ return qobject_cast<TrafficDataFilterProxy *>(tree->model());
}
}
return nullptr;
}
+ATapDataModel * TrafficTab::dataModelForTabIndex(int tabIdx)
+{
+ if (tabIdx == -1)
+ tabIdx = currentIndex();
+
+ return dataModelForWidget(widget(tabIdx));
+}
+
+ATapDataModel * TrafficTab::dataModelForWidget(QWidget * searchWidget)
+{
+ TrafficDataFilterProxy * qsfpm = modelForWidget(searchWidget);
+ if (qsfpm && qobject_cast<ATapDataModel *>(qsfpm->sourceModel())) {
+ return qobject_cast<ATapDataModel *>(qsfpm->sourceModel());
+ }
+ return nullptr;
+}
+
void TrafficTab::setFilter(QString filter)
{
for (int idx = 0; idx < count(); idx++ )
{
- ATapDataModel * atdm = modelForTabIndex(idx);
+ ATapDataModel * atdm = dataModelForTabIndex(idx);
if (! atdm)
continue;
atdm->setFilter(filter);
@@ -406,7 +432,7 @@ void TrafficTab::setNameResolution(bool checked)
for (int idx = 0; idx < count(); idx++ )
{
- ATapDataModel * atdm = modelForTabIndex(idx);
+ ATapDataModel * atdm = dataModelForTabIndex(idx);
if (! atdm)
continue;
atdm->setResolveNames(checked);
@@ -422,7 +448,7 @@ void TrafficTab::setNameResolution(bool checked)
bool TrafficTab::hasNameResolution(int tabIdx)
{
int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx;
- ATapDataModel * dataModel = modelForTabIndex(tab);
+ ATapDataModel * dataModel = dataModelForTabIndex(tab);
if (! dataModel)
return false;
@@ -443,12 +469,12 @@ bool TrafficTab::hasGeoIPData(int tabIdx)
{
int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx;
- ATapDataModel * dataModel = modelForTabIndex(tab);
+ ATapDataModel * dataModel = dataModelForTabIndex(tab);
return dataModel->hasGeoIPData();
}
bool
-TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataModel)
+TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, TrafficDataFilterProxy * model)
{
QTextStream out(fp);
@@ -508,14 +534,15 @@ TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataMo
QJsonArray features;
/* Append map data. */
- for(int row = 0; row < dataModel->rowCount(QModelIndex()); row++)
+ for(int row = 0; row < model->rowCount(QModelIndex()); row++)
{
- QModelIndex index = dataModel->index(row, 0);
+ QModelIndex index = model->mapToSource(model->index(row, 0));
+ ATapDataModel *dataModel = qobject_cast<ATapDataModel *>(model->sourceModel());
const mmdb_lookup_t * result = VariantPointer<const mmdb_lookup_t>::asPtr(dataModel->data(index, ATapDataModel::GEODATA_LOOKUPTABLE));
if (!maxmind_db_has_coords(result)) {
// result could be NULL if the caller did not trigger a lookup
- // before. result->found could be FALSE if no MMDB entry exists.
+ // before. result->found could be false if no MMDB entry exists.
continue;
}
@@ -545,8 +572,8 @@ TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataMo
if (qobject_cast<EndpointDataModel *>(dataModel)) {
EndpointDataModel * endpointModel = qobject_cast<EndpointDataModel *>(dataModel);
- property["packets"] = endpointModel->data(endpointModel->index(row, EndpointDataModel::ENDP_COLUMN_PACKETS)).toString();
- property["bytes"] = endpointModel->data(endpointModel->index(row, EndpointDataModel::ENDP_COLUMN_BYTES)).toString();
+ property["packets"] = endpointModel->data(index.siblingAtColumn(EndpointDataModel::ENDP_COLUMN_PACKETS)).toString();
+ property["bytes"] = endpointModel->data(index.siblingAtColumn(EndpointDataModel::ENDP_COLUMN_BYTES)).toString();
}
arrEntry["properties"] = property;
features.append(arrEntry);
@@ -568,7 +595,7 @@ TrafficTab::writeGeoIPMapFile(QFile * fp, bool json_only, ATapDataModel * dataMo
QUrl TrafficTab::createGeoIPMap(bool json_only, int tabIdx)
{
int tab = tabIdx == -1 || tabIdx >= count() ? currentIndex() : tabIdx;
- ATapDataModel * dataModel = modelForTabIndex(tab);
+ ATapDataModel * dataModel = dataModelForTabIndex(tab);
if (! (dataModel && dataModel->hasGeoIPData())) {
QMessageBox::warning(this, tr("Map file error"), tr("No endpoints available to map"));
return QUrl();
@@ -581,7 +608,7 @@ QUrl TrafficTab::createGeoIPMap(bool json_only, int tabIdx)
return QUrl();
}
- if (!writeGeoIPMapFile(&tf, json_only, dataModel)) {
+ if (!writeGeoIPMapFile(&tf, json_only, modelForTabIndex(tab))) {
tf.close();
return QUrl();
}
@@ -592,7 +619,7 @@ QUrl TrafficTab::createGeoIPMap(bool json_only, int tabIdx)
#endif
void TrafficTab::detachTab(int tabIdx, QPoint pos) {
- ATapDataModel * model = modelForTabIndex(tabIdx);
+ ATapDataModel * model = dataModelForTabIndex(tabIdx);
if (!model)
return;
@@ -608,7 +635,7 @@ void TrafficTab::detachTab(int tabIdx, QPoint pos) {
void TrafficTab::attachTab(QWidget * content, QString name)
{
- ATapDataModel * model = modelForWidget(content);
+ ATapDataModel * model = dataModelForWidget(content);
if (!model) {
attachTab(content, name);
return;
diff --git a/ui/qt/widgets/traffic_tab.h b/ui/qt/widgets/traffic_tab.h
index 71c48ae5..ff0bacc5 100644
--- a/ui/qt/widgets/traffic_tab.h
+++ b/ui/qt/widgets/traffic_tab.h
@@ -12,10 +12,9 @@
#include "config.h"
-#include <glib.h>
-
#include <ui/qt/models/atap_data_model.h>
#include <ui/qt/filter_action.h>
+#include <ui/qt/widgets/traffic_tree.h>
#include <ui/qt/widgets/detachable_tabwidget.h>
#include <ui/qt/widgets/traffic_types_list.h>
@@ -94,7 +93,7 @@ public:
*
* @see ATapModelCallback
*/
- void setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentColumnList, ATapModelCallback createModel);
+ void setProtocolInfo(QString tableName, TrafficTypesList * trafficList, GList ** recentList, GList ** recentColumnList, ATapModelCallback createModel);
/**
* @brief Set the Delegate object for the tab. It will apply for all
@@ -221,20 +220,23 @@ private:
QMap<int, int> _tabs;
ATapModelCallback _createModel;
ATapCreateDelegate _createDelegate;
+ GList ** _recentList;
GList ** _recentColumnList;
bool _disableTaps;
bool _nameResolution;
QTreeView * createTree(int protoId);
- ATapDataModel * modelForTabIndex(int tabIdx = -1);
- ATapDataModel * modelForWidget(QWidget * widget);
+ TrafficDataFilterProxy * modelForTabIndex(int tabIdx = -1);
+ TrafficDataFilterProxy * modelForWidget(QWidget * widget);
+ ATapDataModel * dataModelForTabIndex(int tabIdx = -1);
+ ATapDataModel * dataModelForWidget(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);
+ bool writeGeoIPMapFile(QFile * fp, bool json_only, TrafficDataFilterProxy * model);
#endif
private slots:
diff --git a/ui/qt/widgets/traffic_tree.cpp b/ui/qt/widgets/traffic_tree.cpp
index be154d7d..4325e556 100644
--- a/ui/qt/widgets/traffic_tree.cpp
+++ b/ui/qt/widgets/traffic_tree.cpp
@@ -204,7 +204,7 @@ void TrafficTreeHeaderView::columnTriggered(bool checked)
for (int col = 0; col < tree->dataModel()->columnCount(); col++) {
if (proxy->columnVisible(col)) {
visible << col;
- gchar *nr = qstring_strdup(QString::number(col));
+ char *nr = qstring_strdup(QString::number(col));
*_recentColumnList = g_list_append(*_recentColumnList, nr);
}
}
@@ -473,7 +473,35 @@ bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QMod
int addressTypeA = model->data(source_left, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
int addressTypeB = model->data(source_right, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
if (addressTypeA != 0 && addressTypeB != 0 && addressTypeA != addressTypeB) {
- result = addressTypeA < addressTypeB;
+
+ /* Handle subnets when they are compared to IP addresses */
+ if ( (addressTypeA == AT_STRINGZ) && (addressTypeB == AT_IPv4) ) {
+ QString subnet = datA.toString();
+ qint64 lpart = subnet.indexOf("/");
+ ws_in4_addr ip4addr;
+
+ if(ws_inet_pton4(subnet.left(lpart).toUtf8().data(), &ip4addr)) {
+ quint32 valA = g_ntohl(ip4addr);
+ quint32 valB = model->data(source_right, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
+ result = valA < valB;
+ identical = valA == valB;
+ }
+ // else: never supposed to happen
+ } else if ( (addressTypeA == AT_IPv4) && (addressTypeB == AT_STRINGZ) ) {
+ QString subnet = datB.toString();
+ qint64 lpart = subnet.indexOf("/");
+ ws_in4_addr ip4addr;
+ if(ws_inet_pton4(subnet.left(lpart).toUtf8().data(), &ip4addr)) {
+ quint32 valA = model->data(source_left, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
+ quint32 valB = g_ntohl(ip4addr);
+ result = valA < valB;
+ identical = valA == valB;
+ }
+ // else: never supposed to happen
+ } else {
+ result = addressTypeA < addressTypeB;
+ }
+
} else if (addressTypeA != 0 && addressTypeA == addressTypeB) {
if (addressTypeA == AT_IPv4) {
@@ -693,8 +721,28 @@ QMenu * TrafficTree::createActionSubMenu(FilterAction::Action cur_action, QModel
foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
if (isConversation && conv_item) {
QMenu *subsubmenu = subMenu->addMenu(FilterAction::actionTypeName(at));
- if (hasConvId && (cur_action == FilterAction::ActionApply || cur_action == FilterAction::ActionPrepare)) {
- QString filter = QString("%1.stream eq %2").arg(conv_item->ctype == CONVERSATION_TCP ? "tcp" : "udp").arg(conv_item->conv_id);
+
+ /* For IP, ensure subnets-like conversations won't enable Stream ID filters (!CONV_ID_UNSET) */
+ if (hasConvId && (conv_item->conv_id!=CONV_ID_UNSET) && (cur_action == FilterAction::ActionApply || cur_action == FilterAction::ActionPrepare)) {
+ QString filter;
+ switch (conv_item->ctype) {
+ case CONVERSATION_TCP:
+ filter = QString("%1.stream eq %2").arg("tcp").arg(conv_item->conv_id);
+ break;
+ case CONVERSATION_UDP:
+ filter = QString("%1.stream eq %2").arg("udp").arg(conv_item->conv_id);
+ break;
+ case CONVERSATION_IP:
+ filter = QString("%1.stream eq %2").arg("ip").arg(conv_item->conv_id);
+ break;
+ case CONVERSATION_IPV6:
+ filter = QString("%1.stream eq %2").arg("ipv6").arg(conv_item->conv_id);
+ break;
+ case CONVERSATION_ETH:
+ default:
+ filter = QString("%1.stream eq %2").arg("eth").arg(conv_item->conv_id);
+ break;
+ }
FilterAction * act = new FilterAction(subsubmenu, cur_action, at, tr("Filter on stream id"));
act->setProperty("filter", filter);
subsubmenu->addAction(act);
diff --git a/ui/qt/widgets/traffic_tree.h b/ui/qt/widgets/traffic_tree.h
index 5bc87e91..00d91783 100644
--- a/ui/qt/widgets/traffic_tree.h
+++ b/ui/qt/widgets/traffic_tree.h
@@ -12,8 +12,6 @@
#include "config.h"
-#include <glib.h>
-
#include <ui/recent.h>
#include <ui/qt/models/atap_data_model.h>
diff --git a/ui/qt/widgets/traffic_types_list.cpp b/ui/qt/widgets/traffic_types_list.cpp
index 30126ef9..ddbe34a3 100644
--- a/ui/qt/widgets/traffic_types_list.cpp
+++ b/ui/qt/widgets/traffic_types_list.cpp
@@ -9,9 +9,8 @@
#include "config.h"
-#include <glib.h>
-
#include <epan/conversation_table.h>
+#include <epan/prefs.h>
#include <ui/qt/widgets/traffic_types_list.h>
@@ -48,12 +47,12 @@ static bool 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;
+ const QString title = (const char*)key;
int proto_id = get_conversation_proto_id(ct);
TrafficTypesRowData entry(proto_id, title);
protocols->append(entry);
- return FALSE;
+ return false;
}
TrafficTypesModel::TrafficTypesModel(GList ** recentList, QObject *parent) :
@@ -152,6 +151,26 @@ bool TrafficTypesModel::setData(const QModelIndex &idx, const QVariant &value, i
if (_allTaps.count() <= idx.row())
return false;
+ // When updating the tabs, save the current selection, it will be restored below
+ GList *selected_tab = g_list_first(*_recentList);
+ int rct_protoId = -1;
+ if (selected_tab != nullptr) {
+ rct_protoId = proto_get_id_by_short_name((const char *)selected_tab->data);
+
+ // Did the user just uncheck the current selection?
+ if (_allTaps[idx.row()].protocol() == rct_protoId && value.toInt() == Qt::Unchecked) {
+ // Yes. The code below will restore it. Rather than removing it,
+ // resetting the model, and then adding it back, just return.
+ // The user might want to uncheck the current selection, in which
+ // case the code needs to changed to handle that.
+ //
+ // Note that not allowing the current first tab to be unselected does
+ // have the advantage of preventing a crash from having no tabs
+ // selected in the Endpoint dialog (#18250).
+ return false;
+ }
+ }
+
_allTaps[idx.row()].setChecked(value.toInt() == Qt::Checked);
QList<int> selected;
@@ -161,12 +180,21 @@ bool TrafficTypesModel::setData(const QModelIndex &idx, const QVariant &value, i
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);
+ if(protoId != rct_protoId) {
+ selected.append(protoId);
+ char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(protoId)));
+ *_recentList = g_list_append(*_recentList, title);
+ }
}
}
+ if (rct_protoId != -1) {
+ // restore the selection by prepending it to the recent list
+ char *rct_title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(rct_protoId)));
+ selected.prepend(rct_protoId);
+ *_recentList = g_list_prepend(*_recentList, rct_title);
+ }
+
emit protocolsChanged(selected);
emit dataChanged(idx, idx);
diff --git a/ui/qt/widgets/traffic_types_list.h b/ui/qt/widgets/traffic_types_list.h
index 00798c50..2666eb78 100644
--- a/ui/qt/widgets/traffic_types_list.h
+++ b/ui/qt/widgets/traffic_types_list.h
@@ -12,8 +12,6 @@
#include "config.h"
-#include <glib.h>
-
#include <QTreeView>
#include <QAbstractListModel>
#include <QMap>
@@ -122,4 +120,4 @@ private:
TrafficListSortModel * _sortModel;
};
-#endif // TRAFFIC_TYPES_LIST_H \ No newline at end of file
+#endif // TRAFFIC_TYPES_LIST_H
diff --git a/ui/qt/widgets/wireless_timeline.cpp b/ui/qt/widgets/wireless_timeline.cpp
index dcdcd4f8..87eb1079 100644
--- a/ui/qt/widgets/wireless_timeline.cpp
+++ b/ui/qt/widgets/wireless_timeline.cpp
@@ -75,7 +75,7 @@ static void reset_rgb(float rgb[TIMELINE_HEIGHT][3])
rgb[i][0] = rgb[i][1] = rgb[i][2] = 1.0;
}
-static void render_pixels(QPainter &p, gint x, gint width, float rgb[TIMELINE_HEIGHT][3], float ratio)
+static void render_pixels(QPainter &p, int x, int width, float rgb[TIMELINE_HEIGHT][3], float ratio)
{
int previous = 0, i;
for (i = 1; i <= TIMELINE_HEIGHT; i++) {
@@ -92,7 +92,7 @@ static void render_pixels(QPainter &p, gint x, gint width, float rgb[TIMELINE_HE
reset_rgb(rgb);
}
-static void render_rectangle(QPainter &p, gint x, gint width, guint height, int dfilter, float r, float g, float b, float ratio)
+static void render_rectangle(QPainter &p, int x, int width, unsigned height, int dfilter, float r, float g, float b, float ratio)
{
p.fillRect(QRectF(x/ratio, TIMELINE_HEIGHT/2-height, width/ratio, dfilter ? height * 2 : height), pcolor(r,g,b));
}
@@ -157,29 +157,25 @@ void WirelessTimeline::mouseReleaseEvent(QMouseEvent *event)
return;
/* this was a click */
- guint num = find_packet(localPos.x());
+ unsigned num = find_packet(localPos.x());
if (num == 0)
return;
- frame_data *fdata = frame_data_sequence_find(cfile.provider.frames, num);
- if (!fdata->passed_dfilter && fdata->prev_dis_num > 0)
- num = fdata->prev_dis_num;
-
- cf_goto_frame(&cfile, num);
+ cf_goto_frame(&cfile, num, false);
}
void WirelessTimeline::clip_tsf()
{
// did we go past the start of the file?
- if (((gint64) start_tsf) < ((gint64) first->start_tsf)) {
+ if (((int64_t) start_tsf) < ((int64_t) first->start_tsf)) {
// align the start of the file at the left edge
- guint64 shift = first->start_tsf - start_tsf;
+ uint64_t shift = first->start_tsf - start_tsf;
start_tsf += shift;
end_tsf += shift;
}
if (end_tsf > last->end_tsf) {
- guint64 shift = end_tsf - last->end_tsf;
+ uint64_t shift = end_tsf - last->end_tsf;
start_tsf -= shift;
end_tsf -= shift;
}
@@ -194,32 +190,32 @@ void WirelessTimeline::selectedFrameChanged(QList<int>)
if (cfile.current_frame) {
struct wlan_radio *wr = get_wlan_radio(cfile.current_frame->num);
- guint left_margin = 0.9 * start_tsf + 0.1 * end_tsf;
- guint right_margin = 0.1 * start_tsf + 0.9 * end_tsf;
- guint64 half_window = (end_tsf - start_tsf)/2;
+ unsigned left_margin = 0.9 * start_tsf + 0.1 * end_tsf;
+ unsigned right_margin = 0.1 * start_tsf + 0.9 * end_tsf;
+ uint64_t half_window = (end_tsf - start_tsf)/2;
if (wr) {
// are we to the left of the left margin?
if (wr->start_tsf < left_margin) {
// scroll the left edge back to the left margin
- guint64 offset = left_margin - wr->start_tsf;
+ uint64_t offset = left_margin - wr->start_tsf;
if (offset < half_window) {
// small movement; keep packet to margin
start_tsf -= offset;
end_tsf -= offset;
} else {
// large movement; move packet to center of window
- guint64 center = (wr->start_tsf + wr->end_tsf)/2;
+ uint64_t center = (wr->start_tsf + wr->end_tsf)/2;
start_tsf = center - half_window;
end_tsf = center + half_window;
}
} else if (wr->end_tsf > right_margin) {
- guint64 offset = wr->end_tsf - right_margin;
+ uint64_t offset = wr->end_tsf - right_margin;
if (offset < half_window) {
start_tsf += offset;
end_tsf += offset;
} else {
- guint64 center = (wr->start_tsf + wr->end_tsf)/2;
+ uint64_t center = (wr->start_tsf + wr->end_tsf)/2;
start_tsf = center - half_window;
end_tsf = center + half_window;
}
@@ -234,10 +230,10 @@ void WirelessTimeline::selectedFrameChanged(QList<int>)
/* given an x position find which packet that corresponds to.
* if it's inter frame space the subsequent packet is returned */
-guint
+unsigned
WirelessTimeline::find_packet(qreal x_position)
{
- guint64 x_time = start_tsf + (x_position/width() * (end_tsf - start_tsf));
+ uint64_t x_time = start_tsf + (x_position/width() * (end_tsf - start_tsf));
return find_packet_tsf(x_time);
}
@@ -267,7 +263,7 @@ void WirelessTimeline::captureFileReadFinished()
*/
/* TODO: update GUI to handle captures with occasional frames missing TSF data */
/* TODO: indicate error message to the user */
- for (guint32 n = 1; n < cfile.count; n++) {
+ for (uint32_t n = 1; n < cfile.count; n++) {
struct wlan_radio *w = get_wlan_radio(n);
if (w->start_tsf == 0 || w->end_tsf == 0) {
QString err = tr("Packet number %1 does not include TSF timestamp, not showing timeline.").arg(n);
@@ -315,11 +311,11 @@ void WirelessTimeline::resizeEvent(QResizeEvent*)
// Calculate the x position on the GUI from the timestamp
-int WirelessTimeline::position(guint64 tsf, float ratio)
+int WirelessTimeline::position(uint64_t tsf, float ratio)
{
int position = -100;
- if (tsf != G_MAXUINT64) {
+ if (tsf != UINT64_MAX) {
position = ((double) tsf - start_tsf)*width()*ratio/(end_tsf-start_tsf);
}
return position;
@@ -378,11 +374,11 @@ tap_packet_status WirelessTimeline::tap_timeline_packet(void *tapdata, packet_in
const struct wlan_radio *wlan_radio_info = (const struct wlan_radio *)data;
/* Save the radio information in our own (GUI) hashtable */
- g_hash_table_insert(timeline->radio_packet_list, GUINT_TO_POINTER(pinfo->num), (gpointer)wlan_radio_info);
+ g_hash_table_insert(timeline->radio_packet_list, GUINT_TO_POINTER(pinfo->num), (void *)wlan_radio_info);
return TAP_PACKET_DONT_REDRAW;
}
-struct wlan_radio* WirelessTimeline::get_wlan_radio(guint32 packet_num)
+struct wlan_radio* WirelessTimeline::get_wlan_radio(uint32_t packet_num)
{
return (struct wlan_radio*)g_hash_table_lookup(radio_packet_list, GUINT_TO_POINTER(packet_num));
}
@@ -402,7 +398,7 @@ bool WirelessTimeline::event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
- guint packet = find_packet(helpEvent->pos().x());
+ unsigned packet = find_packet(helpEvent->pos().x());
if (packet) {
doToolTip(get_wlan_radio(packet), helpEvent->globalPos(), helpEvent->x());
} else {
@@ -452,16 +448,16 @@ void WirelessTimeline::bgColorizationProgress(int first, int last)
void WirelessTimeline::zoom(double x_fraction)
{
/* adjust the zoom around the selected packet */
- guint64 file_range = last->end_tsf - first->start_tsf;
- guint64 center = start_tsf + x_fraction * (end_tsf - start_tsf);
- guint64 span = pow(file_range, 1.0 - zoom_level / TIMELINE_MAX_ZOOM);
+ uint64_t file_range = last->end_tsf - first->start_tsf;
+ uint64_t center = start_tsf + x_fraction * (end_tsf - start_tsf);
+ uint64_t span = pow(file_range, 1.0 - zoom_level / TIMELINE_MAX_ZOOM);
start_tsf = center - span * x_fraction;
end_tsf = center + span * (1.0 - x_fraction);
clip_tsf();
update();
}
-int WirelessTimeline::find_packet_tsf(guint64 tsf)
+int WirelessTimeline::find_packet_tsf(uint64_t tsf)
{
if (cfile.count < 1)
return 0;
@@ -469,11 +465,11 @@ int WirelessTimeline::find_packet_tsf(guint64 tsf)
if (cfile.count < 2)
return 1;
- guint32 min_count = 1;
- guint32 max_count = cfile.count-1;
+ uint32_t min_count = 1;
+ uint32_t max_count = cfile.count-1;
- guint64 min_tsf = get_wlan_radio(min_count)->end_tsf;
- guint64 max_tsf = get_wlan_radio(max_count)->end_tsf;
+ uint64_t min_tsf = get_wlan_radio(min_count)->end_tsf;
+ uint64_t max_tsf = get_wlan_radio(max_count)->end_tsf;
for (;;) {
if (tsf >= max_tsf)
@@ -482,11 +478,11 @@ int WirelessTimeline::find_packet_tsf(guint64 tsf)
if (tsf < min_tsf)
return min_count;
- guint32 middle = (min_count + max_count)/2;
+ uint32_t middle = (min_count + max_count)/2;
if (middle == min_count)
return middle+1;
- guint64 middle_tsf = get_wlan_radio(middle)->end_tsf;
+ uint64_t middle_tsf = get_wlan_radio(middle)->end_tsf;
if (tsf >= middle_tsf) {
min_count = middle;
@@ -550,9 +546,9 @@ WirelessTimeline::paintEvent(QPaintEvent *qpe)
if (ri == NULL) continue;
- gint8 rssi = ri->aggregate ? ri->aggregate->rssi : ri->rssi;
- guint height = (rssi+100)/2;
- gint end_nav;
+ int8_t rssi = ri->aggregate ? ri->aggregate->rssi : ri->rssi;
+ unsigned height = (rssi+100)/2;
+ int end_nav;
/* leave a margin above the packets so the selected packet can be seen */
if (height > TIMELINE_HEIGHT/2-6)
@@ -567,7 +563,7 @@ WirelessTimeline::paintEvent(QPaintEvent *qpe)
if (ri->start_tsf == 0 || ri->end_tsf == 0)
continue;
- x = ((gint64) (ri->start_tsf - start_tsf))*zoom;
+ x = ((int64_t) (ri->start_tsf - start_tsf))*zoom;
/* is there a previous anti-aliased pixel to output */
if (last_x >= 0 && ((int) x) != last_x) {
/* write it out now */
@@ -606,7 +602,7 @@ WirelessTimeline::paintEvent(QPaintEvent *qpe)
/* record NAV field at higher magnifications */
end_nav = x + width + ri->nav*zoom;
if (zoom >= 0.01 && ri->nav && end_nav > 0) {
- gint y = 2*(packet % (TIMELINE_HEIGHT/2));
+ int y = 2*(packet % (TIMELINE_HEIGHT/2));
qs.addLine(QLineF((x+width)/ratio, y, end_nav/ratio, y), QPen(pcolor(red,green,blue)));
}
diff --git a/ui/qt/widgets/wireless_timeline.h b/ui/qt/widgets/wireless_timeline.h
index de43d123..cf67ff4e 100644
--- a/ui/qt/widgets/wireless_timeline.h
+++ b/ui/qt/widgets/wireless_timeline.h
@@ -21,8 +21,6 @@
#include <config.h>
-#include <glib.h>
-
#include "file.h"
#include "ui/ws_ui_util.h"
@@ -75,21 +73,21 @@ protected:
static void tap_timeline_reset(void* tapdata);
static tap_packet_status tap_timeline_packet(void *tapdata, packet_info* pinfo, epan_dissect_t* edt, const void *data, tap_flags_t flags);
- struct wlan_radio* get_wlan_radio(guint32 packet_num);
+ struct wlan_radio* get_wlan_radio(uint32_t packet_num);
void clip_tsf();
- int position(guint64 tsf, float ratio);
- int find_packet_tsf(guint64 tsf);
+ int position(uint64_t tsf, float ratio);
+ int find_packet_tsf(uint64_t tsf);
void doToolTip(struct wlan_radio *wr, QPoint pos, int x);
void zoom(double x_fraction);
double zoom_level;
qreal start_x, last_x;
PacketList *packet_list;
- guint find_packet(qreal x);
+ unsigned find_packet(qreal x);
float rgb[TIMELINE_HEIGHT][3];
- guint64 start_tsf;
- guint64 end_tsf;
+ uint64_t start_tsf;
+ uint64_t end_tsf;
int first_packet; /* first packet displayed */
struct wlan_radio *first, *last;
capture_file *capfile;
diff --git a/ui/qt/widgets/wireshark_file_dialog.cpp b/ui/qt/widgets/wireshark_file_dialog.cpp
index acf9c7db..3c5b8d3e 100644
--- a/ui/qt/widgets/wireshark_file_dialog.cpp
+++ b/ui/qt/widgets/wireshark_file_dialog.cpp
@@ -43,6 +43,15 @@ WiresharkFileDialog::WiresharkFileDialog(QWidget *parent, const QString &caption
#endif
}
+QString WiresharkFileDialog::selectedNativePath() const
+{
+ if (selectedFiles().isEmpty()) {
+ // The API implies this can't happen
+ return QString();
+ }
+ return QDir::toNativeSeparators(selectedFiles().at(0));
+}
+
QString WiresharkFileDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, Options options)
{
#ifdef Q_OS_WIN
@@ -52,7 +61,7 @@ QString WiresharkFileDialog::getExistingDirectory(QWidget *parent, const QString
#ifdef Q_OS_WIN
revert_thread_per_monitor_v2_awareness(da_ctx);
#endif
- return ed;
+ return QDir::toNativeSeparators(ed);
}
QString WiresharkFileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
@@ -64,7 +73,7 @@ QString WiresharkFileDialog::getOpenFileName(QWidget *parent, const QString &cap
#ifdef Q_OS_WIN
revert_thread_per_monitor_v2_awareness(da_ctx);
#endif
- return ofn;
+ return QDir::toNativeSeparators(ofn);
}
QString WiresharkFileDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
@@ -76,5 +85,5 @@ QString WiresharkFileDialog::getSaveFileName(QWidget *parent, const QString &cap
#ifdef Q_OS_WIN
revert_thread_per_monitor_v2_awareness(da_ctx);
#endif
- return sfn;
+ return QDir::toNativeSeparators(sfn);
}
diff --git a/ui/qt/widgets/wireshark_file_dialog.h b/ui/qt/widgets/wireshark_file_dialog.h
index 43ac67a6..7d452b3d 100644
--- a/ui/qt/widgets/wireshark_file_dialog.h
+++ b/ui/qt/widgets/wireshark_file_dialog.h
@@ -15,6 +15,11 @@
/**
* @brief The WiresharkFileDialog class
*
+ * Qt uses '/' as a universal path separator and converts to native path
+ * separators, i.e., '\' on Windows, only immediately before displaying a
+ * path to a user. This class can return the path with native path
+ * separators.
+ *
* Qt <= 5.9 supports setting old (Windows 8.1) per-monitor DPI awareness
* via Qt:AA_EnableHighDpiScaling. We do this in main.cpp. In order for
* native dialogs to be rendered correctly we need to set per-monitor
@@ -23,13 +28,15 @@
* we need to revert our thread context when we're done.
* The class functions below are simple wrappers around their QFileDialog
* equivalents that set PMv2 awareness before showing native dialogs on
- * Windows and resets it afterward.
+ * Windows and resets it afterward. They also return the result with native
+ * directory separators on Windows.
*/
class WiresharkFileDialog : public QFileDialog
{
public:
WiresharkFileDialog(QWidget *parent = nullptr, const QString &caption = QString(), const QString &directory = QString(), const QString &filter = QString());
+ QString selectedNativePath() const;
static QString getExistingDirectory(QWidget *parent = Q_NULLPTR, const QString &caption = QString(), const QString &dir = QString(), Options options = ShowDirsOnly);
static QString getOpenFileName(QWidget *parent = Q_NULLPTR, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = Q_NULLPTR, Options options = Options());
static QString getSaveFileName(QWidget *parent = Q_NULLPTR, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = Q_NULLPTR, Options options = Options());