3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "packet_list.h"
30 #include <epan/epan.h>
31 #include <epan/epan_dissect.h>
33 #include <epan/column-info.h>
34 #include <epan/column.h>
35 #include <epan/ipproto.h>
36 #include <epan/packet.h>
37 #include <epan/prefs.h>
39 #include "ui/main_statusbar.h"
40 #include "ui/packet_list_utils.h"
41 #include "ui/preference_utils.h"
42 #include "ui/recent.h"
43 #include "ui/recent_utils.h"
44 #include "ui/ui_util.h"
45 #include <wsutil/utf8_entities.h>
48 #include "wsutil/str_util.h"
51 #include "color_filters.h"
52 #include "frame_tvbuff.h"
54 #include "color_utils.h"
55 #include "overlay_scroll_bar.h"
56 #include "proto_tree.h"
57 #include "qt_ui_utils.h"
58 #include "wireshark_application.h"
61 #include <QActionGroup>
63 #include <QContextMenuEvent>
64 #include <QtCore/qmath.h>
65 #include <QElapsedTimer>
66 #include <QFontMetrics>
67 #include <QHeaderView>
68 #include <QMessageBox>
74 #include <QTimerEvent>
75 #include <QTreeWidget>
78 // - Fix "apply as filter" behavior.
79 // - Add colorize conversation.
80 // - Use a timer to trigger automatic scrolling.
82 // If we ever add the ability to open multiple capture files we might be
83 // able to use something like QMap<capture_file *, PacketList *> to match
84 // capture files against packet lists and models.
85 static PacketList *gbl_cur_packet_list = NULL;
87 const int max_comments_to_fetch_ = 20000000; // Arbitrary
88 const int tail_update_interval_ = 100; // Milliseconds.
89 const int overlay_update_interval_ = 100; // 250; // Milliseconds.
92 packet_list_append(column_info *, frame_data *fdata)
94 if (!gbl_cur_packet_list)
97 /* fdata should be filled with the stuff we need
98 * strings are built at display time.
102 visible_pos = gbl_cur_packet_list->packetListModel()->appendPacket(fdata);
106 // Copied from ui/gtk/packet_list.c
107 void packet_list_resize_column(gint col)
109 if (!gbl_cur_packet_list) return;
110 gbl_cur_packet_list->resizeColumnToContents(col);
114 packet_list_select_first_row(void)
116 if (!gbl_cur_packet_list)
118 gbl_cur_packet_list->goFirstPacket();
119 gbl_cur_packet_list->setFocus();
123 packet_list_select_last_row(void)
125 if (!gbl_cur_packet_list)
127 gbl_cur_packet_list->goLastPacket();
128 gbl_cur_packet_list->setFocus();
132 * Given a frame_data structure, scroll to and select the row in the
133 * packet list corresponding to that frame. If there is no such
134 * row, return FALSE, otherwise return TRUE.
137 packet_list_select_row_from_data(frame_data *fdata_needle)
139 gbl_cur_packet_list->packetListModel()->flushVisibleRows();
140 int row = gbl_cur_packet_list->packetListModel()->visibleIndexOf(fdata_needle);
142 gbl_cur_packet_list->setCurrentIndex(gbl_cur_packet_list->packetListModel()->index(row,0));
150 packet_list_check_end(void)
152 return FALSE; // GTK+ only.
156 packet_list_clear(void)
158 if (gbl_cur_packet_list) {
159 gbl_cur_packet_list->clear();
164 packet_list_enable_color(gboolean)
166 if (gbl_cur_packet_list) {
167 gbl_cur_packet_list->recolorPackets();
172 packet_list_freeze(void)
174 if (gbl_cur_packet_list) {
175 gbl_cur_packet_list->freeze();
180 packet_list_thaw(void)
182 if (gbl_cur_packet_list) {
183 gbl_cur_packet_list->thaw();
186 packets_bar_update();
190 packet_list_recreate_visible_rows(void)
192 if (gbl_cur_packet_list && gbl_cur_packet_list->packetListModel()) {
193 gbl_cur_packet_list->packetListModel()->recreateVisibleRows();
198 packet_list_get_row_data(gint row)
200 if (gbl_cur_packet_list && gbl_cur_packet_list->packetListModel()) {
201 return gbl_cur_packet_list->packetListModel()->getRowFdata(row);
206 // Called from cf_continue_tail and cf_finish_tail when auto_scroll_live
209 packet_list_moveto_end(void)
211 // gbl_cur_packet_list->scrollToBottom();
214 /* Redraw the packet list *and* currently-selected detail */
216 packet_list_queue_draw(void)
218 if (gbl_cur_packet_list)
219 gbl_cur_packet_list->redrawVisiblePackets();
223 packet_list_recent_write_all(FILE *rf) {
224 if (!gbl_cur_packet_list)
227 gbl_cur_packet_list->writeRecent(rf);
230 #define MIN_COL_WIDTH_STR "MMMMMM"
232 Q_DECLARE_METATYPE(PacketList::ColumnActions)
234 enum copy_summary_type {
240 PacketList::PacketList(QWidget *parent) :
243 byte_view_tab_(NULL),
247 overlay_timer_id_(0),
248 create_near_overlay_(true),
249 create_far_overlay_(true),
250 capture_in_progress_(false),
252 rows_inserted_(false),
253 columns_changed_(false),
254 set_column_visibility_(false)
256 QMenu *main_menu_item, *submenu;
259 setItemsExpandable(false);
260 setRootIsDecorated(false);
261 setSortingEnabled(true);
262 setUniformRowHeights(true);
263 setAccessibleName("Packet list");
265 overlay_sb_ = new OverlayScrollBar(Qt::Vertical, this);
266 setVerticalScrollBar(overlay_sb_);
268 packet_list_model_ = new PacketListModel(this, cap_file_);
269 setModel(packet_list_model_);
270 sortByColumn(-1, Qt::AscendingOrder);
272 // XXX We might want to reimplement setParent() and fill in the context
274 ctx_menu_.addAction(window()->findChild<QAction *>("actionEditMarkPacket"));
275 ctx_menu_.addAction(window()->findChild<QAction *>("actionEditIgnorePacket"));
276 ctx_menu_.addAction(window()->findChild<QAction *>("actionEditSetTimeReference"));
277 ctx_menu_.addAction(window()->findChild<QAction *>("actionEditTimeShift"));
278 ctx_menu_.addAction(window()->findChild<QAction *>("actionEditPacketComment"));
280 ctx_menu_.addSeparator();
282 ctx_menu_.addAction(window()->findChild<QAction *>("actionViewEditResolvedName"));
283 ctx_menu_.addSeparator();
285 main_menu_item = window()->findChild<QMenu *>("menuApplyAsFilter");
286 submenu = new QMenu(main_menu_item->title());
287 ctx_menu_.addMenu(submenu);
288 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFSelected"));
289 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFNotSelected"));
290 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFAndSelected"));
291 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFOrSelected"));
292 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFAndNotSelected"));
293 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFOrNotSelected"));
295 main_menu_item = window()->findChild<QMenu *>("menuPrepareAFilter");
296 submenu = new QMenu(main_menu_item->title());
297 ctx_menu_.addMenu(submenu);
298 submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFSelected"));
299 submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFNotSelected"));
300 submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFAndSelected"));
301 submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFOrSelected"));
302 submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFAndNotSelected"));
303 submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFOrNotSelected"));
305 const char *conv_menu_name = "menuConversationFilter";
306 main_menu_item = window()->findChild<QMenu *>(conv_menu_name);
307 conv_menu_.setTitle(main_menu_item->title());
308 conv_menu_.setObjectName(conv_menu_name);
309 ctx_menu_.addMenu(&conv_menu_);
311 const char *colorize_menu_name = "menuColorizeConversation";
312 main_menu_item = window()->findChild<QMenu *>(colorize_menu_name);
313 colorize_menu_.setTitle(main_menu_item->title());
314 colorize_menu_.setObjectName(colorize_menu_name);
315 ctx_menu_.addMenu(&colorize_menu_);
317 main_menu_item = window()->findChild<QMenu *>("menuSCTP");
318 submenu = new QMenu(main_menu_item->title());
319 ctx_menu_.addMenu(submenu);
320 submenu->addAction(window()->findChild<QAction *>("actionSCTPAnalyseThisAssociation"));
321 submenu->addAction(window()->findChild<QAction *>("actionSCTPShowAllAssociations"));
322 submenu->addAction(window()->findChild<QAction *>("actionSCTPFilterThisAssociation"));
324 main_menu_item = window()->findChild<QMenu *>("menuFollow");
325 submenu = new QMenu(main_menu_item->title());
326 ctx_menu_.addMenu(submenu);
327 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream"));
328 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream"));
329 submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowSSLStream"));
331 ctx_menu_.addSeparator();
333 main_menu_item = window()->findChild<QMenu *>("menuEditCopy");
334 submenu = new QMenu(main_menu_item->title());
335 ctx_menu_.addMenu(submenu);
337 action = submenu->addAction(tr("Summary as Text"));
338 action->setData(copy_summary_text_);
339 connect(action, SIGNAL(triggered()), this, SLOT(copySummary()));
340 action = submenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS "as CSV"));
341 action->setData(copy_summary_csv_);
342 connect(action, SIGNAL(triggered()), this, SLOT(copySummary()));
343 action = submenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS "as YAML"));
344 action->setData(copy_summary_yaml_);
345 connect(action, SIGNAL(triggered()), this, SLOT(copySummary()));
346 submenu->addSeparator();
348 submenu->addAction(window()->findChild<QAction *>("actionEditCopyAsFilter"));
349 submenu->addSeparator();
351 action = window()->findChild<QAction *>("actionContextCopyBytesHexTextDump");
352 submenu->addAction(action);
353 copy_actions_ << action;
354 action = window()->findChild<QAction *>("actionContextCopyBytesHexDump");
355 submenu->addAction(action);
356 copy_actions_ << action;
357 action = window()->findChild<QAction *>("actionContextCopyBytesPrintableText");
358 submenu->addAction(action);
359 copy_actions_ << action;
360 action = window()->findChild<QAction *>("actionContextCopyBytesHexStream");
361 submenu->addAction(action);
362 copy_actions_ << action;
363 action = window()->findChild<QAction *>("actionContextCopyBytesBinary");
364 submenu->addAction(action);
365 copy_actions_ << action;
367 ctx_menu_.addSeparator();
368 ctx_menu_.addMenu(&proto_prefs_menu_);
369 decode_as_ = window()->findChild<QAction *>("actionAnalyzeDecodeAs");
370 ctx_menu_.addAction(decode_as_);
371 // "Print" not ported intentionally
372 action = window()->findChild<QAction *>("actionViewShowPacketInNewWindow");
373 ctx_menu_.addAction(action);
375 initHeaderContextMenu();
377 g_assert(gbl_cur_packet_list == NULL);
378 gbl_cur_packet_list = this;
380 connect(packet_list_model_, SIGNAL(goToPacket(int)), this, SLOT(goToPacket(int)));
381 connect(packet_list_model_, SIGNAL(itemHeightChanged(const QModelIndex&)), this, SLOT(updateRowHeights(const QModelIndex&)));
382 connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(redrawVisiblePackets()));
384 header()->setContextMenuPolicy(Qt::CustomContextMenu);
385 connect(header(), SIGNAL(customContextMenuRequested(QPoint)),
386 this, SLOT(showHeaderMenu(QPoint)));
387 connect(header(), SIGNAL(sectionResized(int,int,int)),
388 this, SLOT(sectionResized(int,int,int)));
389 connect(header(), SIGNAL(sectionMoved(int,int,int)),
390 this, SLOT(sectionMoved(int,int,int)));
392 connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(vScrollBarActionTriggered(int)));
394 connect(&proto_prefs_menu_, SIGNAL(showProtocolPreferences(QString)),
395 this, SIGNAL(showProtocolPreferences(QString)));
396 connect(&proto_prefs_menu_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
397 this, SIGNAL(editProtocolPreference(preference*,pref_module*)));
400 void PacketList::drawRow (QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
402 QTreeView::drawRow(painter, option, index);
404 if (prefs.gui_qt_packet_list_separator) {
405 QRect rect = visualRect(index);
407 painter->setPen(QColor(Qt::white));
408 painter->drawLine(0, rect.y() + rect.height() - 1, width(), rect.y() + rect.height() - 1);
412 void PacketList::setProtoTree (ProtoTree *proto_tree) {
413 proto_tree_ = proto_tree;
415 connect(proto_tree_, SIGNAL(goToPacket(int)), this, SLOT(goToPacket(int)));
416 connect(proto_tree_, SIGNAL(relatedFrame(int,ft_framenum_type_t)),
417 &related_packet_delegate_, SLOT(addRelatedFrame(int,ft_framenum_type_t)));
420 void PacketList::setByteViewTab (ByteViewTab *byte_view_tab) {
421 byte_view_tab_ = byte_view_tab;
423 connect(proto_tree_, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
424 byte_view_tab_, SLOT(protoTreeItemChanged(QTreeWidgetItem*)));
427 PacketListModel *PacketList::packetListModel() const {
428 return packet_list_model_;
431 void PacketList::showEvent (QShowEvent *) {
432 setColumnVisibility();
435 void PacketList::selectionChanged (const QItemSelection & selected, const QItemSelection & deselected) {
436 QTreeView::selectionChanged(selected, deselected);
438 if (!cap_file_) return;
440 if (selected.isEmpty()) {
441 cf_unselect_packet(cap_file_);
443 int row = selected.first().top();
444 cf_select_packet(cap_file_, row);
447 related_packet_delegate_.clear();
448 if (proto_tree_) proto_tree_->clear();
449 if (byte_view_tab_) byte_view_tab_->clear();
451 emit packetSelectionChanged();
453 if (!cap_file_->edt) {
454 viewport()->update();
458 if (proto_tree_ && cap_file_->edt->tree) {
459 packet_info *pi = &cap_file_->edt->pi;
460 related_packet_delegate_.setCurrentFrame(pi->fd->num);
461 proto_tree_->fillProtocolTree(cap_file_->edt->tree);
462 conversation_t *conv = find_conversation(pi->fd->num, &pi->src, &pi->dst, pi->ptype,
463 pi->srcport, pi->destport, 0);
465 related_packet_delegate_.setConversation(conv);
467 viewport()->update();
470 if (byte_view_tab_) {
472 struct data_source *source;
475 for (src_le = cap_file_->edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
476 source = (struct data_source *)src_le->data;
477 source_name = get_data_source_name(source);
478 byte_view_tab_->addTab(source_name, get_data_source_tvb(source), cap_file_->edt->tree, proto_tree_, (packet_char_enc)cap_file_->current_frame->flags.encoding);
479 wmem_free(NULL, source_name);
481 byte_view_tab_->setCurrentIndex(0);
485 void PacketList::contextMenuEvent(QContextMenuEvent *event)
487 const char *module_name = NULL;
488 if (cap_file_ && cap_file_->edt && cap_file_->edt->tree) {
489 GPtrArray *finfo_array = proto_all_finfos(cap_file_->edt->tree);
491 for (guint i = finfo_array->len - 1; i > 0 ; i --) {
492 field_info *fi = (field_info *)g_ptr_array_index (finfo_array, i);
493 header_field_info *hfinfo = fi->hfinfo;
495 if (!g_str_has_prefix(hfinfo->abbrev, "text") &&
496 !g_str_has_prefix(hfinfo->abbrev, "_ws.expert") &&
497 !g_str_has_prefix(hfinfo->abbrev, "_ws.malformed")) {
499 if (hfinfo->parent == -1) {
500 module_name = hfinfo->abbrev;
502 module_name = proto_registrar_get_abbrev(hfinfo->parent);
508 proto_prefs_menu_.setModule(module_name);
510 foreach (QAction *action, copy_actions_) {
511 action->setData(QVariant());
514 decode_as_->setData(qVariantFromValue(true));
515 ctx_column_ = columnAt(event->x());
517 // Set menu sensitivity for the current column and set action data.
518 emit packetSelectionChanged();
520 ctx_menu_.exec(event->globalPos());
522 decode_as_->setData(QVariant());
527 // - We're not at the end
528 // - We are capturing
529 // - actionGoAutoScroll in the main UI is checked.
530 // - It's been more than tail_update_interval_ ms since we last scrolled
531 // - The last user-set vertical scrollbar position was at the end.
533 // Using a timer assumes that we can save CPU overhead by updating
534 // periodically. If that's not the case we can dispense with it and call
535 // scrollToBottom() from rowsInserted().
536 void PacketList::timerEvent(QTimerEvent *event)
538 QTreeView::timerEvent(event);
540 if (event->timerId() == tail_timer_id_
542 && capture_in_progress_
545 rows_inserted_ = false;
546 } else if (event->timerId() == overlay_timer_id_ && !capture_in_progress_) {
547 if (create_near_overlay_) drawNearOverlay();
548 if (create_far_overlay_) drawFarOverlay();
552 void PacketList::paintEvent(QPaintEvent *event)
554 // XXX This is overkill, but there are quite a few events that
555 // require a new overlay, e.g. page up/down, scrolling, column
557 create_near_overlay_ = true;
558 QTreeView::paintEvent(event);
561 void PacketList::mousePressEvent (QMouseEvent *event)
563 setAutoScroll(false);
564 QTreeView::mousePressEvent(event);
568 void PacketList::setColumnVisibility()
570 set_column_visibility_ = true;
571 for (int i = 0; i < prefs.num_cols; i++) {
572 setColumnHidden(i, get_column_visible(i) ? false : true);
574 set_column_visibility_ = false;
577 int PacketList::sizeHintForColumn(int column) const
581 // This is a bit hacky but Qt does a fine job of column sizing and
582 // reimplementing QTreeView::sizeHintForColumn seems like a worse idea.
583 if (itemDelegateForColumn(column)) {
584 // In my (gcc) testing this results in correct behavior on Windows but adds extra space
585 // on OS X and Linux. We might want to add Q_OS_... #ifdefs accordingly.
586 size_hint = itemDelegateForColumn(column)->sizeHint(viewOptions(), QModelIndex()).width();
588 size_hint += QTreeView::sizeHintForColumn(column); // Decoration padding
592 void PacketList::setRecentColumnWidth(int col)
594 int col_width = recent_get_column_width(col);
597 int fmt = get_column_format(col);
598 const char *long_str = get_column_width_string(fmt, col);
600 QFontMetrics fm = QFontMetrics(wsApp->monospaceFont());
602 col_width = fm.width(long_str);
604 col_width = fm.width(MIN_COL_WIDTH_STR);
607 // Custom delegate padding
608 if (itemDelegateForColumn(col)) {
609 col_width += itemDelegateForColumn(col)->sizeHint(viewOptions(), QModelIndex()).width();
613 setColumnWidth(col, col_width);
616 void PacketList::initHeaderContextMenu()
618 header_ctx_menu_.clear();
619 header_actions_.clear();
621 // Leave these out for now since Qt doesn't have a "no sort" option
622 // and the user can sort by left-clicking on the header.
623 // header_actions_[] = header_ctx_menu_.addAction(tr("Sort Ascending"));
624 // header_actions_[] = header_ctx_menu_.addAction(tr("Sort Descending"));
625 // header_actions_[] = header_ctx_menu_.addAction(tr("Do Not Sort"));
626 // header_ctx_menu_.addSeparator();
627 header_actions_[caAlignLeft] = header_ctx_menu_.addAction(tr("Align Left"));
628 header_actions_[caAlignCenter] = header_ctx_menu_.addAction(tr("Align Center"));
629 header_actions_[caAlignRight] = header_ctx_menu_.addAction(tr("Align Right"));
630 header_ctx_menu_.addSeparator();
631 header_actions_[caColumnPreferences] = header_ctx_menu_.addAction(tr("Column Preferences" UTF8_HORIZONTAL_ELLIPSIS));
632 header_actions_[caEditColumn] = header_ctx_menu_.addAction(tr("Edit Column")); // XXX Create frame instead of dialog
633 header_actions_[caResizeToContents] = header_ctx_menu_.addAction(tr("Resize To Contents"));
634 header_actions_[caResolveNames] = header_ctx_menu_.addAction(tr("Resolve Names"));
635 header_ctx_menu_.addSeparator();
636 // header_actions_[caDisplayedColumns] = header_ctx_menu_.addAction(tr("Displayed Columns"));
637 show_hide_separator_ = header_ctx_menu_.addSeparator();
638 // header_actions_[caHideColumn] = header_ctx_menu_.addAction(tr("Hide This Column"));
639 header_actions_[caRemoveColumn] = header_ctx_menu_.addAction(tr("Remove This Column"));
641 foreach (ColumnActions ca, header_actions_.keys()) {
642 header_actions_[ca]->setData(qVariantFromValue(ca));
643 connect(header_actions_[ca], SIGNAL(triggered()), this, SLOT(headerMenuTriggered()));
646 checkable_actions_ = QList<ColumnActions>() << caAlignLeft << caAlignCenter << caAlignRight << caResolveNames;
647 foreach (ColumnActions ca, checkable_actions_) {
648 header_actions_[ca]->setCheckable(true);
652 void PacketList::drawCurrentPacket()
654 QModelIndex current_index = currentIndex();
655 setCurrentIndex(QModelIndex());
656 if (current_index.isValid()) {
657 setCurrentIndex(current_index);
661 // Redraw the packet list and detail. Called from many places.
662 // XXX We previously re-selected the packet here, but that seems to cause
663 // automatic scrolling problems.
664 void PacketList::redrawVisiblePackets() {
670 // prefs.col_list has changed.
671 void PacketList::columnsChanged()
673 columns_changed_ = true;
675 // Keep columns_changed_ = true until we load a capture file.
679 prefs.num_cols = g_list_length(prefs.col_list);
680 col_cleanup(&cap_file_->cinfo);
681 build_column_format_array(&cap_file_->cinfo, prefs.num_cols, FALSE);
682 setColumnVisibility();
683 create_far_overlay_ = true;
684 packet_list_model_->resetColumns();
685 columns_changed_ = false;
688 // Fields have changed, update custom columns
689 void PacketList::fieldsChanged(capture_file *cf)
691 prefs.num_cols = g_list_length(prefs.col_list);
692 col_cleanup(&cf->cinfo);
693 build_column_format_array(&cf->cinfo, prefs.num_cols, FALSE);
694 // call packet_list_model_->resetColumns() ?
697 // Column widths should
698 // - Load from recent when we load a new profile (including at starting up).
699 // - Persist across freezes and thaws.
700 // - Persist across file closing and opening.
701 // - Save to recent when we save our profile (including shutting down).
703 // Called via recentFilesRead.
704 void PacketList::applyRecentColumnWidths()
706 // Either we've just started up or a profile has changed. Read
707 // the recent settings, apply them, and save the header state.
708 for (int col = 0; col < prefs.num_cols; col++) {
709 setRecentColumnWidth(col);
711 column_state_ = header()->saveState();
714 void PacketList::preferencesChanged()
716 // Related packet delegate
717 if (prefs.gui_packet_list_show_related) {
718 setItemDelegateForColumn(0, &related_packet_delegate_);
720 setItemDelegateForColumn(0, 0);
723 // Intelligent scroll bar (minimap)
724 if (prefs.gui_packet_list_show_minimap) {
725 if (overlay_timer_id_ == 0) {
726 overlay_timer_id_ = startTimer(overlay_update_interval_);
729 if (overlay_timer_id_ != 0) {
730 killTimer(overlay_timer_id_);
731 overlay_timer_id_ = 0;
736 // This sets the mode for the entire view. If we want to make this setting
737 // per-column we'll either have to generalize RelatedPacketDelegate so that
738 // we can set it for entire rows or create another delegate.
739 Qt::TextElideMode elide_mode = Qt::ElideRight;
740 switch (prefs.gui_packet_list_elide_mode) {
742 elide_mode = Qt::ElideLeft;
745 elide_mode = Qt::ElideMiddle;
748 elide_mode = Qt::ElideNone;
753 setTextElideMode(elide_mode);
756 void PacketList::recolorPackets()
758 packet_list_model_->resetColorized();
759 redrawVisiblePackets();
762 /* Enable autoscroll timer. Note: must be called after the capture is started,
763 * otherwise the timer will not be executed. */
764 void PacketList::setVerticalAutoScroll(bool enabled)
766 tail_at_end_ = enabled;
767 if (enabled && capture_in_progress_) {
769 if (tail_timer_id_ == 0) tail_timer_id_ = startTimer(tail_update_interval_);
770 } else if (tail_timer_id_ != 0) {
771 killTimer(tail_timer_id_);
776 // Called when we finish reading, reloading, rescanning, and retapping
778 void PacketList::captureFileReadFinished()
780 packet_list_model_->flushVisibleRows();
781 packet_list_model_->dissectIdle(true);
784 void PacketList::freeze()
786 setUpdatesEnabled(false);
787 column_state_ = header()->saveState();
789 // It looks like GTK+ sends a cursor-changed signal at this point but Qt doesn't
790 // call selectionChanged.
791 related_packet_delegate_.clear();
792 proto_tree_->clear();
793 byte_view_tab_->clear();
796 void PacketList::thaw()
798 setUpdatesEnabled(true);
799 setModel(packet_list_model_);
801 // Resetting the model resets our column widths so we restore them here.
802 // We don't reapply the recent settings because the user could have
803 // resized the columns manually since they were initially loaded.
804 header()->restoreState(column_state_);
806 setColumnVisibility();
809 void PacketList::clear() {
810 // packet_history_clear();
811 related_packet_delegate_.clear();
812 packet_list_model_->clear();
813 proto_tree_->clear();
814 byte_view_tab_->clear();
817 overlay_sb_->setNearOverlayImage(overlay);
818 overlay_sb_->setFarOverlayImage(overlay);
819 create_near_overlay_ = true;
820 create_far_overlay_ = true;
822 setColumnVisibility();
825 void PacketList::writeRecent(FILE *rf) {
826 gint col, width, col_fmt;
829 fprintf (rf, "%s:", RECENT_KEY_COL_WIDTH);
830 for (col = 0; col < prefs.num_cols; col++) {
834 col_fmt = get_column_format(col);
835 if (col_fmt == COL_CUSTOM) {
836 fprintf (rf, " %%Cus:%s,", get_column_custom_field(col));
838 fprintf (rf, " %s,", col_format_to_string(col_fmt));
840 width = recent_get_column_width (col);
841 xalign = recent_get_column_xalign (col);
842 fprintf (rf, " %d", width);
843 if (xalign != COLUMN_XALIGN_DEFAULT) {
844 fprintf (rf, ":%c", xalign);
851 bool PacketList::contextMenuActive()
853 return ctx_column_ >= 0 ? true : false;
856 QString PacketList::getFilterFromRowAndColumn()
860 int row = currentIndex().row();
862 if (!cap_file_ || !packet_list_model_ || ctx_column_ < 0 || ctx_column_ >= cap_file_->cinfo.num_cols) return filter;
864 fdata = packet_list_model_->getRowFdata(row);
869 if (!cf_read_record(cap_file_, fdata))
870 return filter; /* error reading the record */
871 /* proto tree, visible. We need a proto tree if there's custom columns */
872 epan_dissect_init(&edt, cap_file_->epan, have_custom_cols(&cap_file_->cinfo), FALSE);
873 col_custom_prime_edt(&edt, &cap_file_->cinfo);
875 epan_dissect_run(&edt, cap_file_->cd_t, &cap_file_->phdr, frame_tvbuff_new_buffer(fdata, &cap_file_->buf), fdata, &cap_file_->cinfo);
876 epan_dissect_fill_in_columns(&edt, TRUE, TRUE);
878 if ((cap_file_->cinfo.columns[ctx_column_].col_custom_occurrence) ||
879 (strchr (cap_file_->cinfo.col_expr.col_expr_val[ctx_column_], ',') == NULL))
881 /* Only construct the filter when a single occurrence is displayed
882 * otherwise we might end up with a filter like "ip.proto==1,6".
884 * Or do we want to be able to filter on multiple occurrences so that
885 * the filter might be calculated as "ip.proto==1 && ip.proto==6"
888 if (strlen(cap_file_->cinfo.col_expr.col_expr[ctx_column_]) != 0 &&
889 strlen(cap_file_->cinfo.col_expr.col_expr_val[ctx_column_]) != 0) {
890 if (cap_file_->cinfo.columns[ctx_column_].col_fmt == COL_CUSTOM) {
891 header_field_info *hfi = proto_registrar_get_byname(cap_file_->cinfo.columns[ctx_column_].col_custom_field);
892 if (hfi->parent == -1) {
894 filter.append(cap_file_->cinfo.col_expr.col_expr[ctx_column_]);
895 } else if (hfi->type == FT_STRING) {
896 /* Custom string, add quotes */
897 filter.append(QString("%1 == \"%2\"")
898 .arg(cap_file_->cinfo.col_expr.col_expr[ctx_column_])
899 .arg(cap_file_->cinfo.col_expr.col_expr_val[ctx_column_]));
902 if (filter.isEmpty()) {
903 filter.append(QString("%1 == %2")
904 .arg(cap_file_->cinfo.col_expr.col_expr[ctx_column_])
905 .arg(cap_file_->cinfo.col_expr.col_expr_val[ctx_column_]));
910 epan_dissect_cleanup(&edt);
916 void PacketList::resetColorized()
918 packet_list_model_->resetColorized();
922 QString PacketList::packetComment()
924 int row = currentIndex().row();
925 const frame_data *fdata;
928 if (!cap_file_ || !packet_list_model_) return NULL;
930 fdata = packet_list_model_->getRowFdata(row);
932 if (!fdata) return NULL;
934 pkt_comment = cf_get_comment(cap_file_, fdata);
936 return QString(pkt_comment);
938 /* XXX, g_free(pkt_comment) */
941 void PacketList::setPacketComment(QString new_comment)
943 int row = currentIndex().row();
945 gchar *new_packet_comment;
947 if (!cap_file_ || !packet_list_model_) return;
949 fdata = packet_list_model_->getRowFdata(row);
953 /* Check if we are clearing the comment */
954 if(new_comment.isEmpty()) {
955 new_packet_comment = NULL;
957 new_packet_comment = qstring_strdup(new_comment);
960 cf_set_user_packet_comment(cap_file_, fdata, new_packet_comment);
961 g_free(new_packet_comment);
963 redrawVisiblePackets();
966 QString PacketList::allPacketComments()
972 if (!cap_file_) return buf_str;
974 for (framenum = 1; framenum <= cap_file_->count ; framenum++) {
975 fdata = frame_data_sequence_find(cap_file_->frames, framenum);
977 char *pkt_comment = cf_get_comment(cap_file_, fdata);
980 buf_str.append(QString(tr("Frame %1: %2\n\n")).arg(framenum).arg(pkt_comment));
983 if (buf_str.length() > max_comments_to_fetch_) {
984 buf_str.append(QString(tr("[ Comment text exceeds %1. Stopping. ]"))
985 .arg(format_size(max_comments_to_fetch_, format_size_unit_bytes|format_size_prefix_si)));
994 void PacketList::setCaptureFile(capture_file *cf)
997 // We're opening. Restore our column widths.
998 header()->restoreState(column_state_);
1001 if (cap_file_ && columns_changed_) {
1003 applyRecentColumnWidths();
1005 packet_list_model_->setCaptureFile(cf);
1006 create_near_overlay_ = true;
1009 void PacketList::setMonospaceFont(const QFont &mono_font)
1012 header()->setFont(wsApp->font());
1015 void PacketList::goNextPacket(void) {
1016 if (selectionModel()->hasSelection()) {
1017 setCurrentIndex(moveCursor(MoveDown, Qt::NoModifier));
1019 // First visible packet.
1020 setCurrentIndex(indexAt(viewport()->rect().topLeft()));
1024 void PacketList::goPreviousPacket(void) {
1025 if (selectionModel()->hasSelection()) {
1026 setCurrentIndex(moveCursor(MoveUp, Qt::NoModifier));
1028 // Last visible packet.
1029 QModelIndex last_idx = indexAt(viewport()->rect().bottomLeft());
1030 if (last_idx.isValid()) {
1031 setCurrentIndex(last_idx);
1038 void PacketList::goFirstPacket(void) {
1039 if (packet_list_model_->rowCount() < 1) return;
1040 setCurrentIndex(packet_list_model_->index(0, 0));
1041 scrollTo(currentIndex());
1044 void PacketList::goLastPacket(void) {
1045 if (packet_list_model_->rowCount() < 1) return;
1046 setCurrentIndex(packet_list_model_->index(packet_list_model_->rowCount() - 1, 0));
1047 scrollTo(currentIndex());
1050 // XXX We can jump to the wrong packet if a display filter is applied
1051 void PacketList::goToPacket(int packet) {
1052 if (!cf_goto_frame(cap_file_, packet)) return;
1053 int row = packet_list_model_->packetNumberToRow(packet);
1055 setCurrentIndex(packet_list_model_->index(row, 0));
1059 void PacketList::goToPacket(int packet, int hf_id)
1062 proto_tree_->goToField(hf_id);
1065 void PacketList::markFrame()
1067 if (!cap_file_ || !packet_list_model_) return;
1069 packet_list_model_->toggleFrameMark(currentIndex());
1070 create_far_overlay_ = true;
1071 packets_bar_update();
1074 void PacketList::markAllDisplayedFrames(bool set)
1076 if (!cap_file_ || !packet_list_model_) return;
1078 packet_list_model_->setDisplayedFrameMark(set);
1079 create_far_overlay_ = true;
1080 packets_bar_update();
1083 void PacketList::ignoreFrame()
1085 if (!cap_file_ || !packet_list_model_) return;
1087 packet_list_model_->toggleFrameIgnore(currentIndex());
1088 create_far_overlay_ = true;
1089 int sb_val = verticalScrollBar()->value(); // Surely there's a better way to keep our position?
1090 setUpdatesEnabled(false);
1091 emit packetDissectionChanged();
1092 setUpdatesEnabled(true);
1093 verticalScrollBar()->setValue(sb_val);
1096 void PacketList::ignoreAllDisplayedFrames(bool set)
1098 if (!cap_file_ || !packet_list_model_) return;
1100 packet_list_model_->setDisplayedFrameIgnore(set);
1101 create_far_overlay_ = true;
1102 emit packetDissectionChanged();
1105 void PacketList::setTimeReference()
1107 if (!cap_file_ || !packet_list_model_) return;
1108 packet_list_model_->toggleFrameRefTime(currentIndex());
1109 create_far_overlay_ = true;
1112 void PacketList::unsetAllTimeReferences()
1114 if (!cap_file_ || !packet_list_model_) return;
1115 packet_list_model_->unsetAllFrameRefTime();
1116 create_far_overlay_ = true;
1119 void PacketList::applyTimeShift()
1121 packet_list_model_->applyTimeShift();
1122 redrawVisiblePackets();
1123 // XXX emit packetDissectionChanged(); ?
1126 void PacketList::showHeaderMenu(QPoint pos)
1128 header_ctx_column_ = header()->logicalIndexAt(pos);
1129 foreach (ColumnActions ca, checkable_actions_) {
1130 header_actions_[ca]->setChecked(false);
1133 switch (recent_get_column_xalign(header_ctx_column_)) {
1134 case COLUMN_XALIGN_LEFT:
1135 header_actions_[caAlignLeft]->setChecked(true);
1137 case COLUMN_XALIGN_CENTER:
1138 header_actions_[caAlignCenter]->setChecked(true);
1140 case COLUMN_XALIGN_RIGHT:
1141 header_actions_[caAlignRight]->setChecked(true);
1147 bool can_resolve = resolve_column(header_ctx_column_, cap_file_);
1148 header_actions_[caResolveNames]->setChecked(can_resolve && get_column_resolved(header_ctx_column_));
1149 header_actions_[caResolveNames]->setEnabled(can_resolve);
1151 header_actions_[caRemoveColumn]->setEnabled(header_ctx_column_ >= 0 && header()->count() > 2);
1153 foreach (QAction *action, show_hide_actions_) {
1154 header_ctx_menu_.removeAction(action);
1157 show_hide_actions_.clear();
1158 for (int i = 0; i < prefs.num_cols; i++) {
1159 QAction *action = new QAction(get_column_title(i), &header_ctx_menu_);
1160 action->setCheckable(true);
1161 action->setChecked(get_column_visible(i));
1162 action->setData(qVariantFromValue(i));
1163 connect(action, SIGNAL(triggered()), this, SLOT(columnVisibilityTriggered()));
1164 header_ctx_menu_.insertAction(show_hide_separator_, action);
1165 show_hide_actions_ << action;
1168 header_ctx_menu_.popup(header()->viewport()->mapToGlobal(pos));
1171 void PacketList::headerMenuTriggered()
1173 QAction *ha = qobject_cast<QAction*>(sender());
1176 bool checked = ha->isChecked();
1177 bool redraw = false;
1179 switch(ha->data().value<ColumnActions>()) {
1181 recent_set_column_xalign(header_ctx_column_, checked ? COLUMN_XALIGN_LEFT : COLUMN_XALIGN_DEFAULT);
1184 recent_set_column_xalign(header_ctx_column_, checked ? COLUMN_XALIGN_CENTER : COLUMN_XALIGN_DEFAULT);
1187 recent_set_column_xalign(header_ctx_column_, checked ? COLUMN_XALIGN_RIGHT : COLUMN_XALIGN_DEFAULT);
1189 case caColumnPreferences:
1190 emit showColumnPreferences(PreferencesDialog::ppColumn);
1193 emit editColumn(header_ctx_column_);
1195 case caResolveNames:
1196 set_column_resolved(header_ctx_column_, checked);
1197 packet_list_model_->resetColumns();
1198 if (!prefs.gui_use_pref_save) {
1203 case caResizeToContents:
1204 resizeColumnToContents(header_ctx_column_);
1206 case caDisplayedColumns:
1210 set_column_visible(header_ctx_column_, FALSE);
1211 hideColumn(header_ctx_column_);
1212 if (!prefs.gui_use_pref_save) {
1216 case caRemoveColumn:
1218 if (header()->count() > 2) {
1219 column_prefs_remove_nth(header_ctx_column_);
1221 if (!prefs.gui_use_pref_save) {
1232 redrawVisiblePackets();
1238 void PacketList::columnVisibilityTriggered()
1240 QAction *ha = qobject_cast<QAction*>(sender());
1243 int col = ha->data().toInt();
1244 set_column_visible(col, ha->isChecked());
1245 setColumnVisibility();
1246 if (ha->isChecked()) {
1247 setRecentColumnWidth(col);
1249 if (!prefs.gui_use_pref_save) {
1254 void PacketList::sectionResized(int col, int, int new_width)
1256 if (isVisible() && !columns_changed_ && !set_column_visibility_ && new_width > 0) {
1257 // Column 1 gets an invalid value (32 on OS X) when we're not yet
1260 // Don't set column width when columns changed or setting column
1261 // visibility because we may get a sectionReized() from QTreeView
1262 // with values from a old columns layout.
1264 // Don't set column width when hiding a column.
1266 recent_set_column_width(col, new_width);
1270 // The user moved a column. Make sure prefs.col_list, the column format
1271 // array, and the header's visual and logical indices all agree.
1272 // gtk/packet_list.c:column_dnd_changed_cb
1273 void PacketList::sectionMoved(int, int, int)
1275 GList *new_col_list = NULL;
1276 QList<int> saved_sizes;
1278 // Build a new column list based on the header's logical order.
1279 for (int vis_idx = 0; vis_idx < header()->count(); vis_idx++) {
1280 int log_idx = header()->logicalIndex(vis_idx);
1281 saved_sizes << header()->sectionSize(log_idx);
1283 void *pref_data = g_list_nth_data(prefs.col_list, log_idx);
1284 if (!pref_data) continue;
1286 new_col_list = g_list_append(new_col_list, pref_data);
1289 // Clear and rebuild our (and the header's) model. There doesn't appear
1290 // to be another way to reset the logical index.
1293 g_list_free(prefs.col_list);
1294 prefs.col_list = new_col_list;
1298 for (int i = 0; i < saved_sizes.length(); i++) {
1299 if (saved_sizes[i] < 1) continue;
1300 header()->resizeSection(i, saved_sizes[i]);
1303 if (!prefs.gui_use_pref_save) {
1307 wsApp->emitAppSignal(WiresharkApplication::ColumnsChanged);
1310 void PacketList::updateRowHeights(const QModelIndex &ih_index)
1312 QStyleOptionViewItem option = viewOptions();
1315 // One of our columns increased the maximum row height. Find out which one.
1316 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
1317 QSize size_hint = itemDelegate()->sizeHint(option, packet_list_model_->index(ih_index.row(), col));
1318 max_height = qMax(max_height, size_hint.height());
1321 if (max_height > 0) {
1322 packet_list_model_->setMaximiumRowHeight(max_height);
1326 void PacketList::copySummary()
1328 if (!currentIndex().isValid()) return;
1330 QAction *ca = qobject_cast<QAction*>(sender());
1334 int copy_type = ca->data().toInt(&ok);
1337 QStringList col_parts;
1338 int row = currentIndex().row();
1339 for (int col = 0; col < packet_list_model_->columnCount(); col++) {
1340 col_parts << packet_list_model_->data(packet_list_model_->index(row, col), Qt::DisplayRole).toString();
1344 switch (copy_type) {
1345 case copy_summary_csv_:
1347 copy_text += col_parts.join("\",\"");
1350 case copy_summary_yaml_:
1351 copy_text = "----\n";
1352 copy_text += QString("# Packet %1 from %2\n").arg(row).arg(cap_file_->filename);
1354 copy_text += col_parts.join("\n- ");
1357 case copy_summary_text_:
1359 copy_text = col_parts.join("\t");
1361 wsApp->clipboard()->setText(copy_text);
1364 // We need to tell when the user has scrolled the packet list, either to
1365 // the end or anywhere other than the end.
1366 void PacketList::vScrollBarActionTriggered(int)
1368 // If we're scrolling with a mouse wheel or trackpad sliderPosition can end up
1370 tail_at_end_ = (verticalScrollBar()->sliderPosition() >= verticalScrollBar()->maximum());
1372 if (capture_in_progress_ && prefs.capture_auto_scroll) {
1373 emit packetListScrolled(tail_at_end_);
1377 // Goal: Overlay the packet list scroll bar with the colors of all of the
1379 // Try 1: Average packet colors in each scroll bar raster line. This has
1380 // two problems: It's easy to wash out colors and we dissect every packet.
1381 // Try 2: Color across a 5000 or 10000 packet window. We still end up washing
1383 // Try 3: One packet per vertical scroll bar pixel. This seems to work best
1384 // but has the smallest window.
1385 // Try 4: Use a multiple of the scroll bar heigh and scale the image down
1386 // using Qt::SmoothTransformation. This gives us more packets per raster
1389 // Odd (prime?) numbers resulted in fewer scaling artifacts. A multiplier
1390 // of 9 washed out colors a little too much.
1391 const int height_multiplier_ = 7;
1392 void PacketList::drawNearOverlay()
1394 if (create_near_overlay_) {
1395 create_near_overlay_ = false;
1398 if (!cap_file_ || cap_file_->state != FILE_READ_DONE) return;
1400 if (!prefs.gui_packet_list_show_minimap) return;
1402 qreal dp_ratio = 1.0;
1403 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
1404 dp_ratio = overlay_sb_->devicePixelRatio();
1406 int o_height = overlay_sb_->height() * dp_ratio * height_multiplier_;
1407 int o_rows = qMin(packet_list_model_->rowCount(), o_height);
1408 int selected_pos = -1;
1410 if (recent.packet_list_colorize && o_rows > 0) {
1411 QImage overlay(1, o_height, QImage::Format_ARGB32_Premultiplied);
1413 QPainter painter(&overlay);
1415 QElapsedTimer timer;
1419 overlay.fill(Qt::transparent);
1424 if (packet_list_model_->rowCount() > o_height && overlay_sb_->maximum() > 0) {
1425 start += ((double) overlay_sb_->value() / overlay_sb_->maximum()) * (packet_list_model_->rowCount() - o_rows);
1427 int end = start + o_rows;
1428 for (int row = start; row < end; row++) {
1429 packet_list_model_->ensureRowColorized(row);
1432 // Try to remain responsive for large captures.
1433 if (timer.elapsed() > update_time_) {
1434 wsApp->processEvents();
1435 if (!cap_file_ || cap_file_->state != FILE_READ_DONE) {
1436 create_overlay_ = true;
1443 frame_data *fdata = packet_list_model_->getRowFdata(row);
1444 const color_t *bgcolor = NULL;
1445 if (fdata->color_filter) {
1446 const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
1447 bgcolor = &color_filter->bg_color;
1450 int next_line = (row - start) * o_height / o_rows;
1452 QColor color(ColorUtils::fromColorT(bgcolor));
1454 painter.setPen(color);
1455 painter.drawLine(0, cur_line, 0, next_line);
1457 cur_line = next_line;
1460 // If the selected packet is in the overlay set selected_pos
1461 // accordingly. Otherwise, pin it to either the top or bottom.
1462 if (selectionModel()->hasSelection()) {
1463 int sel_row = selectionModel()->currentIndex().row();
1464 if (sel_row < start) {
1466 } else if (sel_row >= end) {
1467 selected_pos = overlay.height() - 1;
1469 selected_pos = (sel_row - start) * o_height / o_rows;
1473 overlay_sb_->setNearOverlayImage(overlay, selected_pos);
1476 overlay_sb_->setNearOverlayImage(overlay);
1480 void PacketList::drawFarOverlay()
1482 if (create_far_overlay_) {
1483 create_far_overlay_ = false;
1486 if (!cap_file_ || cap_file_->state != FILE_READ_DONE) return;
1488 if (!prefs.gui_packet_list_show_minimap) return;
1490 qreal dp_ratio = 1.0;
1491 #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
1492 dp_ratio = overlay_sb_->devicePixelRatio();
1494 int o_width = 2 * dp_ratio;
1495 int o_height = overlay_sb_->height() * dp_ratio;
1496 int pl_rows = packet_list_model_->rowCount();
1498 if (recent.packet_list_colorize && pl_rows > 0) {
1499 // Create a tall image here. OverlayScrollBar will scale it to fit.
1500 QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied);
1502 QPainter painter(&overlay);
1503 painter.setRenderHint(QPainter::Antialiasing);
1505 QElapsedTimer timer;
1509 // The default "marked" background is black and the default "ignored"
1510 // background is white. Instead of trying to figure out if our
1511 // available colors will show up, just use the palette's background
1512 // here and foreground below.
1513 #if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
1514 overlay.fill(palette().base().color().value());
1516 overlay.fill(palette().base().color());
1518 QColor arrow_fg = palette().text().color();
1519 arrow_fg.setAlphaF(0.3);
1520 painter.setPen(arrow_fg);
1521 painter.setBrush(arrow_fg);
1523 bool have_far = false;
1524 for (int row = 0; row < pl_rows; row++) {
1526 // Try to remain responsive for large captures.
1527 if (timer.elapsed() > update_time_) {
1528 wsApp->processEvents();
1529 if (!cap_file_ || cap_file_->state != FILE_READ_DONE) {
1530 create_overlay_ = true;
1537 frame_data *fdata = packet_list_model_->getRowFdata(row);
1538 bool marked = false;
1539 if (fdata->flags.marked || fdata->flags.ref_time || fdata->flags.ignored) {
1544 int new_line = (row) * o_height / pl_rows;
1546 QPointF points[3] = {
1547 QPointF(o_width, new_line),
1548 QPointF(0, new_line - (o_width * 0.7)),
1549 QPointF(0, new_line + (o_width * 0.7))
1551 painter.drawPolygon(points, 3);
1557 overlay_sb_->setFarOverlayImage(overlay);
1560 QImage null_overlay;
1561 overlay_sb_->setFarOverlayImage(null_overlay);
1565 void PacketList::rowsInserted(const QModelIndex &parent, int start, int end)
1567 QTreeView::rowsInserted(parent, start, end);
1568 rows_inserted_ = true;
1577 * indent-tabs-mode: nil
1580 * ex: set shiftwidth=4 tabstop=8 expandtab:
1581 * :indentSize=4:tabSize=8:noTabs=true: