Bug Summary

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