Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

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