Bug Summary

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