Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

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