Bug Summary

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