Bug Summary

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

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