Bug Summary

File:builds/wireshark/wireshark/ui/qt/packet_list.cpp
Warning:line 928, column 20
Potential leak of memory pointed to by 'drag_label'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name packet_list.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore5Compat -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE5COMPAT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=c++17 -fdeprecated-macro -ferror-limit 19 -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2026-04-11-100403-3641-1 -x c++ /builds/wireshark/wireshark/ui/qt/packet_list.cpp
1/* packet_list.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <[email protected]>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10#include <ui/qt/packet_list.h>
11
12#include "config.h"
13
14#include "file.h"
15
16#include <epan/epan.h>
17#include <epan/epan_dissect.h>
18
19#include <epan/column.h>
20#include <epan/expert.h>
21#include <epan/packet.h>
22#include <epan/prefs.h>
23#include <epan/proto.h>
24
25#include "ui/main_statusbar.h"
26#include "ui/packet_list_utils.h"
27#include "ui/preference_utils.h"
28#include "ui/recent.h"
29#include "ui/recent_utils.h"
30#include "ui/ws_ui_util.h"
31#include "ui/simple_dialog.h"
32#include <wsutil/utf8_entities.h>
33#include "ui/util.h"
34
35#include "wiretap/wtap_opttypes.h"
36#include "app/application_flavor.h"
37#include "wsutil/str_util.h"
38#include <wsutil/wslog.h>
39
40#include <epan/color_filters.h>
41
42#include <ui/qt/utils/color_utils.h>
43#include <ui/qt/widgets/overlay_scroll_bar.h>
44#include "proto_tree.h"
45#include <ui/qt/utils/qt_ui_utils.h>
46#include "main_application.h"
47#include <ui/qt/utils/data_printer.h>
48#include <ui/qt/utils/frame_information.h>
49#include <ui/qt/utils/profile_switcher.h>
50#include <ui/qt/utils/variant_pointer.h>
51#include <ui/qt/models/pref_models.h>
52#include <ui/qt/widgets/packet_list_header.h>
53#include <ui/qt/utils/wireshark_mime_data.h>
54#include <ui/qt/widgets/drag_label.h>
55#include <ui/qt/filter_action.h>
56#include <ui/qt/follow_stream_action.h>
57#include <ui/qt/decode_as_dialog.h>
58#include <ui/qt/wireshark_main_window.h>
59
60#include <QAction>
61#include <QActionGroup>
62#include <QClipboard>
63#include <QContextMenuEvent>
64#include <QtCore/qmath.h>
65#include <QElapsedTimer>
66#include <QFontMetrics>
67#include <QHeaderView>
68#include <QMessageBox>
69#include <QPainter>
70#include <QScreen>
71#include <QScrollBar>
72#include <QTabWidget>
73#include <QTextEdit>
74#include <QTimerEvent>
75#include <QTreeWidget>
76#include <QWindow>
77#include <QJsonObject>
78#include <QJsonDocument>
79
80#ifdef Q_OS_WIN
81#include "wsutil/file_util.h"
82#include <QSysInfo>
83#include <uxtheme.h>
84#endif
85
86// To do:
87// - Fix "apply as filter" behavior.
88// - Add colorize conversation.
89// - Use a timer to trigger automatic scrolling.
90
91// If we ever add the ability to open multiple capture files we might be
92// able to use something like QMap<capture_file *, PacketList *> to match
93// capture files against packet lists and models.
94static PacketList *gbl_cur_packet_list;
95
96const int max_comments_to_fetch_ = 20000000; // Arbitrary
97const int overlay_update_interval_ = 100; // 250; // Milliseconds.
98
99
100/*
101 * Given a frame_data structure, scroll to and select the row in the
102 * packet list corresponding to that frame. If there is no such
103 * row, return false, otherwise return true.
104 */
105bool
106packet_list_select_row_from_data(frame_data *fdata_needle)
107{
108 if (! gbl_cur_packet_list || ! gbl_cur_packet_list->model())
109 return false;
110 return gbl_cur_packet_list->selectRow(fdata_needle);
111}
112
113/*
114 * Given a field_info, select the field (which will scroll to it in
115 * the main ProtoTree, etc.) This is kind of an odd place for it,
116 * but we call this when performing Find Packet in lieu of changing the
117 * selected frame (the function above), because we found a match in the
118 * same frame as the currently selected one.
119 */
120bool
121packet_list_select_finfo(field_info *fi)
122{
123 if (! gbl_cur_packet_list || ! gbl_cur_packet_list->model())
124 return false;
125
126 if (fi) {
127 FieldInformation finfo(fi, gbl_cur_packet_list);
128 emit gbl_cur_packet_list->fieldSelected(&finfo);
129 } else {
130 emit gbl_cur_packet_list->fieldSelected(0);
131 }
132 return true;
133}
134
135void
136packet_list_clear(void)
137{
138 if (gbl_cur_packet_list) {
139 gbl_cur_packet_list->clear();
140 }
141}
142
143void
144packet_list_freeze(void)
145{
146 if (gbl_cur_packet_list) {
147 gbl_cur_packet_list->freeze();
148 }
149}
150
151void
152packet_list_thaw(void)
153{
154 if (gbl_cur_packet_list) {
155 gbl_cur_packet_list->thaw();
156 }
157
158 packets_bar_update();
159}
160
161/* Redraw the packet list *and* currently-selected detail */
162void
163packet_list_queue_draw(void)
164{
165 if (gbl_cur_packet_list)
166 gbl_cur_packet_list->redrawVisiblePackets();
167}
168
169void
170packet_list_recent_write_all(FILE *rf) {
171 if (!gbl_cur_packet_list)
172 return;
173
174 gbl_cur_packet_list->writeRecent(rf);
175}
176
177bool
178packet_list_multi_select_active(void)
179{
180 if (gbl_cur_packet_list) {
181 return gbl_cur_packet_list->multiSelectActive();
182 }
183 return false;
184}
185
186#define MIN_COL_WIDTH_STR"MMMMMM" "MMMMMM"
187
188PacketList::PacketList(QWidget *parent) :
189 QTreeView(parent),
190 proto_tree_(nullptr),
191 cap_file_(nullptr),
192 ctx_column_(-1),
193 overlay_timer_id_(0),
194 create_near_overlay_(true),
195 create_far_overlay_(true),
196 mouse_pressed_at_(QModelIndex()),
197 capture_in_progress_(false),
198 tail_at_end_(0),
199 columns_changed_(false),
200 set_column_visibility_(false),
201 set_style_sheet_(false),
202 frozen_current_row_(QModelIndex()),
203 frozen_selected_rows_(QModelIndexList()),
204 cur_history_(-1),
205 in_history_(false),
206 finfo_array(nullptr),
207 profile_switcher_(nullptr)
208{
209 setItemsExpandable(false);
210 setRootIsDecorated(false);
211 setSortingEnabled(prefs.gui_packet_list_sortable);
212 setUniformRowHeights(true);
213 setFocusPolicy(Qt::StrongFocus);
214
215#ifdef Q_OS_MAC
216 setAttribute(Qt::WA_MacShowFocusRect, true);
217#endif
218
219 verticalScrollBar()->setFocusPolicy(Qt::NoFocus);
220 horizontalScrollBar()->setFocusPolicy(Qt::NoFocus);
221
222 packet_list_header_ = new PacketListHeader(header()->orientation());
223 connect(packet_list_header_, &PacketListHeader::resetColumnWidth, this, &PacketList::setRecentColumnWidth);
224 connect(packet_list_header_, &PacketListHeader::updatePackets, this, &PacketList::updatePackets);
225 connect(packet_list_header_, &PacketListHeader::showColumnPreferences, this, &PacketList::showProtocolPreferences);
226 connect(packet_list_header_, &PacketListHeader::editColumn, this, &PacketList::editColumn);
227 connect(packet_list_header_, &PacketListHeader::columnsChanged, this, &PacketList::columnsChanged);
228 setHeader(packet_list_header_);
229
230 header()->setFirstSectionMovable(true);
231
232 setSelectionMode(QAbstractItemView::ExtendedSelection);
233
234 // Shrink down to a small but nonzero size in the main splitter.
235 int one_em = fontMetrics().height();
236 setMinimumSize(one_em, one_em);
237
238 overlay_sb_ = new OverlayScrollBar(Qt::Vertical, this);
239 setVerticalScrollBar(overlay_sb_);
240
241 header()->setSortIndicator(-1, Qt::AscendingOrder);
242
243 packet_list_model_ = new PacketListModel(this, cap_file_);
244 setModel(packet_list_model_);
245
246 Q_ASSERT(gbl_cur_packet_list == Q_NULLPTR)((gbl_cur_packet_list == nullptr) ? static_cast<void>(0
) : qt_assert("gbl_cur_packet_list == Q_NULLPTR", "ui/qt/packet_list.cpp"
, 246))
;
247 gbl_cur_packet_list = this;
248
249 connect(packet_list_model_, &PacketListModel::goToPacket, this, [=](int packet) { goToPacket(packet); });
250 connect(mainApp, &MainApplication::addressResolutionChanged, this, &PacketList::redrawVisiblePacketsDontSelectCurrent);
251 connect(mainApp, &MainApplication::columnDataChanged, this, &PacketList::redrawVisiblePacketsDontSelectCurrent);
252 connect(mainApp, &MainApplication::preferencesChanged, this, [=]() {
253 /* The pref is a uint but QCache maxCost is a signed int (/
254 * qsizetype in Qt 6). Note that QAbstractItemModel row numbers
255 * are ints, not unsigned ints, so we're limited to INT_MAX
256 * rows anyway.
257 */
258 PacketListRecord::setMaxCache(prefs.gui_packet_list_cached_rows_max > INT_MAX2147483647 ? INT_MAX2147483647 : prefs.gui_packet_list_cached_rows_max);
259 if ((bool) (prefs.gui_packet_list_sortable) != isSortingEnabled()) {
260 setSortingEnabled(prefs.gui_packet_list_sortable);
261 }
262 });
263
264 connect(header(), &QHeaderView::sectionResized, this, &PacketList::sectionResized);
265 connect(header(), &QHeaderView::sectionMoved, this, &PacketList::sectionMoved);
266
267 connect(verticalScrollBar(), &QScrollBar::actionTriggered, this, &PacketList::vScrollBarActionTriggered);
268}
269
270PacketList::~PacketList()
271{
272 if (finfo_array)
273 {
274 g_ptr_array_free(finfo_array, true);
275 }
276}
277
278void PacketList::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint)
279{
280 /* QAbstractItemView doesn't have a way to indicate "auto scroll, but
281 * only vertically." So just restore the horizontal scroll value whenever
282 * it scrolls.
283 */
284 setUpdatesEnabled(false);
285 int horizVal = horizontalScrollBar()->value();
286 QTreeView::scrollTo(index, hint);
287 horizontalScrollBar()->setValue(horizVal);
288 setUpdatesEnabled(true);
289}
290
291void PacketList::colorsChanged()
292{
293 const QString c_active = "active";
294 const QString c_inactive = "!active";
295
296 QString flat_style_format =
297 "QTreeView::item:selected:%1 {"
298 " color: %2;"
299 " background-color: %3;"
300 "}";
301
302 QString gradient_style_format =
303 "QTreeView::item:selected:%1 {"
304 " color: %2;"
305 " background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1 stop: 0 %4, stop: 0.5 %3, stop: 1 %4);"
306 "}";
307
308 QString hover_style = QStringLiteral((QString(QtPrivate::qMakeStringPrivate(u"" "QTreeView:item:hover {"
" background-color: %1;" " color: palette(text);" "}")))
309 "QTreeView:item:hover {"(QString(QtPrivate::qMakeStringPrivate(u"" "QTreeView:item:hover {"
" background-color: %1;" " color: palette(text);" "}")))
310 " background-color: %1;"(QString(QtPrivate::qMakeStringPrivate(u"" "QTreeView:item:hover {"
" background-color: %1;" " color: palette(text);" "}")))
311 " color: palette(text);"(QString(QtPrivate::qMakeStringPrivate(u"" "QTreeView:item:hover {"
" background-color: %1;" " color: palette(text);" "}")))
312 "}")(QString(QtPrivate::qMakeStringPrivate(u"" "QTreeView:item:hover {"
" background-color: %1;" " color: palette(text);" "}")))
.arg(ColorUtils::hoverBackground().name(QColor::HexArgb));
313
314 QString active_style = QString();
315 QString inactive_style = QString();
316
317 if (prefs.gui_active_style == COLOR_STYLE_DEFAULT0) {
318 // ACTIVE = Default
319 } else if (prefs.gui_active_style == COLOR_STYLE_FLAT1) {
320 // ACTIVE = Flat
321 QColor foreground = ColorUtils::fromColorT(prefs.gui_active_fg);
322 QColor background = ColorUtils::fromColorT(prefs.gui_active_bg);
323
324 active_style = flat_style_format.arg(
325 c_active,
326 foreground.name(),
327 background.name());
328 } else if (prefs.gui_active_style == COLOR_STYLE_GRADIENT2) {
329 // ACTIVE = Gradient
330 QColor foreground = ColorUtils::fromColorT(prefs.gui_active_fg);
331 QColor background1 = ColorUtils::fromColorT(prefs.gui_active_bg);
332 QColor background2 = QColor::fromRgb(ColorUtils::alphaBlend(foreground, background1, COLOR_STYLE_ALPHA0.25));
333
334 active_style = gradient_style_format.arg(
335 c_active,
336 foreground.name(),
337 background1.name(),
338 background2.name());
339 }
340
341 // INACTIVE style sheet settings
342 if (prefs.gui_inactive_style == COLOR_STYLE_DEFAULT0) {
343 // INACTIVE = Default
344 } else if (prefs.gui_inactive_style == COLOR_STYLE_FLAT1) {
345 // INACTIVE = Flat
346 QColor foreground = ColorUtils::fromColorT(prefs.gui_inactive_fg);
347 QColor background = ColorUtils::fromColorT(prefs.gui_inactive_bg);
348
349 inactive_style = flat_style_format.arg(
350 c_inactive,
351 foreground.name(),
352 background.name());
353 } else if (prefs.gui_inactive_style == COLOR_STYLE_GRADIENT2) {
354 // INACTIVE = Gradient
355 QColor foreground = ColorUtils::fromColorT(prefs.gui_inactive_fg);
356 QColor background1 = ColorUtils::fromColorT(prefs.gui_inactive_bg);
357 QColor background2 = QColor::fromRgb(ColorUtils::alphaBlend(foreground, background1, COLOR_STYLE_ALPHA0.25));
358
359 inactive_style = gradient_style_format.arg(
360 c_inactive,
361 foreground.name(),
362 background1.name(),
363 background2.name());
364 }
365
366 // Set the style sheet
367 set_style_sheet_ = true;
368 if(prefs.gui_packet_list_hover_style) {
369 setStyleSheet(active_style + inactive_style + hover_style);
370 } else {
371 setStyleSheet(active_style + inactive_style);
372 }
373 set_style_sheet_ = false;
374#if \
375 ( \
376 (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 5, 4)((6<<16)|(5<<8)|(4)) && QT_VERSION((6<<16)|(4<<8)|(2)) < QT_VERSION_CHECK(6, 6, 0)((6<<16)|(6<<8)|(0))) \
377 || (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 6, 1)((6<<16)|(6<<8)|(1))) \
378 )
379 // https://bugreports.qt.io/browse/QTBUG-122109
380 // Affects Qt 6.5.4 and later, 6.6.1 and later.
381 // When setting the style sheet, all visible sections are set
382 // to the new minimum DefaultSectionSize (even if it hasn't
383 // changed.) So make sure the new widths aren't saved to recent
384 // and then restore from recent.
385 applyRecentColumnWidths();
386 setColumnVisibility();
387#endif
388}
389
390QString PacketList::joinSummaryRow(QStringList col_parts, int row, SummaryCopyType type)
391{
392 QString copy_text;
393 switch (type) {
394 case CopyAsCSV:
395 copy_text = "\"";
396 copy_text += col_parts.join("\",\"");
397 copy_text += "\"";
398 break;
399 case CopyAsYAML:
400 copy_text = "----\n";
401 copy_text += QStringLiteral("# Packet %1 from %2\n")(QString(QtPrivate::qMakeStringPrivate(u"" "# Packet %1 from %2\n"
)))
.arg(row).arg(cap_file_->filename);
402 copy_text += "- ";
403 copy_text += col_parts.join("\n- ");
404 copy_text += "\n";
405 break;
406 case CopyAsText:
407 default:
408 copy_text = col_parts.join("\t");
409 }
410
411 return copy_text;
412}
413
414void PacketList::drawRow (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
415{
416 QTreeView::drawRow(painter, option, index);
417
418 if (prefs.gui_packet_list_separator) {
419 QRect rect = visualRect(index);
420
421 painter->setPen(QColor(Qt::white));
422 painter->drawLine(0, rect.y() + rect.height() - 1, width(), rect.y() + rect.height() - 1);
423 }
424}
425
426void PacketList::setProtoTree (ProtoTree *proto_tree) {
427 proto_tree_ = proto_tree;
428
429 connect(proto_tree_, &ProtoTree::goToPacket, this, [=](int packet) { goToPacket(packet); });
430 connect(proto_tree_, &ProtoTree::relatedFrame,
431 &related_packet_delegate_, &RelatedPacketDelegate::addRelatedFrame);
432}
433
434bool PacketList::uniqueSelectActive()
435{
436 return selectionModel()->selectedRows(0).count() == 1 ? true : false;
437}
438
439bool PacketList::multiSelectActive()
440{
441 return selectionModel()->selectedRows(0).count() > 1 ? true : false;
442}
443
444QList<int> PacketList::selectedRows(bool useFrameNum)
445{
446 QList<int> rows;
447 if (selectionModel() && selectionModel()->hasSelection())
448 {
449 foreach (QModelIndex idx, selectionModel()->selectedRows(0))for (auto _container_449 = QtPrivate::qMakeForeachContainer(selectionModel
()->selectedRows(0)); _container_449.i != _container_449.e
; ++_container_449.i) if (QModelIndex idx = *_container_449.i
; false) {} else
450 {
451 if (idx.isValid())
452 {
453 if (! useFrameNum)
454 rows << idx.row();
455 else if (useFrameNum)
456 {
457 frame_data * frame = getFDataForRow(idx.row());
458 if (frame)
459 rows << frame->num;
460 }
461 }
462 }
463 }
464 else if (currentIndex().isValid())
465 {
466 //
467 // XXX - will we ever have a current index but not a selection
468 // model?
469 //
470 if (! useFrameNum)
471 rows << currentIndex().row();
472 else
473 {
474 frame_data *frame = getFDataForRow(currentIndex().row());
475 if (frame)
476 rows << frame->num;
477 }
478 }
479
480 return rows;
481}
482
483void PacketList::selectionChanged (const QItemSelection & selected, const QItemSelection & deselected)
484{
485 QTreeView::selectionChanged(selected, deselected);
486
487 if (!cap_file_) return;
488
489 int row = -1;
490 static bool multiSelect = false;
491
492 if (selectionModel())
493 {
494 QModelIndexList selRows = selectionModel()->selectedRows(0);
495 if (selRows.count() > 1)
496 {
497 QList<int> rows;
498 foreach (QModelIndex idx, selRows)for (auto _container_498 = QtPrivate::qMakeForeachContainer(selRows
); _container_498.i != _container_498.e; ++_container_498.i) if
(QModelIndex idx = *_container_498.i; false) {} else
499 {
500 if (idx.isValid())
501 rows << idx.row();
502 }
503
504 emit framesSelected(rows);
505 emit fieldSelected(0);
506 cf_unselect_packet(cap_file_);
507
508 /* We have to repaint the content while changing state, as some delegates react to multi-select */
509 if (! multiSelect)
510 {
511 related_packet_delegate_.clear();
512 viewport()->update();
513 }
514
515 multiSelect = true;
516
517 return;
518 }
519 else if (selRows.count() > 0 && selRows.at(0).isValid())
520 {
521 multiSelect = false;
522 row = selRows.at(0).row();
523 }
524
525 /* Handling empty selection */
526 if (selRows.count() <= 0)
527 {
528 /* Nothing selected, but multiSelect is still active */
529 if (multiSelect)
530 {
531 multiSelect = false;
532 if (currentIndex().isValid())
533 {
534 selectionModel()->select(currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
535 return;
536 }
537 }
538 /* Nothing selected, so in WS <= 3.0 nothing was indicated as well */
539 else if (currentIndex().isValid())
540 {
541 setCurrentIndex(QModelIndex());
542 }
543 }
544 }
545
546 if (row < 0 || !packet_list_model_)
547 cf_unselect_packet(cap_file_);
548 else {
549 frame_data * fdata = packet_list_model_->getRowFdata(row);
550 cf_select_packet(cap_file_, fdata);
551 }
552
553 if (!in_history_ && cap_file_->current_frame) {
554 cur_history_++;
555 selection_history_.resize(cur_history_);
556 selection_history_.append(cap_file_->current_frame->num);
557 }
558
559 related_packet_delegate_.clear();
560
561 // The previous dissection state has been invalidated by cf_select_packet
562 // above, receivers must clear the previous state and apply the updated one.
563 emit framesSelected(QList<int>() << row);
564
565 if (!cap_file_->edt) {
566 viewport()->update();
567 emit fieldSelected(0);
568 return;
569 }
570
571 if (cap_file_->edt->tree) {
572 packet_info *pi = &cap_file_->edt->pi;
573 related_packet_delegate_.setCurrentFrame(pi->num);
574 conversation_t *conv = find_conversation_pinfo_ro(pi, 0);
575 if (conv) {
576 related_packet_delegate_.setConversation(conv);
577 }
578 viewport()->update();
579 }
580
581 if (cap_file_->search_in_progress) {
582 field_info *fi = NULL__null;
583
584 if (cap_file_->string && cap_file_->decode_data) {
585 // The tree where the target string matched one of the labels was discarded in
586 // match_protocol_tree() so we have to search again in the latest tree.
587 fi = cf_find_string_protocol_tree(cap_file_, cap_file_->edt->tree);
588 } else if (cap_file_->search_len != 0) {
589 // Find the finfo that corresponds to our byte.
590 // The match can span multiple fields (and a single byte can
591 // match more than one field.) Our behavior is to find the last
592 // field in the tree (so hopefully spanning fewer bytes) that
593 // matches the last byte in the search match.
594 // (regex search can find a zero length match not at the
595 // start of the frame if lookbehind is used, but
596 // proto_find_field_from_offset doesn't match such a field
597 // and it's not clear which field we would want to match.)
598 fi = proto_find_field_from_offset(cap_file_->edt->tree, cap_file_->search_pos + cap_file_->search_len - 1,
599 cap_file_->edt->tvb);
600 }
601
602 if (fi) {
603 FieldInformation finfo(fi, this);
604 emit fieldSelected(&finfo);
605 } else {
606 emit fieldSelected(0);
607 }
608 } else if (proto_tree_) {
609 proto_tree_->restoreSelectedField();
610 }
611}
612
613void PacketList::contextMenuEvent(QContextMenuEvent *event)
614{
615 const char *module_name = NULL__null;
616
617 if (finfo_array)
618 {
619 g_ptr_array_free(finfo_array, true);
620 finfo_array = NULL__null;
621 }
622
623 QModelIndex ctxIndex = indexAt(event->pos());
624
625 if (selectionModel() && selectionModel()->selectedRows(0).count() > 1)
626 selectionModel()->select(ctxIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
627
628 // frameData will be owned by one of the submenus, see below.
629 FrameInformation * frameData =
630 new FrameInformation(new CaptureFile(this, cap_file_), packet_list_model_->getRowFdata(ctxIndex.row()));
631
632 QMenu * ctx_menu = new QMenu(this);
633 ctx_menu->setAttribute(Qt::WA_DeleteOnClose);
634 // XXX We might want to reimplement setParent() and fill in the context
635 // menu there.
636 ctx_menu->addAction(window()->findChild<QAction *>("actionEditMarkSelected"));
637 ctx_menu->addAction(window()->findChild<QAction *>("actionEditIgnoreSelected"));
638 ctx_menu->addAction(window()->findChild<QAction *>("actionEditSetTimeReference"));
639 ctx_menu->addAction(window()->findChild<QAction *>("actionEditTimeShift"));
640 ctx_menu->addMenu(window()->findChild<QMenu *>("menuPacketComment"));
641
642 ctx_menu->addSeparator();
643
644 // Code for custom context menus from Lua's register_packet_menu()
645 MainWindow * mainWindow = mainApp->mainWindow();
646 // N.B., will only call for a single frame selection,
647 if (cap_file_ && cap_file_->edt && cap_file_->edt->tree) {
648 finfo_array = proto_all_finfos(cap_file_->edt->tree);
649 if (mainWindow) {
650 bool insertedPacketMenu = mainWindow->addPacketMenus(ctx_menu, finfo_array);
651 if (insertedPacketMenu) {
652 ctx_menu->addSeparator();
653 }
654 }
655 }
656
657 ctx_menu->addAction(window()->findChild<QAction *>("actionViewEditResolvedName"));
658 ctx_menu->addSeparator();
659
660 QString selectedfilter = getFilterFromRowAndColumn(currentIndex());
661
662 if (! hasFocus() && cap_file_ && cap_file_->finfo_selected) {
663 char *tmp_field = proto_construct_match_selected_string(cap_file_->finfo_selected, cap_file_->edt);
664 selectedfilter = QString(tmp_field);
665 wmem_free(NULL__null, tmp_field);
666 }
667
668 bool have_filter_expr = !selectedfilter.isEmpty();
669 ctx_menu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionApply, selectedfilter, have_filter_expr, ctx_menu));
670 ctx_menu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionPrepare, selectedfilter, have_filter_expr, ctx_menu));
671
672 const char *conv_menu_name = "menuConversationFilter";
673 QMenu * main_menu_item = window()->findChild<QMenu *>(conv_menu_name);
674 conv_menu_.setTitle(main_menu_item->title());
675 conv_menu_.setObjectName(conv_menu_name);
676 ctx_menu->addMenu(&conv_menu_);
677
678 const char *colorize_menu_name = "menuColorizeConversation";
679 main_menu_item = window()->findChild<QMenu *>(colorize_menu_name);
680 colorize_menu_.setTitle(main_menu_item->title());
681 colorize_menu_.setObjectName(colorize_menu_name);
682 ctx_menu->addMenu(&colorize_menu_);
683
684 QMenu * submenu;
685 main_menu_item = window()->findChild<QMenu *>("menuSCTP");
686 if (main_menu_item) {
687 submenu = new QMenu(main_menu_item->title(), ctx_menu);
688 ctx_menu->addMenu(submenu);
689 submenu->addAction(window()->findChild<QAction *>("actionSCTPAnalyseThisAssociation"));
690 submenu->addAction(window()->findChild<QAction *>("actionSCTPShowAllAssociations"));
691 submenu->addAction(window()->findChild<QAction *>("actionSCTPFilterThisAssociation"));
692 }
693
694 main_menu_item = window()->findChild<QMenu *>("menuFollow");
695 if (main_menu_item) {
696 submenu = new QMenu(main_menu_item->title(), ctx_menu);
697 ctx_menu->addMenu(submenu);
698 foreach (FollowStreamAction *follow_action, main_menu_item->findChildren<FollowStreamAction *>())for (auto _container_698 = QtPrivate::qMakeForeachContainer(main_menu_item
->findChildren<FollowStreamAction *>()); _container_698
.i != _container_698.e; ++_container_698.i) if (FollowStreamAction
*follow_action = *_container_698.i; false) {} else
{
699 /* XXX: We could, like the prefs above, walk the protocols/layers
700 * and add the follow actions in the order they appear in the packet.
701 */
702 if (follow_action->isEnabled()) {
703 submenu->addAction(follow_action);
704 }
705 }
706 }
707
708 ctx_menu->addSeparator();
709
710 main_menu_item = window()->findChild<QMenu *>("menuEditCopy");
711 submenu = new QMenu(main_menu_item->title(), ctx_menu);
712 submenu->setToolTipsVisible(true);
713 ctx_menu->addMenu(submenu);
714
715 QAction * action = submenu->addAction(tr("Summary as Text"));
716 action->setData(CopyAsText);
717 connect(action, &QAction::triggered, this, &PacketList::copySummary);
718 action = submenu->addAction(tr("…as CSV"));
719 action->setData(CopyAsCSV);
720 connect(action, &QAction::triggered, this, &PacketList::copySummary);
721 action = submenu->addAction(tr("…as YAML"));
722 action->setData(CopyAsYAML);
723 connect(action, &QAction::triggered, this, &PacketList::copySummary);
724 action = submenu->addAction(tr("…as HTML"));
725 action->setData(CopyAsHTML);
726 connect(action, &QAction::triggered, this, &PacketList::copySummary);
727 submenu->addSeparator();
728
729 submenu->addAction(window()->findChild<QAction *>("actionEditCopyAsFilter"));
730 submenu->addSeparator();
731
732 QActionGroup * copyEntries = DataPrinter::copyActions(this, frameData);
733 submenu->addActions(copyEntries->actions());
734 copyEntries->setParent(submenu);
735 frameData->setParent(submenu);
736
737 if (application_flavor_is_wireshark()) {
738 /* i.e., Wireshark only */
739 ctx_menu->addSeparator();
740 QMenu *proto_prefs_menus = new QMenu(ProtocolPreferencesMenu::tr("Protocol Preferences"), ctx_menu);
741
742 if (cap_file_ && cap_file_->edt && cap_file_->edt->tree) {
743 QList<QString> added_proto_prefs;
744 // N.B. finfo_array will be assigned above
745 for (unsigned i = 0; i < finfo_array->len; i++) {
746 field_info *fi = (field_info *)g_ptr_array_index (finfo_array, i)((finfo_array)->pdata)[i];
747 const header_field_info *hfinfo = fi->hfinfo;
748
749 if (prefs_is_registered_protocol(hfinfo->abbrev)) {
750 if (hfinfo->parent == -1) {
751 module_name = hfinfo->abbrev;
752 } else {
753 module_name = proto_registrar_get_abbrev(hfinfo->parent);
754 }
755
756 if (added_proto_prefs.contains(module_name)) {
757 continue;
758 }
759
760 ProtocolPreferencesMenu *proto_prefs_menu = new ProtocolPreferencesMenu(hfinfo->name, module_name, proto_prefs_menus);
761
762 connect(proto_prefs_menu, &ProtocolPreferencesMenu::showProtocolPreferences,
763 this, &PacketList::showProtocolPreferences);
764 connect(proto_prefs_menu, SIGNAL(editProtocolPreference(pref_t*,module_t*))qFlagLocation("2" "editProtocolPreference(pref_t*,module_t*)"
"\0" "ui/qt/packet_list.cpp" ":" "764")
,
765 this, SIGNAL(editProtocolPreference(pref_t*,module_t*))qFlagLocation("2" "editProtocolPreference(pref_t*,module_t*)"
"\0" "ui/qt/packet_list.cpp" ":" "765")
);
766
767 proto_prefs_menus->addMenu(proto_prefs_menu);
768 added_proto_prefs << module_name;
769 }
770 }
771 }
772 ctx_menu->addMenu(proto_prefs_menus);
773 action = ctx_menu->addAction(tr("Decode As…"));
774 action->setProperty("create_new", QVariant(true));
775 connect(action, &QAction::triggered, this, &PacketList::ctxDecodeAsDialog);
776 // "Print" not ported intentionally
777 action = window()->findChild<QAction *>("actionViewShowPacketInNewWindow");
778 ctx_menu->addAction(action);
779 }
780
781 // Set menu sensitivity for the current column and set action data.
782 if (frameData)
783 emit framesSelected(QList<int>() << frameData->frameNum());
784 else
785 emit framesSelected(QList<int>());
786
787 ctx_menu->popup(event->globalPos());
788}
789
790void PacketList::ctxDecodeAsDialog()
791{
792 QAction *da_action = qobject_cast<QAction*>(sender());
793 if (! da_action)
794 return;
795 bool create_new = da_action->property("create_new").toBool();
796
797 DecodeAsDialog *da_dialog = new DecodeAsDialog(this, cap_file_, create_new);
798 connect(da_dialog, &DecodeAsDialog::destroyed, mainApp, &MainApplication::flushAppSignals);
799 da_dialog->setWindowModality(Qt::ApplicationModal);
800 da_dialog->setAttribute(Qt::WA_DeleteOnClose);
801 da_dialog->show();
802}
803
804void PacketList::timerEvent(QTimerEvent *event)
805{
806 if (event->timerId() == overlay_timer_id_) {
807 if (!capture_in_progress_ && model() != nullptr) {
808 if (create_near_overlay_) drawNearOverlay();
809 if (create_far_overlay_) drawFarOverlay();
810 }
811 } else {
812 QTreeView::timerEvent(event);
813 }
814}
815
816void PacketList::paintEvent(QPaintEvent *event)
817{
818 // XXX This is overkill, but there are quite a few events that
819 // require a new overlay, e.g. page up/down, scrolling, column
820 // resizing, etc.
821 create_near_overlay_ = true;
822 QTreeView::paintEvent(event);
823}
824
825void PacketList::mousePressEvent(QMouseEvent *event)
826{
827 QTreeView::mousePressEvent(event);
828
829 QModelIndex curIndex = indexAt(event->pos());
830 mouse_pressed_at_ = curIndex;
831
832 bool midButton = (event->buttons() & Qt::MiddleButton) == Qt::MiddleButton;
833 if (midButton && cap_file_ && packet_list_model_)
834 {
835 packet_list_model_->toggleFrameMark(QModelIndexList() << curIndex);
836
837 // Make sure the packet list's frame.marked related field text is updated.
838 redrawVisiblePackets();
839
840 create_far_overlay_ = true;
841 packets_bar_update();
842 }
843}
844
845void PacketList::mouseReleaseEvent(QMouseEvent *event) {
846 QTreeView::mouseReleaseEvent(event);
847
848 mouse_pressed_at_ = QModelIndex();
849}
850
851void PacketList::mouseMoveEvent (QMouseEvent *event)
852{
853 QModelIndex curIndex = indexAt(event->pos());
854 if (event->buttons() & Qt::LeftButton && curIndex.isValid() && curIndex == mouse_pressed_at_)
1
Assuming the condition is true
2
Taking true branch
855 {
856 ctx_column_ = curIndex.column();
857 QMimeData * mimeData = new QMimeData();
858 DragLabel * drag_label = nullptr;
859
860 QString filter = getFilterFromRowAndColumn(curIndex);
861 QList<int> rows = selectedRows();
862 if (rows.count() > 1)
3
Assuming the condition is false
4
Taking false branch
863 {
864 QStringList entries;
865 foreach (int row, rows)for (auto _container_865 = QtPrivate::qMakeForeachContainer(rows
); _container_865.i != _container_865.e; ++_container_865.i) if
(int row = *_container_865.i; false) {} else
866 {
867 QModelIndex idx = model()->index(row, 0);
868 if (! idx.isValid())
869 continue;
870
871 QString entry = createSummaryText(idx, CopyAsText);
872 entries << entry;
873 }
874
875 if (entries.count() > 0)
876 mimeData->setText(entries.join("\n"));
877 }
878 else if (! filter.isEmpty())
5
Assuming the condition is true
6
Taking true branch
879 {
880 QString abbrev;
881 QString name = model()->headerData(curIndex.column(), header()->orientation()).toString();
882
883 if (! filter.isEmpty())
7
Assuming the condition is false
8
Taking false branch
884 {
885 abbrev = filter.left(filter.indexOf(' '));
886 }
887 else
888 {
889 filter = model()->data(curIndex).toString().toLower();
890 abbrev = filter;
891 }
892
893 mimeData->setText(filter);
894
895 QJsonObject filterData;
896 filterData["filter"] = filter;
897 filterData["name"] = abbrev;
898 filterData["description"] = name;
899
900 mimeData->setData(WiresharkMimeData::DisplayFilterMimeType, QJsonDocument(filterData).toJson());
901 drag_label = new DragLabel(QStringLiteral("%1\n%2")(QString(QtPrivate::qMakeStringPrivate(u"" "%1\n%2"))).arg(name, abbrev), this);
9
Memory is allocated
902 }
903 else
904 {
905 QString text = model()->data(curIndex).toString();
906 if (! text.isEmpty())
907 mimeData->setText(text);
908 }
909
910 if (mimeData->hasText() || mimeData->hasFormat(WiresharkMimeData::DisplayFilterMimeType))
10
Assuming the condition is false
11
Assuming the condition is false
12
Taking false branch
911 {
912 QDrag * drag = new QDrag(this);
913 drag->setMimeData(mimeData);
914 if (drag_label)
915 {
916 qreal dpr = window()->windowHandle()->devicePixelRatio();
917 QPixmap pixmap= QPixmap(drag_label->size() * dpr);
918 pixmap.setDevicePixelRatio(dpr);
919 drag_label->render(&pixmap);
920 drag->setPixmap(pixmap);
921 delete drag_label;
922 }
923
924 drag->exec(Qt::CopyAction);
925 }
926 else
927 {
928 delete mimeData;
13
Potential leak of memory pointed to by 'drag_label'
929 }
930 }
931}
932
933void PacketList::keyPressEvent(QKeyEvent *event)
934{
935 QTreeView::keyPressEvent(event);
936
937 if (event->matches(QKeySequence::Copy))
938 {
939 QStringList content, htmlContent;
940 if (model() && selectionModel() && selectionModel()->hasSelection())
941 {
942 QList<int> rows;
943 QModelIndexList selRows = selectionModel()->selectedRows(0);
944 foreach(QModelIndex row, selRows)for (auto _container_944 = QtPrivate::qMakeForeachContainer(selRows
); _container_944.i != _container_944.e; ++_container_944.i) if
(QModelIndex row = *_container_944.i; false) {} else
945 rows.append(row.row());
946
947 QStringList hdr_parts;
948 QList<int> align_parts, size_parts;
949
950 switch (prefs.gui_packet_list_copy_format_options_for_keyboard_shortcut) {
951 case COPY_FORMAT_TEXT:
952 case COPY_FORMAT_HTML:
953 if (prefs.gui_packet_list_copy_text_with_aligned_columns) {
954 hdr_parts = createHeaderPartsForAligned();
955 align_parts = createAlignmentPartsForAligned();
956 size_parts = createSizePartsForAligned(false, hdr_parts, rows);
957 }
958 if (prefs.gui_packet_list_copy_format_options_for_keyboard_shortcut == COPY_FORMAT_HTML) {
959 htmlContent << createDefaultStyleForHtml();
960 htmlContent << createOpeningTagForHtml();
961 }
962 break;
963 case COPY_FORMAT_CSV:
964 case COPY_FORMAT_YAML:
965 break;
966 }
967
968 QList<QStringList> entries;
969 foreach(int row, rows)for (auto _container_969 = QtPrivate::qMakeForeachContainer(rows
); _container_969.i != _container_969.e; ++_container_969.i) if
(int row = *_container_969.i; false) {} else
970 {
971 QModelIndex idx = model()->index(row, 0);
972 if (! idx.isValid())
973 continue;
974
975 switch (prefs.gui_packet_list_copy_format_options_for_keyboard_shortcut) {
976 case COPY_FORMAT_TEXT:
977 case COPY_FORMAT_HTML:
978 if (prefs.gui_packet_list_copy_text_with_aligned_columns)
979 content << createSummaryForAligned(idx, align_parts, size_parts);
980 else
981 content << createSummaryText(idx, CopyAsText);
982 if (prefs.gui_packet_list_copy_format_options_for_keyboard_shortcut == COPY_FORMAT_HTML)
983 htmlContent << createSummaryForHtml(idx);
984 break;
985 case COPY_FORMAT_CSV:
986 content << createSummaryText(idx, CopyAsCSV);
987 break;
988 case COPY_FORMAT_YAML:
989 content << createSummaryText(idx, CopyAsYAML);
990 break;
991 }
992 }
993 }
994
995 if (prefs.gui_packet_list_copy_format_options_for_keyboard_shortcut == COPY_FORMAT_HTML) {
996 // htmlContent will never be empty as they will always have style and table tags
997 QMimeData *mimeData = new QMimeData;
998 htmlContent << createClosingTagForHtml();
999 mimeData->setHtml(htmlContent.join('\n'));
1000 mimeData->setText(content.join('\n').append("\n"));
1001 mainApp->clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
1002 }
1003 else {
1004 if (content.count() > 0) {
1005 QString copy_text;
1006 if (prefs.gui_packet_list_copy_format_options_for_keyboard_shortcut == COPY_FORMAT_YAML) {
1007 copy_text = content.join("");
1008 }
1009 else {
1010 copy_text = content.join("\n");
1011 copy_text += "\n";
1012 }
1013 mainApp->clipboard()->setText(copy_text);
1014 }
1015 }
1016 }
1017}
1018
1019void PacketList::focusInEvent(QFocusEvent *event)
1020{
1021 QTreeView::focusInEvent(event);
1022
1023 if (event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason) {
1024 if (model() && model()->rowCount() > 0 && selectionModel()) {
1025 if (!selectionModel()->hasSelection()) {
1026 QModelIndex first = model()->index(0, 0);
1027 if (first.isValid()) {
1028 selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1029 setCurrentIndex(first);
1030 }
1031 }
1032
1033 // ALWAYS scroll to the current index if we have one
1034 if (currentIndex().isValid()) {
1035 scrollTo(currentIndex());
1036 }
1037 }
1038 }
1039}
1040
1041void PacketList::resizeEvent(QResizeEvent *event)
1042{
1043 create_near_overlay_ = true;
1044 create_far_overlay_ = true;
1045 QTreeView::resizeEvent(event);
1046}
1047
1048void PacketList::setColumnVisibility()
1049{
1050 set_column_visibility_ = true;
1051 for (unsigned i = 0; i < prefs.num_cols; i++) {
1052 setColumnHidden(i, get_column_visible(i) ? false : true);
1053 }
1054 setColumnDelegate();
1055 set_column_visibility_ = false;
1056}
1057
1058void PacketList::setColumnDelegate()
1059{
1060 for (unsigned i = 0; i < prefs.num_cols; i++) {
1061 setItemDelegateForColumn(i, nullptr); // Reset all delegates
1062 }
1063
1064 // Multi-color delegate takes precedence over related packet delegate (row stripes only, not scrollbar-only mode)
1065 if (prefs.gui_packet_list_multi_color_mode == PACKET_LIST_MULTI_COLOR_MODE_FULL ||
1066 prefs.gui_packet_list_multi_color_mode == PACKET_LIST_MULTI_COLOR_MODE_SHIFT_RIGHT) {
1067 for (unsigned i = 0; i < prefs.num_cols; i++) {
1068 if (get_column_visible(i)) {
1069 setItemDelegateForColumn(i, &multi_color_delegate_);
1070 }
1071 }
1072 }
1073 else if (prefs.gui_packet_list_show_related) {
1074 for (unsigned i = 0; i < prefs.num_cols; i++) {
1075 if (get_column_visible(i)) {
1076 setItemDelegateForColumn(i, &related_packet_delegate_);
1077 break; // Set the delegate only on the first visible column
1078 }
1079 }
1080 }
1081}
1082
1083void PacketList::setRecentColumnWidth(int col)
1084{
1085 int col_width = recent_get_column_width(col);
1086
1087 if (col_width < 1) {
1088 int fmt = get_column_format(col);
1089 const char *long_str = get_column_width_string(fmt, col);
1090
1091 QFontMetrics fm = QFontMetrics(mainApp->monospaceFont());
1092 if (long_str) {
1093 col_width = fm.horizontalAdvance(long_str);
1094 } else {
1095 col_width = fm.horizontalAdvance(MIN_COL_WIDTH_STR"MMMMMM");
1096 }
1097 // Custom delegate padding
1098 if (itemDelegateForColumn(col)) {
1099 QStyleOptionViewItem option;
1100#if QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0))
1101 initViewItemOption(&option);
1102#else
1103 option = viewOptions();
1104#endif
1105 // This is adding "how much width hinted for an empty index, plus
1106 // the decoration, plus any padding between the decoration and
1107 // normal display?" Many styles however have a non zero hint for
1108 // an empty index, so this isn't quite right. What we really want
1109 // is a size hint for an index whose data is the string above, and
1110 // to just use that for the width.
1111 col_width += itemDelegateForColumn(col)->sizeHint(option, QModelIndex()).width();
1112 }
1113 }
1114
1115 setColumnWidth(col, col_width);
1116}
1117
1118void PacketList::drawCurrentPacket()
1119{
1120 // XXX - Update for multi-select? If more than one packet is Selected,
1121 // this changes it so that only the Current packet is Selected.
1122 QModelIndex current_index = currentIndex();
1123 if (selectionModel() && current_index.isValid()) {
1124 selectionModel()->clearSelection();
1125 selectionModel()->setCurrentIndex(current_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
1126 }
1127}
1128
1129// Redraw the packet list and detail. Re-selects the current packet (causes
1130// the UI to scroll to that packet).
1131// Called from many places.
1132void PacketList::redrawVisiblePackets() {
1133 redrawVisiblePacketsDontSelectCurrent();
1134 drawCurrentPacket();
1135}
1136
1137// Redraw the packet list and detail.
1138// Does not scroll back to the selected packet.
1139void PacketList::redrawVisiblePacketsDontSelectCurrent() {
1140 packet_list_model_->invalidateAllColumnStrings();
1141}
1142
1143void PacketList::resetColumns()
1144{
1145 packet_list_model_->resetColumns();
1146}
1147
1148// Return true if we have a visible packet further along in the history.
1149bool PacketList::haveNextHistory(bool update_cur)
1150{
1151 if (selection_history_.size() < 1 || cur_history_ >= selection_history_.size() - 1) {
1152 return false;
1153 }
1154
1155 for (int i = cur_history_ + 1; i < selection_history_.size(); i++) {
1156 if (packet_list_model_->packetNumberToRow(selection_history_.at(i)) >= 0) {
1157 if (update_cur) {
1158 cur_history_ = i;
1159 }
1160 return true;
1161 }
1162 }
1163 return false;
1164}
1165
1166// Return true if we have a visible packet back in the history.
1167bool PacketList::havePreviousHistory(bool update_cur)
1168{
1169 if (selection_history_.size() < 1 || cur_history_ < 1) {
1170 return false;
1171 }
1172
1173 for (int i = cur_history_ - 1; i >= 0; i--) {
1174 if (packet_list_model_->packetNumberToRow(selection_history_.at(i)) >= 0) {
1175 if (update_cur) {
1176 cur_history_ = i;
1177 }
1178 return true;
1179 }
1180 }
1181 return false;
1182}
1183
1184void PacketList::setProfileSwitcher(ProfileSwitcher *profile_switcher)
1185{
1186 profile_switcher_ = profile_switcher;
1187 if (profile_switcher) {
1188 connect(packet_list_model_, &PacketListModel::packetAppended, profile_switcher_, &ProfileSwitcher::checkPacket);
1189 }
1190}
1191
1192frame_data *PacketList::getFDataForRow(int row) const
1193{
1194 return packet_list_model_->getRowFdata(row);
1195}
1196
1197// prefs.col_list has changed.
1198void PacketList::columnsChanged()
1199{
1200 columns_changed_ = true;
1201 column_register_fields();
1202 mainApp->emitAppSignal(MainApplication::FieldsChanged);
1203 if (!cap_file_) {
1204 // Keep columns_changed_ = true until we load a capture file.
1205 return;
1206 }
1207
1208 prefs.num_cols = g_list_length(prefs.col_list);
1209 col_cleanup(&cap_file_->cinfo);
1210 build_column_format_array(&cap_file_->cinfo, prefs.num_cols, false);
1211 create_far_overlay_ = true;
1212 resetColumns();
1213 applyRecentColumnWidths();
1214 setColumnVisibility();
1215 columns_changed_ = false;
1216}
1217
1218// Fields have changed, update custom columns
1219void PacketList::fieldsChanged(capture_file *cf)
1220{
1221 // hfids used by custom columns or by the _ws.col fields may have changed,
1222 // so recreate the columns. We shouldn't need to if prefs.col_list is NULL,
1223 // since that doesn't register and deregister _ws.col fields.
1224 // If the column pref changes to or from NULL, that triggers columnsChanged
1225 // above, so we don't need to do it twice.
1226 //
1227 // XXX - If we knew exactly which fields changed, we could rebuild the
1228 // columns only if a field used by the columns changed.
1229 if (prefs.col_list) {
1230 prefs.num_cols = g_list_length(prefs.col_list);
1231 col_cleanup(&cf->cinfo);
1232 build_column_format_array(&cf->cinfo, prefs.num_cols, false);
1233 resetColumns();
1234 }
1235}
1236
1237// Column widths should
1238// - Load from recent when we load a new profile (including at starting up).
1239// - Reapply when changing columns.
1240// - Persist across freezes and thaws.
1241// - Persist across file closing and opening.
1242// - Save to recent when we save our profile (including shutting down).
1243// - Not be affected by the behavior of stretchLastSection. (XXX: We
1244// still save the stretched value to recent, sectionResized doesn't
1245// distinguish between a resize from being stretched and a manual change.)
1246void PacketList::applyRecentColumnWidths()
1247{
1248 // Either we've just started up or a profile has changed. Read
1249 // the recent settings, apply them, and save the header state.
1250
1251 for (unsigned col = 0; col < prefs.num_cols; col++) {
1252 // The column must be shown before setting column width.
1253 // Visibility will be updated in setColumnVisibility().
1254 setColumnHidden(col, false);
1255 setRecentColumnWidth(col);
1256 }
1257}
1258
1259void PacketList::preferencesChanged()
1260{
1261 // Intelligent scroll bar (minimap)
1262 if (prefs.gui_packet_list_show_minimap) {
1263 if (overlay_timer_id_ == 0) {
1264 overlay_timer_id_ = startTimer(overlay_update_interval_);
1265 }
1266 } else {
1267 if (overlay_timer_id_ != 0) {
1268 killTimer(overlay_timer_id_);
1269 overlay_timer_id_ = 0;
1270 }
1271 }
1272
1273 // Elide mode.
1274 // This sets the mode for the entire view. If we want to make this setting
1275 // per-column we'll either have to generalize RelatedPacketDelegate so that
1276 // we can set it for entire rows or create another delegate.
1277 Qt::TextElideMode elide_mode = Qt::ElideRight;
1278 switch (prefs.gui_packet_list_elide_mode) {
1279 case ELIDE_LEFT:
1280 elide_mode = Qt::ElideLeft;
1281 break;
1282 case ELIDE_MIDDLE:
1283 elide_mode = Qt::ElideMiddle;
1284 break;
1285 case ELIDE_NONE:
1286 elide_mode = Qt::ElideNone;
1287 break;
1288 default:
1289 break;
1290 }
1291 setTextElideMode(elide_mode);
1292}
1293
1294void PacketList::freezePacketList(bool changing_profile)
1295{
1296 changing_profile_ = changing_profile;
1297 freeze(true);
1298}
1299
1300void PacketList::recolorPackets()
1301{
1302 packet_list_model_->resetColorized();
1303 redrawVisiblePackets();
1304}
1305
1306// Enable autoscroll.
1307void PacketList::setVerticalAutoScroll(bool enabled)
1308{
1309 tail_at_end_ = enabled;
1310 if (enabled && capture_in_progress_) {
1311 scrollToBottom();
1312 }
1313}
1314
1315// Called when we finish reading, reloading, rescanning, and retapping
1316// packets.
1317void PacketList::captureFileReadFinished()
1318{
1319 packet_list_model_->flushVisibleRows();
1320 packet_list_model_->dissectIdle(true);
1321 // Invalidating the column strings picks up and request/response
1322 // tracking changes. We might just want to call it from flushVisibleRows.
1323 packet_list_model_->invalidateAllColumnStrings();
1324 // Sort *after* invalidating the column strings
1325 if (isSortingEnabled()) {
1326 sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
1327 }
1328}
1329
1330bool PacketList::freeze(bool keep_current_frame)
1331{
1332 if (!cap_file_ || model() == Q_NULLPTRnullptr) {
1333 // No capture file or already frozen
1334 return false;
1335 }
1336
1337 frame_data *current_frame = cap_file_->current_frame;
1338 column_state_ = header()->saveState();
1339 setHeaderHidden(true);
1340 frozen_current_row_ = currentIndex();
1341 frozen_selected_rows_ = selectionModel()->selectedRows();
1342 selectionModel()->clear();
1343 setModel(Q_NULLPTRnullptr);
1344 // It looks like GTK+ sends a cursor-changed signal at this point but Qt doesn't
1345 // call selectionChanged.
1346 related_packet_delegate_.clear();
1347
1348 if (keep_current_frame) {
1349 cap_file_->current_frame = current_frame;
1350 }
1351
1352 /* Clears packet list as well as byteview */
1353 emit framesSelected(QList<int>());
1354
1355 return true;
1356}
1357
1358bool PacketList::thaw(bool restore_selection)
1359{
1360 if (!cap_file_ || model() != Q_NULLPTRnullptr) {
1361 // No capture file or not frozen
1362 return false;
1363 }
1364
1365 setHeaderHidden(false);
1366 // Note that if we have a current sort status set in the header,
1367 // this will automatically try to sort the model (we don't want
1368 // that to happen if we're in the middle of reading the file).
1369 setModel(packet_list_model_);
1370
1371 if (changing_profile_) {
1372 // When changing profile the new recent settings must be applied to the columns.
1373 applyRecentColumnWidths();
1374 setColumnVisibility();
1375 changing_profile_ = false;
1376 } else {
1377 // Resetting the model resets our column widths so we restore them here.
1378 // We don't reapply the recent settings because the user could have
1379 // resized the columns manually since they were initially loaded.
1380 header()->restoreState(column_state_);
1381 }
1382
1383 if (restore_selection && frozen_selected_rows_.length() > 0 && selectionModel()) {
1384 /* This updates our selection, which redissects the current packet,
1385 * which is needed when we're called from MainWindow::layoutPanes.
1386 * Also, this resets all ProtoTree and ByteView data */
1387 clearSelection();
1388 setCurrentIndex(frozen_current_row_);
1389 foreach (QModelIndex idx, frozen_selected_rows_)for (auto _container_1389 = QtPrivate::qMakeForeachContainer(
frozen_selected_rows_); _container_1389.i != _container_1389.
e; ++_container_1389.i) if (QModelIndex idx = *_container_1389
.i; false) {} else
{
1390 selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows);
1391 }
1392 scrollTo(currentIndex(), PositionAtCenter);
1393 }
1394 frozen_current_row_ = QModelIndex();
1395 frozen_selected_rows_ = QModelIndexList();
1396
1397 return true;
1398}
1399
1400void PacketList::clear() {
1401 related_packet_delegate_.clear();
1402 selectionModel()->clear();
1403 packet_list_model_->clear();
1404 proto_tree_->clear();
1405 selection_history_.clear();
1406 cur_history_ = -1;
1407 in_history_ = false;
1408
1409 QImage overlay;
1410 overlay_sb_->setNearOverlayImage(overlay);
1411 overlay_sb_->setMarkedPacketImage(overlay);
1412 create_near_overlay_ = true;
1413 create_far_overlay_ = true;
1414}
1415
1416void PacketList::writeRecent(FILE *rf) {
1417 int width, col_fmt;
1418 char xalign;
1419
1420 fprintf (rf, "%s:\n", RECENT_KEY_COL_WIDTH"column.width");
1421 for (unsigned col = 0; col < prefs.num_cols; col++) {
1422 if (col > 0) {
1423 fprintf (rf, ",\n");
1424 }
1425 col_fmt = get_column_format(col);
1426 if (col_fmt == COL_CUSTOM) {
1427 fprintf (rf, " \"%%Cus:%s\",", get_column_custom_fields(col));
1428 } else {
1429 fprintf (rf, " %s,", col_format_to_string(col_fmt));
1430 }
1431 width = recent_get_column_width (col);
1432 xalign = recent_get_column_xalign (col);
1433 fprintf (rf, " %d", width);
1434 if (xalign != COLUMN_XALIGN_DEFAULT0) {
1435 fprintf (rf, ":%c", xalign);
1436 }
1437 }
1438 fprintf (rf, "\n");
1439}
1440
1441bool PacketList::contextMenuActive()
1442{
1443 return ctx_column_ >= 0 ? true : false;
1444}
1445
1446QString PacketList::getFilterFromRowAndColumn(QModelIndex idx)
1447{
1448 frame_data *fdata;
1449 QString filter;
1450
1451 if (! idx.isValid())
1452 return filter;
1453
1454 int row = idx.row();
1455 int column = idx.column();
1456
1457 if (!cap_file_ || !packet_list_model_ || column < 0 || (unsigned)column >= cap_file_->cinfo.num_cols)
1458 return filter;
1459
1460 fdata = packet_list_model_->getRowFdata(row);
1461
1462 if (fdata != NULL__null) {
1463 epan_dissect_t edt;
1464 wtap_rec rec; /* Record information */
1465
1466 wtap_rec_init(&rec, DEFAULT_INIT_BUFFER_SIZE_2048(2 * 1024));
1467 if (!cf_read_record(cap_file_, fdata, &rec)) {
1468 wtap_rec_cleanup(&rec);
1469 return filter; /* error reading the record */
1470 }
1471 /* proto tree, visible. We need a proto tree if there's custom columns */
1472 epan_dissect_init(&edt, cap_file_->epan, have_custom_cols(&cap_file_->cinfo), false);
1473 col_custom_prime_edt(&edt, &cap_file_->cinfo);
1474
1475 epan_dissect_run(&edt, cap_file_->cd_t, &rec, fdata, &cap_file_->cinfo);
1476
1477 if (cap_file_->cinfo.columns[column].col_fmt == COL_CUSTOM) {
1478 filter.append(gchar_free_to_qstring(col_custom_get_filter(&edt, &cap_file_->cinfo, (unsigned)column)));
1479 } else {
1480 /* We don't need to fill in the custom columns, as we get their
1481 * filters above.
1482 */
1483 col_fill_in(&edt.pi, true, true);
1484 if (strlen(cap_file_->cinfo.col_expr.col_expr[column]) != 0 &&
1485 strlen(cap_file_->cinfo.col_expr.col_expr_val[column]) != 0) {
1486 bool is_string_value = false;
1487 header_field_info *hfi = proto_registrar_get_byname(cap_file_->cinfo.col_expr.col_expr[column]);
1488 if (hfi && FT_IS_STRING(hfi->type)((hfi->type) == FT_STRING || (hfi->type) == FT_STRINGZ ||
(hfi->type) == FT_STRINGZPAD || (hfi->type) == FT_STRINGZTRUNC
|| (hfi->type) == FT_UINT_STRING || (hfi->type) == FT_AX25
)
) {
1489 /* Could be an address type such as usb.src which must be quoted. */
1490 is_string_value = true;
1491 }
1492
1493 if (filter.isEmpty()) {
1494 if (is_string_value) {
1495 filter.append(QStringLiteral("%1 == \"%2\"")(QString(QtPrivate::qMakeStringPrivate(u"" "%1 == \"%2\"")))
1496 .arg(cap_file_->cinfo.col_expr.col_expr[column])
1497 .arg(cap_file_->cinfo.col_expr.col_expr_val[column]));
1498 } else {
1499 filter.append(QStringLiteral("%1 == %2")(QString(QtPrivate::qMakeStringPrivate(u"" "%1 == %2")))
1500 .arg(cap_file_->cinfo.col_expr.col_expr[column])
1501 .arg(cap_file_->cinfo.col_expr.col_expr_val[column]));
1502 }
1503 }
1504 }
1505 }
1506
1507 epan_dissect_cleanup(&edt);
1508 wtap_rec_cleanup(&rec);
1509 }
1510
1511 return filter;
1512}
1513
1514void PacketList::resetColorized()
1515{
1516 packet_list_model_->resetColorized();
1517 update();
1518}
1519
1520QString PacketList::getPacketComment(unsigned c_number)
1521{
1522 int row = currentIndex().row();
1523 const frame_data *fdata;
1524 char *pkt_comment;
1525 wtap_opttype_return_val result;
1526 QString ret_val = NULL__null;
1527
1528 if (!cap_file_ || !packet_list_model_) return NULL__null;
1529
1530 fdata = packet_list_model_->getRowFdata(row);
1531
1532 if (!fdata) return NULL__null;
1533
1534 wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata);
1535 result = wtap_block_get_nth_string_option_value(pkt_block, OPT_COMMENT1, c_number, &pkt_comment);
1536 if (result == WTAP_OPTTYPE_SUCCESS) {
1537 ret_val = QString(pkt_comment);
1538 }
1539 wtap_block_unref(pkt_block);
1540 return ret_val;
1541}
1542
1543void PacketList::addPacketComment(QString new_comment)
1544{
1545 if (!cap_file_ || !packet_list_model_) return;
1546 if (new_comment.isEmpty()) return;
1547
1548 QByteArray ba = new_comment.toUtf8();
1549
1550 /*
1551 * Make sure this would fit in a pcapng option.
1552 *
1553 * XXX - 65535 is the maximum size for an option in pcapng;
1554 * what if another capture file format supports larger
1555 * comments?
1556 */
1557 if (ba.size() > 65535) {
1558 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK0x01,
1559 "That comment is too large to save in a capture file.");
1560 return;
1561 }
1562
1563 if (selectionModel() && selectionModel()->hasSelection()) {
1564 packet_list_model_->addFrameComment(selectionModel()->selectedRows(), ba);
1565 drawCurrentPacket();
1566 }
1567}
1568
1569void PacketList::setPacketComment(unsigned c_number, QString new_comment)
1570{
1571 QModelIndex curIndex = currentIndex();
1572
1573 if (!cap_file_ || !packet_list_model_) return;
1574
1575 QByteArray ba = new_comment.toUtf8();
1576 /*
1577 * Make sure this would fit in a pcapng option.
1578 *
1579 * XXX - 65535 is the maximum size for an option in pcapng;
1580 * what if another capture file format supports larger
1581 * comments?
1582 */
1583 if (ba.size() > 65535) {
1584 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK0x01,
1585 "That comment is too large to save in a capture file.");
1586 return;
1587 }
1588
1589 packet_list_model_->setFrameComment(curIndex, ba, c_number);
1590 drawCurrentPacket();
1591}
1592
1593QString PacketList::allPacketComments()
1594{
1595 uint32_t framenum;
1596 frame_data *fdata;
1597 QString buf_str;
1598
1599 if (!cap_file_) return buf_str;
1600
1601 for (framenum = 1; framenum <= cap_file_->count ; framenum++) {
1602 fdata = frame_data_sequence_find(cap_file_->provider.frames, framenum);
1603
1604 wtap_block_t pkt_block = cf_get_packet_block(cap_file_, fdata);
1605
1606 if (pkt_block) {
1607 unsigned n_comments = wtap_block_count_option(pkt_block, OPT_COMMENT1);
1608 for (unsigned i = 0; i < n_comments; i++) {
1609 char *comment_text;
1610 if (WTAP_OPTTYPE_SUCCESS == wtap_block_get_nth_string_option_value(pkt_block, OPT_COMMENT1, i, &comment_text)) {
1611 buf_str.append(tr("Frame %1: %2\n\n").arg(framenum).arg(comment_text));
1612 if (buf_str.length() > max_comments_to_fetch_) {
1613 buf_str.append(tr("[ Comment text exceeds %1. Stopping. ]")
1614 .arg(format_size(max_comments_to_fetch_, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI)format_size_wmem(__null, max_comments_to_fetch_, FORMAT_SIZE_UNIT_BYTES
, (1 << 0))
));
1615 return buf_str;
1616 }
1617 }
1618 }
1619 }
1620 }
1621 return buf_str;
1622}
1623
1624void PacketList::deleteCommentsFromPackets()
1625{
1626 if (!cap_file_ || !packet_list_model_) return;
1627
1628 if (selectionModel() && selectionModel()->hasSelection()) {
1629 packet_list_model_->deleteFrameComments(selectionModel()->selectedRows());
1630 drawCurrentPacket();
1631 }
1632}
1633
1634void PacketList::deleteAllPacketComments()
1635{
1636 if (!cap_file_ || !packet_list_model_) return;
1637
1638 packet_list_model_->deleteAllFrameComments();
1639 drawCurrentPacket();
1640}
1641
1642
1643// Slots
1644
1645void PacketList::setCaptureFile(capture_file *cf)
1646{
1647 cap_file_ = cf;
1648 packet_list_model_->setCaptureFile(cf);
1649 if (cap_file_ && columns_changed_) {
1650 columnsChanged();
1651 }
1652 create_near_overlay_ = true;
1653 changing_profile_ = false;
1654 sortByColumn(-1, Qt::AscendingOrder);
1655}
1656
1657void PacketList::setMonospaceFont(const QFont &mono_font)
1658{
1659 setFont(mono_font);
1660}
1661
1662void PacketList::setRegularFont(const QFont &regular_font)
1663{
1664 header()->setFont(regular_font);
1665 header()->viewport()->setFont(regular_font);
1666}
1667
1668void PacketList::goNextPacket(void)
1669{
1670 if (QApplication::keyboardModifiers() & Qt::AltModifier) {
1671 // Alt+toolbar
1672 goNextHistoryPacket();
1673 return;
1674 }
1675
1676 if (selectionModel()->hasSelection()) {
1677 selectionModel()->setCurrentIndex(moveCursor(MoveDown, Qt::NoModifier), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1678 } else {
1679 // First visible packet.
1680 selectionModel()->setCurrentIndex(indexAt(viewport()->rect().topLeft()), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1681 }
1682
1683 scrollViewChanged(false);
1684}
1685
1686void PacketList::goPreviousPacket(void)
1687{
1688 if (QApplication::keyboardModifiers() & Qt::AltModifier) {
1689 // Alt+toolbar
1690 goPreviousHistoryPacket();
1691 return;
1692 }
1693
1694 if (selectionModel()->hasSelection()) {
1695 selectionModel()->setCurrentIndex(moveCursor(MoveUp, Qt::NoModifier), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1696 } else {
1697 // Last visible packet.
1698 QModelIndex last_idx = indexAt(viewport()->rect().bottomLeft());
1699 if (last_idx.isValid()) {
1700 selectionModel()->setCurrentIndex(last_idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1701 } else {
1702 goLastPacket();
1703 }
1704 }
1705
1706 scrollViewChanged(false);
1707}
1708
1709void PacketList::goFirstPacket(void) {
1710 if (packet_list_model_->rowCount() < 1) return;
1711 selectionModel()->setCurrentIndex(packet_list_model_->index(0, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1712 scrollTo(currentIndex());
1713
1714 scrollViewChanged(false);
1715}
1716
1717void PacketList::goLastPacket(void) {
1718 if (packet_list_model_->rowCount() < 1) return;
1719 selectionModel()->setCurrentIndex(packet_list_model_->index(packet_list_model_->rowCount() - 1, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1720 scrollTo(currentIndex());
1721
1722 scrollViewChanged(false);
1723}
1724
1725void PacketList::goToPacket(int packet, int hf_id)
1726{
1727 if (!cf_goto_frame(cap_file_, packet, false))
1728 return;
1729
1730 // cf_goto_frame only returns true if packet_list_select_row_from_data
1731 // succeeds, the latter has already selected and scrolled to the frame.
1732 if (hf_id > 0) {
1733 proto_tree_->goToHfid(hf_id);
1734 }
1735
1736 scrollViewChanged(false);
1737}
1738
1739void PacketList::goNextHistoryPacket()
1740{
1741 if (haveNextHistory(true)) {
1742 in_history_ = true;
1743 goToPacket(selection_history_.at(cur_history_));
1744 in_history_ = false;
1745 }
1746}
1747
1748void PacketList::goPreviousHistoryPacket()
1749{
1750 if (havePreviousHistory(true)) {
1751 in_history_ = true;
1752 goToPacket(selection_history_.at(cur_history_));
1753 in_history_ = false;
1754 }
1755}
1756
1757void PacketList::markFrame()
1758{
1759 if (!cap_file_ || !packet_list_model_) return;
1760
1761 QModelIndexList frames;
1762
1763 if (selectionModel() && selectionModel()->hasSelection())
1764 {
1765 QModelIndexList selRows = selectionModel()->selectedRows(0);
1766 foreach (QModelIndex idx, selRows)for (auto _container_1766 = QtPrivate::qMakeForeachContainer(
selRows); _container_1766.i != _container_1766.e; ++_container_1766
.i) if (QModelIndex idx = *_container_1766.i; false) {} else
1767 {
1768 if (idx.isValid())
1769 {
1770 frames << idx;
1771 }
1772 }
1773 }
1774 else
1775 frames << currentIndex();
1776
1777 packet_list_model_->toggleFrameMark(frames);
1778
1779 // Make sure the packet list's frame.marked related field text is updated.
1780 redrawVisiblePackets();
1781
1782 create_far_overlay_ = true;
1783 packets_bar_update();
1784}
1785
1786void PacketList::markAllDisplayedFrames(bool set)
1787{
1788 if (!cap_file_ || !packet_list_model_) return;
1789
1790 packet_list_model_->setDisplayedFrameMark(set);
1791
1792 // Make sure the packet list's frame.marked related field text is updated.
1793 redrawVisiblePackets();
1794
1795 create_far_overlay_ = true;
1796 packets_bar_update();
1797}
1798
1799void PacketList::ignoreFrame()
1800{
1801 if (!cap_file_ || !packet_list_model_) return;
1802
1803 QModelIndexList frames;
1804
1805 if (selectionModel() && selectionModel()->hasSelection())
1806 {
1807 foreach (QModelIndex idx, selectionModel()->selectedRows(0))for (auto _container_1807 = QtPrivate::qMakeForeachContainer(
selectionModel()->selectedRows(0)); _container_1807.i != _container_1807
.e; ++_container_1807.i) if (QModelIndex idx = *_container_1807
.i; false) {} else
1808 {
1809 if (idx.isValid())
1810 {
1811 frames << idx;
1812 }
1813 }
1814 }
1815 else
1816 frames << currentIndex();
1817
1818
1819 packet_list_model_->toggleFrameIgnore(frames);
1820 create_far_overlay_ = true;
1821 int sb_val = verticalScrollBar()->value(); // Surely there's a better way to keep our position?
1822 setUpdatesEnabled(false);
1823 emit packetDissectionChanged();
1824 setUpdatesEnabled(true);
1825 verticalScrollBar()->setValue(sb_val);
1826}
1827
1828void PacketList::ignoreAllDisplayedFrames(bool set)
1829{
1830 if (!cap_file_ || !packet_list_model_) return;
1831
1832 packet_list_model_->setDisplayedFrameIgnore(set);
1833 create_far_overlay_ = true;
1834 emit packetDissectionChanged();
1835}
1836
1837void PacketList::setTimeReference()
1838{
1839 if (!cap_file_ || !packet_list_model_) return;
1840 packet_list_model_->toggleFrameRefTime(currentIndex());
1841 create_far_overlay_ = true;
1842}
1843
1844void PacketList::unsetAllTimeReferences()
1845{
1846 if (!cap_file_ || !packet_list_model_) return;
1847 packet_list_model_->unsetAllFrameRefTime();
1848 create_far_overlay_ = true;
1849}
1850
1851void PacketList::applyTimeShift()
1852{
1853 packet_list_model_->resetColumns();
1854 redrawVisiblePackets();
1855 emit packetDissectionChanged();
1856}
1857
1858void PacketList::updatePackets(bool redraw)
1859{
1860 if (redraw) {
1861 packet_list_model_->resetColumns();
1862 redrawVisiblePackets();
1863 } else {
1864 update();
1865 }
1866}
1867
1868void PacketList::columnVisibilityTriggered()
1869{
1870 QAction *ha = qobject_cast<QAction*>(sender());
1871 if (!ha) return;
1872
1873 int col = ha->data().toInt();
1874 set_column_visible(col, ha->isChecked());
1875 setColumnVisibility();
1876 if (ha->isChecked()) {
1877 setRecentColumnWidth(col);
1878 }
1879 prefs_main_write();
1880}
1881
1882void PacketList::sectionResized(int col, int, int new_width)
1883{
1884 if (isVisible() && !columns_changed_ && !set_column_visibility_ && !set_style_sheet_ && new_width > 0) {
1885 // Column 1 gets an invalid value (32 on macOS) when we're not yet
1886 // visible.
1887 //
1888 // Don't set column width when columns changed or setting column
1889 // visibility because we may get a sectionResized() from QTreeView
1890 // with values from a old columns layout.
1891 //
1892 // Don't set column width when hiding a column.
1893
1894 recent_set_column_width(col, new_width);
1895 }
1896}
1897
1898// The user moved a column. Make sure prefs.col_list, the column format
1899// array, and the header's visual and logical indices all agree.
1900// gtk/packet_list.c:column_dnd_changed_cb
1901void PacketList::sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
1902{
1903 GList *new_col_list = NULL__null;
1904 GList *new_recent_col_list = NULL__null;
1905 QList<int> saved_sizes;
1906 int sort_idx;
1907
1908 // Since we undo the move below, these should always stay in sync.
1909 // Otherwise the order of columns can be unexpected after drag and drop.
1910 if (logicalIndex != oldVisualIndex) {
1911 ws_warning("Column moved from an unexpected state (%d, %d, %d)",do { if (true) { ws_log_full("", LOG_LEVEL_WARNING, "ui/qt/packet_list.cpp"
, 1912, __func__, "Column moved from an unexpected state (%d, %d, %d)"
, logicalIndex, oldVisualIndex, newVisualIndex); } } while (0
)
1912 logicalIndex, oldVisualIndex, newVisualIndex)do { if (true) { ws_log_full("", LOG_LEVEL_WARNING, "ui/qt/packet_list.cpp"
, 1912, __func__, "Column moved from an unexpected state (%d, %d, %d)"
, logicalIndex, oldVisualIndex, newVisualIndex); } } while (0
)
;
1913 }
1914
1915 // Remember which column should be sorted. Use the visual index since this
1916 // points to the current GUI state rather than the outdated column order
1917 // (indicated by the logical index).
1918 sort_idx = header()->sortIndicatorSection();
1919 if (sort_idx != -1) {
1920 sort_idx = header()->visualIndex(sort_idx);
1921 }
1922
1923 // Build a new column list based on the header's logical order.
1924 for (int vis_idx = 0; vis_idx < header()->count(); vis_idx++) {
1925 int log_idx = header()->logicalIndex(vis_idx);
1926 saved_sizes << header()->sectionSize(log_idx);
1927
1928 void *pref_data = g_list_nth_data(prefs.col_list, log_idx);
1929 if (pref_data) {
1930 new_col_list = g_list_append(new_col_list, pref_data);
1931 }
1932
1933 pref_data = g_list_nth_data(recent.col_width_list, log_idx);
1934 if (pref_data) {
1935 new_recent_col_list = g_list_append(new_recent_col_list, pref_data);
1936 }
1937 }
1938
1939 // Undo move to ensure that the logical indices map to the visual indices,
1940 // otherwise the column order is changed twice (once via the modified
1941 // col_list, once because of the visual/logical index mismatch).
1942 disconnect(header(), &QHeaderView::sectionMoved, this, &PacketList::sectionMoved);
1943 header()->moveSection(newVisualIndex, oldVisualIndex);
1944 connect(header(), &QHeaderView::sectionMoved, this, &PacketList::sectionMoved);
1945
1946 // Clear and rebuild our (and the header's) model. There doesn't appear
1947 // to be another way to reset the logical index.
1948 freeze();
1949
1950 g_list_free(prefs.col_list);
1951 prefs.col_list = new_col_list;
1952 g_list_free(recent.col_width_list);
1953 recent.col_width_list = new_recent_col_list;
1954
1955 thaw(true);
1956
1957 for (int i = 0; i < saved_sizes.length(); i++) {
1958 if (saved_sizes[i] < 1) continue;
1959 header()->resizeSection(i, saved_sizes[i]);
1960 }
1961
1962 prefs_main_write();
1963
1964 mainApp->emitAppSignal(MainApplication::ColumnsChanged);
1965
1966 // If the column with the sort indicator got shifted, mark the new column
1967 // after updating the columns contents (via ColumnsChanged) to ensure that
1968 // the columns are sorted using the intended column contents.
1969 int left_col = MIN(oldVisualIndex, newVisualIndex)(((oldVisualIndex) < (newVisualIndex)) ? (oldVisualIndex) :
(newVisualIndex))
;
1970 int right_col = MAX(oldVisualIndex, newVisualIndex)(((oldVisualIndex) > (newVisualIndex)) ? (oldVisualIndex) :
(newVisualIndex))
;
1971 if (left_col <= sort_idx && sort_idx <= right_col) {
1972 header()->setSortIndicator(sort_idx, header()->sortIndicatorOrder());
1973 }
1974}
1975
1976QString PacketList::createSummaryText(QModelIndex idx, SummaryCopyType type)
1977{
1978 if (! idx.isValid())
1979 return "";
1980
1981 QStringList col_parts;
1982 int row = idx.row();
1983 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
1984 if (get_column_visible(col)) {
1985 col_parts << packet_list_model_->data(packet_list_model_->index(row, col), Qt::DisplayRole).toString();
1986 }
1987 }
1988 return joinSummaryRow(col_parts, row, type);
1989}
1990
1991QString PacketList::createHeaderSummaryText(SummaryCopyType type)
1992{
1993 QStringList col_parts;
1994 for (int col = 0; col < packet_list_model_->columnCount(); ++col) {
1995 if (get_column_visible(col)) {
1996 col_parts << packet_list_model_->headerData(col, Qt::Orientation::Horizontal, Qt::DisplayRole).toString();
1997 }
1998 }
1999 return joinSummaryRow(col_parts, 0, type);
2000}
2001
2002QStringList PacketList::createHeaderPartsForAligned()
2003{
2004 QStringList hdr_parts;
2005 for (int col = 0; col < packet_list_model_->columnCount(); ++col) {
2006 if (get_column_visible(col)) {
2007 hdr_parts << packet_list_model_->headerData(col, Qt::Orientation::Horizontal, Qt::DisplayRole).toString();
2008 }
2009 }
2010 return hdr_parts;
2011}
2012
2013QList<int> PacketList::createAlignmentPartsForAligned()
2014{
2015 QList<int> align_parts;
2016 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
2017 if (get_column_visible(col)) {
2018 align_parts << packet_list_model_->data(packet_list_model_->index(0, col), Qt::TextAlignmentRole).toInt();
2019 }
2020 }
2021 return align_parts;
2022}
2023
2024QList<int> PacketList::createSizePartsForAligned(bool useHeader, QStringList hdr_parts, QList<int> rows)
2025{
2026 QList<int> size_parts;
2027
2028 for (int i = 0; i < hdr_parts.size(); ++i) {
2029 if (useHeader)
2030 size_parts << static_cast<int>(hdr_parts.at(i).size());
2031 else
2032 size_parts << 0;
2033 }
2034
2035 foreach(int row, rows)for (auto _container_2035 = QtPrivate::qMakeForeachContainer(
rows); _container_2035.i != _container_2035.e; ++_container_2035
.i) if (int row = *_container_2035.i; false) {} else
2036 {
2037 QModelIndex idx = model()->index(row, 0);
2038 if (! idx.isValid())
2039 continue;
2040
2041 QStringList col_parts;
2042 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
2043 if (get_column_visible(col)) {
2044 col_parts << (packet_list_model_->data(packet_list_model_->index(row, col), Qt::DisplayRole).toString());
2045 }
2046 }
2047
2048 for (int i = 0; i < col_parts.size(); ++i) {
2049 if (col_parts.at(i).size() > size_parts.at(i)) {
2050 size_parts[i] = static_cast<int>(col_parts.at(i).size());
2051 }
2052 }
2053 col_parts.clear();
2054 }
2055
2056 return size_parts;
2057}
2058
2059QString PacketList::createHeaderSummaryForAligned(QStringList hdr_parts, QList<int> align_parts, QList<int> size_parts)
2060{
2061 QString hdr_text;
2062 for (int i = 0; i < hdr_parts.size(); ++i) {
2063 if (align_parts.at(i) == Qt::AlignLeft) {
2064 hdr_text += hdr_parts[i].leftJustified(size_parts.at(i), ' ') + " ";
2065 }
2066 else if (align_parts.at(i) == Qt::AlignRight) {
2067 hdr_text += hdr_parts[i].rightJustified(size_parts.at(i), ' ') + " ";
2068 }
2069 }
2070 return QStringLiteral("-%1")(QString(QtPrivate::qMakeStringPrivate(u"" "-%1"))).arg(hdr_text).trimmed().mid(1);
2071}
2072
2073QString PacketList::createSummaryForAligned(QModelIndex idx, QList<int> align_parts, QList<int> size_parts)
2074{
2075 if (! idx.isValid())
2076 return "";
2077
2078 QStringList col_parts;
2079 int row = idx.row();
2080 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
2081 if (get_column_visible(col)) {
2082 col_parts << packet_list_model_->data(packet_list_model_->index(row, col), Qt::DisplayRole).toString();
2083 }
2084 }
2085
2086 QString col_text;
2087 for (int i = 0; i < col_parts.size(); ++i) {
2088 if (align_parts.at(i) == Qt::AlignLeft) {
2089 col_text += col_parts[i].leftJustified(size_parts.at(i), ' ') + " ";
2090 }
2091 else if (align_parts.at(i) == Qt::AlignRight) {
2092 col_text += col_parts[i].rightJustified(size_parts.at(i), ' ') + " ";
2093 }
2094 }
2095
2096 return QStringLiteral("-%1")(QString(QtPrivate::qMakeStringPrivate(u"" "-%1"))).arg(col_text).trimmed().mid(1);
2097}
2098
2099QString PacketList::createDefaultStyleForHtml()
2100{
2101 QString fontFamily = QString(prefs.gui_font_name).split(",")[0];
2102 QString fontSize = QString(prefs.gui_font_name).split(",")[1];
2103
2104 return QStringLiteral("<style>"(QString(QtPrivate::qMakeStringPrivate(u"" "<style>" "table{font-family:%1;font-size:%2pt;}"
"th{background-color:#000000;color:#ffffff;text-align:left;}"
"th,td{padding:%3pt}" "</style>")))
2105 "table{font-family:%1;font-size:%2pt;}"(QString(QtPrivate::qMakeStringPrivate(u"" "<style>" "table{font-family:%1;font-size:%2pt;}"
"th{background-color:#000000;color:#ffffff;text-align:left;}"
"th,td{padding:%3pt}" "</style>")))
2106 "th{background-color:#000000;color:#ffffff;text-align:left;}"(QString(QtPrivate::qMakeStringPrivate(u"" "<style>" "table{font-family:%1;font-size:%2pt;}"
"th{background-color:#000000;color:#ffffff;text-align:left;}"
"th,td{padding:%3pt}" "</style>")))
2107 "th,td{padding:%3pt}"(QString(QtPrivate::qMakeStringPrivate(u"" "<style>" "table{font-family:%1;font-size:%2pt;}"
"th{background-color:#000000;color:#ffffff;text-align:left;}"
"th,td{padding:%3pt}" "</style>")))
2108 "</style>")(QString(QtPrivate::qMakeStringPrivate(u"" "<style>" "table{font-family:%1;font-size:%2pt;}"
"th{background-color:#000000;color:#ffffff;text-align:left;}"
"th,td{padding:%3pt}" "</style>")))
.arg(fontFamily, fontSize).arg(fontSize.toInt() / 2);
2109}
2110
2111QString PacketList::createOpeningTagForHtml()
2112{
2113 return "<table>";
2114}
2115
2116QString PacketList::createHeaderSummaryForHtml()
2117{
2118 QString hdr_text;
2119 hdr_text += "<tr>";
2120 for (int col = 0; col < packet_list_model_->columnCount(); ++col) {
2121 if (get_column_visible(col)) {
2122 hdr_text += "<th>";
2123 hdr_text += packet_list_model_->headerData(col, Qt::Orientation::Horizontal, Qt::DisplayRole).toString();
2124 hdr_text += "</th>";
2125 }
2126 }
2127 hdr_text += "</tr>";
2128 return hdr_text;
2129}
2130
2131QString PacketList::createSummaryForHtml(QModelIndex idx)
2132{
2133 if (! idx.isValid())
2134 return "";
2135
2136 int row = idx.row();
2137 QString col_text;
2138
2139 QString bg_color = packet_list_model_->data(packet_list_model_->index(row, 0), Qt::BackgroundRole).toString();
2140 QString fg_color = packet_list_model_->data(packet_list_model_->index(row, 0), Qt::ForegroundRole).toString();
2141 col_text += "<tr style=\"background-color:" + bg_color + ";color:" + fg_color + ";\">";
2142
2143 QString alignment[] = {"left", "right", "center", "justify"};
2144
2145 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
2146 if (get_column_visible(col)) {
2147 col_text += "<td style=\"text-align:" + alignment[packet_list_model_->data(packet_list_model_->index(row, col), Qt::TextAlignmentRole).toInt() / 2] + ";\">";
2148 col_text += packet_list_model_->data(packet_list_model_->index(row, col), Qt::DisplayRole).toString();
2149 col_text += "</td>";
2150 }
2151 }
2152
2153 col_text += "</tr>";
2154 return col_text;
2155}
2156
2157QString PacketList::createClosingTagForHtml()
2158{
2159 return "</table>";
2160}
2161
2162void PacketList::copySummary()
2163{
2164 if (!currentIndex().isValid()) return;
2165
2166 QAction *ca = qobject_cast<QAction*>(sender());
2167 if (!ca) return;
2168
2169 QVariant type = ca->data();
2170 if (! type.canConvert<SummaryCopyType>())
2171 return;
2172 SummaryCopyType copy_type = type.value<SummaryCopyType>();
2173
2174 QString copy_text;
2175 if (type == CopyAsText || type == CopyAsHTML) {
2176 if (prefs.gui_packet_list_copy_text_with_aligned_columns) {
2177 QList<int> rows;
2178 rows << currentIndex().row();
2179 QStringList hdr_parts;
2180 QList<int> align_parts, size_parts;
2181 hdr_parts = createHeaderPartsForAligned();
2182 align_parts = createAlignmentPartsForAligned();
2183 size_parts = createSizePartsForAligned(false, hdr_parts, rows);
2184 copy_text = createSummaryForAligned(currentIndex(), align_parts, size_parts);
2185 }
2186 else {
2187 copy_text = createSummaryText(currentIndex(), CopyAsText);
2188 }
2189 copy_text += "\n";
2190 if (type == CopyAsHTML) {
2191 QStringList htmlContent;
2192 htmlContent << createDefaultStyleForHtml();
2193 htmlContent << createOpeningTagForHtml();
2194 htmlContent << createSummaryForHtml(currentIndex());
2195 htmlContent << createClosingTagForHtml();
2196 // htmlContent will never be empty as they will always have
2197 // style and table tags
2198 QMimeData *mimeData = new QMimeData;
2199 mimeData->setHtml(htmlContent.join('\n'));
2200 mimeData->setText(copy_text);
2201 mainApp->clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
2202 }
2203 else {
2204 mainApp->clipboard()->setText(copy_text);
2205 }
2206 }
2207 else {
2208 copy_text = createSummaryText(currentIndex(), copy_type);
2209 if (type != CopyAsYAML)
2210 copy_text += "\n";
2211 mainApp->clipboard()->setText(copy_text);
2212 }
2213}
2214
2215// We need to tell when the user has scrolled the packet list, either to
2216// the end or anywhere other than the end.
2217void PacketList::vScrollBarActionTriggered(int)
2218{
2219 // If we're scrolling with a mouse wheel or trackpad sliderPosition can end up
2220 // past the end.
2221 tail_at_end_ = (overlay_sb_->sliderPosition() >= overlay_sb_->maximum());
2222
2223 scrollViewChanged(tail_at_end_);
2224}
2225
2226void PacketList::scrollViewChanged(bool at_end)
2227{
2228 if (capture_in_progress_) {
2229 // We want to start auto scrolling when the user scrolls to (or past)
2230 // the end only if recent.capture_auto_scroll is set.
2231 // We want to stop autoscrolling if the user scrolls up or uses
2232 // Go to Packet regardless of the preference setting.
2233 if (recent.capture_auto_scroll || !at_end) {
2234 emit packetListScrolled(at_end);
2235 }
2236 }
2237}
2238
2239// Goal: Overlay the packet list scroll bar with the colors of all of the
2240// packets.
2241// Try 1: Average packet colors in each scroll bar raster line. This has
2242// two problems: It's easy to wash out colors and we dissect every packet.
2243// Try 2: Color across a 5000 or 10000 packet window. We still end up washing
2244// out colors.
2245// Try 3: One packet per vertical scroll bar pixel. This seems to work best
2246// but has the smallest window.
2247// Try 4: Use a multiple of the scroll bar height and scale the image down
2248// using Qt::SmoothTransformation. This gives us more packets per raster
2249// line.
2250
2251// Odd (prime?) numbers resulted in fewer scaling artifacts. A multiplier
2252// of 9 washed out colors a little too much.
2253//const int height_multiplier_ = 7;
2254void PacketList::drawNearOverlay()
2255{
2256 if (create_near_overlay_) {
2257 create_near_overlay_ = false;
2258 }
2259
2260 if (!cap_file_ || cap_file_->state != FILE_READ_DONE) return;
2261
2262 if (!prefs.gui_packet_list_show_minimap) return;
2263
2264 qreal dp_ratio = overlay_sb_->devicePixelRatio();
2265 int o_height = overlay_sb_->height() * dp_ratio;
2266 int o_rows = qMin(packet_list_model_->rowCount(), o_height);
2267 QFontMetricsF fmf(mainApp->font());
2268 int o_width = ((static_cast<int>(fmf.height())) * 2 * dp_ratio) + 2; // 2ems + 1-pixel border on either side.
2269
2270 if (recent.packet_list_colorize && o_rows > 0) {
2271 QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied);
2272
2273 QPainter painter(&overlay);
2274
2275 overlay.fill(Qt::transparent);
2276
2277 int cur_line = 0;
2278 int start = 0;
2279
2280 if (packet_list_model_->rowCount() > o_height && overlay_sb_->maximum() > 0) {
2281 start += ((double) overlay_sb_->value() / overlay_sb_->maximum()) * (packet_list_model_->rowCount() - o_rows);
2282 }
2283 int end = start + o_rows;
2284 for (int row = start; row < end; row++) {
2285 packet_list_model_->ensureRowColorized(row);
2286
2287 frame_data *fdata = packet_list_model_->getRowFdata(row);
2288 int next_line = (row - start + 1) * o_height / o_rows;
2289 int row_height = next_line - cur_line;
2290
2291 // Multi-color support in minimap (enabled for all non-Off modes)
2292 if (prefs.gui_packet_list_multi_color_mode != PACKET_LIST_MULTI_COLOR_MODE_OFF) {
2293 QModelIndex idx = packet_list_model_->index(row, 0);
2294 PacketListRecord *record = static_cast<PacketListRecord*>(idx.internalPointer());
2295 // Conversation color filters take full precedence — skip multi-color stripe rendering
2296 bool is_conversation_color = fdata->color_filter &&
2297 strncmp(((const color_filter_t *)fdata->color_filter)->filter_name,
2298 CONVERSATION_COLOR_PREFIX"___conversation_color_filter___", strlen(CONVERSATION_COLOR_PREFIX"___conversation_color_filter___")) == 0;
2299 if (record && record->hasMultipleColors() && !is_conversation_color) {
2300 // Draw vertical stripes for multiple colors (skip paused filters)
2301 const GSList *filters = record->matchingColorFilters();
2302
2303 // Count non-paused filters
2304 int num_active_colors = 0;
2305 for (const GSList *item = filters; item != NULL__null; item = g_slist_next(item)((item) ? (((GSList *)(item))->next) : __null)) {
2306 const color_filter_t *colorf = (const color_filter_t *)item->data;
2307 if (!color_filter_is_session_disabled(colorf->filter_name)) {
2308 num_active_colors++;
2309 }
2310 }
2311
2312 if (num_active_colors > 0) {
2313 int stripe_width = o_width / num_active_colors;
2314 int x_pos = 0;
2315 int stripe_idx = 0;
2316
2317 for (const GSList *item = filters; item != NULL__null; item = g_slist_next(item)((item) ? (((GSList *)(item))->next) : __null)) {
2318 const color_filter_t *colorf = (const color_filter_t *)item->data;
2319 // Skip paused filters
2320 if (!color_filter_is_session_disabled(colorf->filter_name)) {
2321 QColor color(ColorUtils::fromColorT(&colorf->bg_color));
2322 int width = (stripe_idx == num_active_colors - 1) ? (o_width - x_pos) : stripe_width;
2323 painter.fillRect(x_pos, cur_line, width, row_height, color);
2324 x_pos += stripe_width;
2325 stripe_idx++;
2326 }
2327 }
2328 } else {
2329 // All filters paused, draw no color
2330 painter.fillRect(0, cur_line, o_width, row_height, QColor(Qt::white));
2331 }
2332 } else if (fdata->color_filter) {
2333 // Single color fallback
2334 const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
2335 QColor color(ColorUtils::fromColorT(&color_filter->bg_color));
2336 painter.fillRect(0, cur_line, o_width, row_height, color);
2337 }
2338 } else {
2339 // Original single-color behavior
2340 const color_t *bgcolor = NULL__null;
2341 if (fdata->color_filter) {
2342 const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
2343 bgcolor = &color_filter->bg_color;
2344 }
2345
2346 if (bgcolor) {
2347 QColor color(ColorUtils::fromColorT(bgcolor));
2348 painter.fillRect(0, cur_line, o_width, row_height, color);
2349 }
2350 }
2351
2352 cur_line = next_line;
2353 }
2354
2355 // If the selected packet is in the overlay set selected_pos
2356 // accordingly. Otherwise, pin it to either the top or bottom.
2357 QList<int> positions;
2358 if (selectionModel()->hasSelection()) {
2359
2360 QModelIndexList selRows = selectionModel()->selectedRows(0);
2361 int last_row = -1;
2362 int last_pos = -1;
2363 foreach (QModelIndex idx, selRows)for (auto _container_2363 = QtPrivate::qMakeForeachContainer(
selRows); _container_2363.i != _container_2363.e; ++_container_2363
.i) if (QModelIndex idx = *_container_2363.i; false) {} else
2364 {
2365 int selected_pos = -1;
2366 int sel_row = idx.row();
2367 if (sel_row < start) {
2368 selected_pos = 0;
2369 } else if (sel_row >= end) {
2370 selected_pos = overlay.height() - 1;
2371 } else {
2372 selected_pos = (sel_row - start) * o_height / o_rows;
2373 }
2374
2375 /* Due to the difference in the display height, we sometimes get empty positions
2376 * inbetween consecutive valid rows. If those are detected, they are signaled as
2377 * being selected as well */
2378 if (last_pos >= 0 && selected_pos > (last_pos + 1) && (last_row + 1) == sel_row)
2379 {
2380 for (int pos = (last_pos + 1); pos < selected_pos; pos++)
2381 {
2382 if (! positions.contains(pos))
2383 positions << pos;
2384 }
2385 }
2386 else if (selected_pos != -1 && ! positions.contains(selected_pos))
2387 positions << selected_pos;
2388
2389 last_row = sel_row;
2390 last_pos = selected_pos;
2391 }
2392 }
2393
2394 overlay_sb_->setNearOverlayImage(overlay, packet_list_model_->rowCount(), start, end, positions, (o_height / o_rows));
2395 } else {
2396 QImage overlay;
2397 overlay_sb_->setNearOverlayImage(overlay);
2398 }
2399}
2400
2401void PacketList::drawFarOverlay()
2402{
2403 if (create_far_overlay_) {
2404 create_far_overlay_ = false;
2405 }
2406
2407 if (!cap_file_ || cap_file_->state != FILE_READ_DONE) return;
2408
2409 if (!prefs.gui_packet_list_show_minimap) return;
2410
2411 QSize groove_size = overlay_sb_->grooveRect().size();
2412 qreal dp_ratio = overlay_sb_->devicePixelRatio();
2413 groove_size *= dp_ratio;
2414 int o_width = groove_size.width();
2415 int o_height = groove_size.height();
2416 int pl_rows = packet_list_model_->rowCount();
2417 QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied);
2418 bool have_marked_image = false;
2419
2420 // If only there were references from popular culture about getting into
2421 // some sort of groove.
2422 if (!overlay.isNull() && recent.packet_list_colorize && pl_rows > 0) {
2423
2424 QPainter painter(&overlay);
2425
2426 // Draw text-colored tick marks on a transparent background.
2427 // Hopefully no themes use the text color for the groove color.
2428 overlay.fill(Qt::transparent);
2429
2430 QColor tick_color = palette().text().color();
2431 tick_color.setAlphaF(0.3f);
2432 painter.setPen(tick_color);
2433
2434 for (int row = 0; row < pl_rows; row++) {
2435
2436 frame_data *fdata = packet_list_model_->getRowFdata(row);
2437 if (fdata->marked || fdata->ref_time || fdata->ignored) {
2438 int new_line = row * o_height / pl_rows;
2439 int tick_width = o_width / 3;
2440 // Marked or ignored: left side, time refs: right side.
2441 // XXX Draw ignored ticks in the middle?
2442 int x1 = fdata->ref_time ? o_width - tick_width : 1;
2443 int x2 = fdata->ref_time ? o_width - 1 : tick_width;
2444
2445 painter.drawLine(x1, new_line, x2, new_line);
2446 have_marked_image = true;
2447 }
2448 }
2449
2450 if (have_marked_image) {
2451 overlay_sb_->setMarkedPacketImage(overlay);
2452 return;
2453 }
2454 }
2455
2456 if (!have_marked_image) {
2457 QImage null_overlay;
2458 overlay_sb_->setMarkedPacketImage(null_overlay);
2459 }
2460}
2461
2462// Auto scroll if:
2463// - We are capturing
2464// - actionGoAutoScroll in the main UI is checked.
2465
2466// actionGoAutoScroll in the main UI:
2467// - Is set to the value of recent.capture_auto_scroll when beginning a capture
2468// - Can be triggered manually by the user
2469// - Is turned on if the last user-set vertical scrollbar position is at the
2470// end and recent.capture_auto_scroll is enabled
2471// - Is turned off if the last user-set vertical scrollbar is not at the end,
2472// or if one of the Go to Packet actions is used (XXX: Should keyboard
2473// navigation in keyPressEvent turn it off for similar reasons?)
2474void PacketList::rowsInserted(const QModelIndex &parent, int start, int end)
2475{
2476 QTreeView::rowsInserted(parent, start, end);
2477 if (recent.aggregation_view && currentIndex().isValid() && currentIndex().row() >= 0) {
2478 selectRow(getFDataForRow(currentIndex().row()), false);
2479 }
2480 if (capture_in_progress_ && tail_at_end_) {
2481 scrollToBottom();
2482 }
2483}
2484
2485void PacketList::resizeAllColumns(bool onlyTimeFormatted)
2486{
2487 if (!cap_file_ || cap_file_->state == FILE_CLOSED || cap_file_->state == FILE_READ_PENDING)
2488 return;
2489
2490 for (unsigned col = 0; col < cap_file_->cinfo.num_cols; col++) {
2491 if (! onlyTimeFormatted || col_has_time_fmt(&cap_file_->cinfo, col)) {
2492 resizeColumnToContents(col);
2493 }
2494 }
2495}
2496
2497bool PacketList::selectRow(const frame_data* fdata, bool flushRows)
2498{
2499 PacketListModel* pktListModel = qobject_cast<PacketListModel*>(model());
2500
2501 if (!pktListModel)
2502 return false;
2503
2504 if (flushRows) {
2505 pktListModel->flushVisibleRows();
2506 }
2507 int row = -1;
2508 if (!fdata)
2509 row = 0;
2510 else
2511 row = pktListModel->visibleIndexOf(fdata);
2512
2513 if (row >= 0) {
2514 /* Calling ClearAndSelect with setCurrentIndex clears the "current"
2515 * item, but doesn't clear the "selected" item. We want to clear
2516 * the "selected" item as well so that selectionChanged() will be
2517 * emitted in order to force an update of the packet details and
2518 * packet bytes after a search.
2519 */
2520 selectionModel()->clearSelection();
2521 selectionModel()->setCurrentIndex(pktListModel->index(row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
2522 scrollTo(currentIndex(), PacketList::PositionAtCenter);
2523 return true;
2524 }
2525
2526 return false;
2527}